Skip to content

fix(reader): honor absolute paths in duckdb:// URIs and fail fast on missing files#347

Merged
georgestagg merged 3 commits intoposit-dev:mainfrom
shntnu:fix-345-duckdb-abs-path
Apr 22, 2026
Merged

fix(reader): honor absolute paths in duckdb:// URIs and fail fast on missing files#347
georgestagg merged 3 commits intoposit-dev:mainfrom
shntnu:fix-345-duckdb-abs-path

Conversation

@shntnu
Copy link
Copy Markdown
Contributor

@shntnu shntnu commented Apr 21, 2026

Summary

Closes #345.

  • duckdb:///abs/path now opens /abs/path (SQLAlchemy convention) instead of stripping the leading slash and silently creating a relative phantom DB.
  • Reject duckdb:////... (4+ slashes) with a clear ambiguity message.
  • DuckDBReader::from_connection_string errors up-front when the target file does not exist, surfacing a helpful message instead of a confusing downstream Table not found error.
  • Apply the same // vs /// parsing rules to sqlite:// URIs for consistency.

Before the fix, duckdb:///tmp/demo.duckdb would be stripped to the relative path tmp/demo.duckdb; DuckDB would then silently create an empty DB at $CWD/tmp/demo.duckdb and queries would fail with a misleading catalog error.

Test plan

  • cargo test -p ggsql --lib --no-default-features --features "duckdb,sqlite,vegalite,ipc,parquet,builtin-data" passes (1305 tests)
  • New regression tests in src/reader/connection.rs cover: absolute path preserved, 4-slash rejected, sqlite mirror
  • New regression tests in src/reader/duckdb.rs cover: missing file errors and does not create a phantom DB; existing absolute-path DB opens correctly
  • Manual CLI smoke test: ggsql exec "..." --reader "duckdb:///nonexistent" now errors with a helpful message instead of silently creating the file

Notes

This PR was authored end-to-end with Claude Code (issue filed from the same session, reproducer built against an installed release, fix developed on a fresh branch off main). Flagging in case you want to tag or track AI-assisted contributions separately; happy to adjust the workflow or commit trailers if you have a preferred convention.

🤖 Generated with Claude Code

…sing files

Closes posit-dev#345.

- `duckdb:///abs/path` now opens `/abs/path` (SQLAlchemy convention)
  instead of stripping the leading slash and silently creating a
  relative phantom DB.
- Reject `duckdb:////...` (4+ slashes) with a clear ambiguity message.
- `DuckDBReader::from_connection_string` errors up-front when the target
  file does not exist, surfacing a helpful message instead of a
  confusing downstream "Table not found" error.
- Apply the same `//` vs `///` parsing rules to `sqlite://` URIs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thomasp85 thomasp85 requested a review from georgestagg April 22, 2026 07:38
Copy link
Copy Markdown
Collaborator

@georgestagg georgestagg left a comment

Choose a reason for hiding this comment

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

Thanks for this PR!

I have some feedback, details in comments below.

After my requested changes, the PR will be quite small. If I am permitted, I am going to commit my changes direct to this branch before merging, otherwise I'll follow up in a new PR.

Comment thread src/reader/duckdb.rs Outdated
Comment on lines +115 to +119
ConnectionInfo::DuckDBFile(path) => {
// Fail fast if the path does not exist. DuckDB's default
// `open` creates the file if missing, which silently masks
// typos and produces confusing "Table not found" errors
// downstream. See issue #345.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is actually intended, the same behaviour as invoking duckdb via the CLI:

$ rm nonexistent.db 
$ duckdb nonexistent.db
DuckDB v1.4.3 (Andium) d1dc88f950
Enter ".help" for usage hints.
D CREATE TABLE table_name AS FROM VALUES (1) as d;

$ ls nonexistent.db 
nonexistent.db

Comment thread src/reader/connection.rs Outdated
}

/// Parse a DuckDB/SQLite URI body into a filesystem path, following
/// SQLAlchemy conventions.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't believe this is a SQLAlchemy specific convention, simply a leading slash after the uri:// portion, indicating an absolute path.

Additionally, IMO extra slashes at the start (or in fact anywhere) should not be an error, they are accepted in paths. Consider e.g.

$ touch /tmp/test.txt
$ ls /tmp/test.txt
/tmp/test.txt
$ ls ///tmp/////test.txt
///tmp/////test.txt

@georgestagg georgestagg merged commit 45ef875 into posit-dev:main Apr 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reader URI: duckdb:///abs/path drops leading slash and falls through to a relative path

2 participants