Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
## Upcoming (TBC)
## Upcoming (TBD)

### Features

* Add support for opening 'file:' URIs with parameters. [(#234)](https://github.com/dbcli/litecli/pull/234)

### Bug Fixes

* Avoid Click 8.1.* to prevent messing up the pager when the PAGER env var has a string with spaces.


## 1.16.0 - 2025-08-16

### Features
Expand Down
20 changes: 14 additions & 6 deletions litecli/sqlexecute.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import sqlparse
import os.path
from urllib.parse import urlparse

from .packages import special

Expand Down Expand Up @@ -68,20 +69,27 @@ def connect(self, database=None):
db = database or self.dbname
_logger.debug("Connection DB Params: \n\tdatabase: %r", db)

db_name = os.path.expanduser(db)
db_dir_name = os.path.dirname(os.path.abspath(db_name))
if not os.path.exists(db_dir_name):
raise Exception("Path does not exist: {}".format(db_dir_name))
location = urlparse(db)
if location.scheme and location.scheme == "file":
uri = True
db_name = db
db_filename = location.path
else:
uri = False
db_filename = db_name = os.path.expanduser(db)
db_dir_name = os.path.dirname(os.path.abspath(db_filename))
if not os.path.exists(db_dir_name):
raise Exception("Path does not exist: {}".format(db_dir_name))

conn = sqlite3.connect(database=db_name, isolation_level=None)
conn = sqlite3.connect(database=db_name, isolation_level=None, uri=uri)
conn.text_factory = lambda x: x.decode("utf-8", "backslashreplace")
if self.conn:
self.conn.close()

self.conn = conn
# Update them after the connection is made to ensure that it was a
# successful connection.
self.dbname = db
self.dbname = db_filename

def run(self, statement):
"""Execute the sql in the database and return the results. The results
Expand Down
30 changes: 29 additions & 1 deletion tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
from unittest.mock import patch

import click
import pytest
from click.testing import CliRunner

from litecli.main import cli, LiteCli
from litecli.packages.special.main import COMMANDS as SPECIAL_COMMANDS
from utils import dbtest, run
from utils import dbtest, run, create_db, db_connection

test_dir = os.path.abspath(os.path.dirname(__file__))
project_dir = os.path.dirname(test_dir)
Expand Down Expand Up @@ -330,3 +331,30 @@ def test_get_prompt(mock_datetime):
# 12. Windows path
lc.connect("C:\\Users\\litecli\\litecli_test.db")
assert lc.get_prompt(r"\d") == "C:\\Users\\litecli\\litecli_test.db"


@pytest.mark.parametrize(
"uri, expected_dbname",
[
("file:{tmp_path}/test.db", "{tmp_path}/test.db"),
("file:{tmp_path}/test.db?mode=ro", "{tmp_path}/test.db"),
("file:{tmp_path}/test.db?mode=ro&cache=shared", "{tmp_path}/test.db"),
],
)
def test_file_uri(tmp_path, uri, expected_dbname):
"""
Test that `file:` URIs are correctly handled
ref:
https://docs.python.org/3/library/sqlite3.html#sqlite3-uri-tricks
https://www.sqlite.org/c3ref/open.html#urifilenameexamples
"""
# - ensure db exists
db_path = tmp_path / "test.db"
create_db(db_path)
db_connection(db_path)
uri = uri.format(tmp_path=tmp_path)

lc = LiteCli()
lc.connect(uri)

assert lc.get_prompt(r"\d") == expected_dbname.format(tmp_path=tmp_path)