From 551a1173b88c5b35b3d6125b678c7e9dd17ee31e Mon Sep 17 00:00:00 2001 From: Guillermo Date: Mon, 25 May 2026 23:50:46 +0200 Subject: [PATCH] fix: bind attach database path --- sqlite_utils/db.py | 8 ++------ tests/test_attach.py | 17 +++++++++++++++++ tests/test_cli.py | 24 ++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py index ed3fc7af2..fed82d582 100644 --- a/sqlite_utils/db.py +++ b/sqlite_utils/db.py @@ -534,12 +534,8 @@ def attach(self, alias: str, filepath: Union[str, pathlib.Path]) -> None: :param alias: Alias name to use :param filepath: Path to SQLite database file on disk """ - attach_sql = """ - ATTACH DATABASE '{}' AS {}; - """.format( - str(pathlib.Path(filepath).resolve()), quote_identifier(alias) - ).strip() - self.execute(attach_sql) + attach_sql = "ATTACH DATABASE ? AS {};".format(quote_identifier(alias)) + self.execute(attach_sql, [str(pathlib.Path(filepath).resolve())]) def query( self, sql: str, params: Optional[Union[Sequence, Dict[str, Any]]] = None diff --git a/tests/test_attach.py b/tests/test_attach.py index b594b3b71..5e7f84bfc 100644 --- a/tests/test_attach.py +++ b/tests/test_attach.py @@ -14,3 +14,20 @@ def test_attach(tmpdir): assert db.execute( "select * from foo union all select * from bar.bar" ).fetchall() == [(1, "foo"), (1, "bar")] + + +def test_attach_filepath_with_apostrophe(tmpdir): + foo_path = str(tmpdir / "foo.db") + bar_dir = tmpdir / "has'apostrophe" + bar_dir.mkdir() + bar_path = str(bar_dir / "bar.db") + db = Database(foo_path) + with db.conn: + db["foo"].insert({"id": 1, "text": "foo"}) + db2 = Database(bar_path) + with db2.conn: + db2["bar"].insert({"id": 1, "text": "bar"}) + db.attach("bar", bar_path) + assert db.execute( + "select * from foo union all select * from bar.bar" + ).fetchall() == [(1, "foo"), (1, "bar")] diff --git a/tests/test_cli.py b/tests/test_cli.py index 40b368540..ea2b70adf 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2239,6 +2239,30 @@ def test_attach(tmpdir): ] +def test_attach_filepath_with_apostrophe(tmpdir): + foo_path = str(tmpdir / "foo.db") + bar_dir = tmpdir / "has'apostrophe" + bar_dir.mkdir() + bar_path = str(bar_dir / "bar.db") + db = Database(foo_path) + with db.conn: + db["foo"].insert({"id": 1, "text": "foo"}) + db2 = Database(bar_path) + with db2.conn: + db2["bar"].insert({"id": 1, "text": "bar"}) + db.attach("bar", bar_path) + sql = "select * from foo union all select * from bar.bar" + result = CliRunner().invoke( + cli.cli, + [foo_path, "--attach", "bar", bar_path, sql], + catch_exceptions=False, + ) + assert json.loads(result.output) == [ + {"id": 1, "text": "foo"}, + {"id": 1, "text": "bar"}, + ] + + def test_csv_insert_bom(tmpdir): db_path = str(tmpdir / "test.db") bom_csv_path = str(tmpdir / "bom.csv")