blob: d266dbd12c01edb78cffe8d1372dead38db07ab6 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import pathlib
import pandas
import pyarrow
import pyarrow.dataset
import pytest
from pandas.testing import assert_frame_equal
from adbc_driver_manager import dbapi
def test_type_objects() -> None:
assert dbapi.NUMBER == pyarrow.int64()
assert pyarrow.int64() == dbapi.NUMBER
assert dbapi.STRING == pyarrow.string()
assert pyarrow.string() == dbapi.STRING
assert dbapi.STRING != dbapi.NUMBER
assert dbapi.NUMBER != dbapi.DATETIME
assert dbapi.NUMBER == dbapi.ROWID
@pytest.mark.sqlite
def test_attrs(sqlite) -> None:
assert sqlite.Warning == dbapi.Warning
assert sqlite.Error == dbapi.Error
assert sqlite.InterfaceError == dbapi.InterfaceError
assert sqlite.DatabaseError == dbapi.DatabaseError
assert sqlite.DataError == dbapi.DataError
assert sqlite.OperationalError == dbapi.OperationalError
assert sqlite.IntegrityError == dbapi.IntegrityError
assert sqlite.InternalError == dbapi.InternalError
assert sqlite.ProgrammingError == dbapi.ProgrammingError
assert sqlite.NotSupportedError == dbapi.NotSupportedError
with sqlite.cursor() as cur:
assert cur.arraysize == 1
assert cur.connection is sqlite
assert cur.description is None
assert cur.rowcount == -1
@pytest.mark.sqlite
def test_info(sqlite) -> None:
info = sqlite.adbc_get_info()
assert set(info.keys()) == {
"driver_adbc_version",
"driver_arrow_version",
"driver_name",
"driver_version",
"vendor_name",
"vendor_version",
}
assert info["driver_name"] == "ADBC SQLite Driver"
assert info["vendor_name"] == "SQLite"
@pytest.mark.sqlite
def test_get_underlying(sqlite) -> None:
assert sqlite.adbc_database
assert sqlite.adbc_connection
with sqlite.cursor() as cur:
assert cur.adbc_statement
@pytest.mark.sqlite
def test_clone(sqlite) -> None:
with sqlite.adbc_clone() as sqlite2:
with sqlite2.cursor() as cur:
cur.execute("CREATE TABLE temporary (ints)")
cur.execute("INSERT INTO temporary VALUES (1)")
sqlite2.commit()
with sqlite.cursor() as cur:
cur.execute("SELECT * FROM temporary")
assert cur.fetchone() == (1,)
@pytest.mark.sqlite
def test_get_objects(sqlite) -> None:
with sqlite.cursor() as cur:
cur.execute("CREATE TABLE temporary (ints)")
cur.execute("INSERT INTO temporary VALUES (1)")
metadata = (
sqlite.adbc_get_objects(table_name_filter="temporary").read_all().to_pylist()
)
assert len(metadata) == 1
assert metadata[0]["catalog_name"] == "main"
schemas = metadata[0]["catalog_db_schemas"]
assert len(schemas) == 1
assert schemas[0]["db_schema_name"] == ""
tables = schemas[0]["db_schema_tables"]
assert len(tables) == 1
assert tables[0]["table_name"] == "temporary"
assert tables[0]["table_type"] == "table"
assert tables[0]["table_columns"][0]["column_name"] == "ints"
assert tables[0]["table_columns"][0]["ordinal_position"] == 1
assert tables[0]["table_constraints"] == []
@pytest.mark.sqlite
def test_get_table_schema(sqlite) -> None:
with sqlite.cursor() as cur:
cur.execute("CREATE TABLE temporary (ints)")
cur.execute("INSERT INTO temporary VALUES (1)")
assert sqlite.adbc_get_table_schema("temporary") == pyarrow.schema(
[("ints", pyarrow.int64())]
)
@pytest.mark.sqlite
def test_get_table_types(sqlite) -> None:
assert sqlite.adbc_get_table_types() == ["table", "view"]
@pytest.mark.sqlite
def test_get_statistics_not_supported(sqlite) -> None:
"""SQLite does not support GetStatistics."""
with pytest.raises(dbapi.NotSupportedError):
sqlite.adbc_get_statistics()
@pytest.mark.sqlite
def test_get_statistic_names_not_supported(sqlite) -> None:
"""SQLite does not support GetStatisticNames."""
with pytest.raises(dbapi.NotSupportedError):
sqlite.adbc_get_statistic_names()
class ArrayWrapper:
def __init__(self, array) -> None:
self.array = array
def __arrow_c_array__(self, requested_schema=None):
return self.array.__arrow_c_array__(requested_schema=requested_schema)
class StreamWrapper:
def __init__(self, stream) -> None:
self.stream = stream
def __arrow_c_stream__(self, requested_schema=None):
return self.stream.__arrow_c_stream__(requested_schema=requested_schema)
@pytest.mark.parametrize(
"data",
[
lambda: pyarrow.record_batch([[1, 2], ["foo", ""]], names=["ints", "strs"]),
lambda: pyarrow.table([[1, 2], ["foo", ""]], names=["ints", "strs"]),
lambda: pyarrow.table(
[[1, 2], ["foo", ""]], names=["ints", "strs"]
).to_reader(),
lambda: ArrayWrapper(
pyarrow.record_batch([[1, 2], ["foo", ""]], names=["ints", "strs"])
),
lambda: StreamWrapper(
pyarrow.table([[1, 2], ["foo", ""]], names=["ints", "strs"])
),
lambda: pyarrow.table(
[[1, 2], ["foo", ""]], names=["ints", "strs"]
).__arrow_c_stream__(),
],
)
@pytest.mark.sqlite
def test_ingest(data, sqlite) -> None:
with sqlite.cursor() as cur:
cur.adbc_ingest("bulk_ingest", data())
with pytest.raises(dbapi.Error):
cur.adbc_ingest("bulk_ingest", data())
cur.adbc_ingest("bulk_ingest", data(), mode="append")
with pytest.raises(dbapi.Error):
cur.adbc_ingest("nonexistent", data(), mode="append")
with pytest.raises(ValueError):
cur.adbc_ingest("bulk_ingest", data(), mode="invalid")
with sqlite.cursor() as cur:
cur.execute("SELECT * FROM bulk_ingest")
assert cur.fetchone() == (1, "foo")
assert cur.fetchone() == (2, "")
assert cur.fetchone() == (1, "foo")
assert cur.fetchone() == (2, "")
@pytest.mark.sqlite
def test_partitions(sqlite) -> None:
with pytest.raises(dbapi.NotSupportedError):
with sqlite.cursor() as cur:
cur.adbc_execute_partitions("SELECT 1")
@pytest.mark.sqlite
def test_query_fetch_py(sqlite) -> None:
with sqlite.cursor() as cur:
cur.execute("SELECT 1, 'foo' AS foo, 2.0")
assert cur.description == [
("1", dbapi.NUMBER, None, None, None, None, None),
("foo", dbapi.STRING, None, None, None, None, None),
("2.0", dbapi.NUMBER, None, None, None, None, None),
]
assert cur.rownumber == 0
assert cur.fetchone() == (1, "foo", 2.0)
assert cur.rownumber == 1
assert cur.fetchone() is None
cur.execute("SELECT 1, 'foo', 2.0")
assert cur.fetchmany() == [(1, "foo", 2.0)]
assert cur.fetchmany() == []
cur.execute("SELECT 1, 'foo', 2.0")
assert cur.fetchall() == [(1, "foo", 2.0)]
assert cur.fetchall() == []
cur.execute("SELECT 1, 'foo', 2.0")
assert list(cur) == [(1, "foo", 2.0)]
@pytest.mark.sqlite
def test_query_fetch_arrow(sqlite) -> None:
with sqlite.cursor() as cur:
with pytest.raises(sqlite.ProgrammingError):
cur.fetch_arrow()
cur.execute("SELECT 1, 'foo' AS foo, 2.0")
capsule = cur.fetch_arrow().__arrow_c_stream__()
reader = pyarrow.RecordBatchReader._import_from_c_capsule(capsule)
assert reader.read_all() == pyarrow.table(
{
"1": [1],
"foo": ["foo"],
"2.0": [2.0],
}
)
with pytest.raises(sqlite.ProgrammingError):
cur.fetch_arrow()
@pytest.mark.sqlite
def test_query_fetch_arrow_3543(sqlite) -> None:
# Regression test for https://github.com/apache/arrow-adbc/issues/3543
with sqlite.cursor() as cur:
cur.execute("SELECT 1, 'foo' AS foo, 2.0")
# This should not consume the result
assert cur.description == [
("1", dbapi.NUMBER, None, None, None, None, None),
("foo", dbapi.STRING, None, None, None, None, None),
("2.0", dbapi.NUMBER, None, None, None, None, None),
]
capsule = cur.fetch_arrow().__arrow_c_stream__()
reader = pyarrow.RecordBatchReader._import_from_c_capsule(capsule)
assert reader.read_all() == pyarrow.table(
{
"1": [1],
"foo": ["foo"],
"2.0": [2.0],
}
)
@pytest.mark.sqlite
def test_query_fetch_arrow_table(sqlite) -> None:
with sqlite.cursor() as cur:
cur.execute("SELECT 1, 'foo' AS foo, 2.0")
assert cur.fetch_arrow_table() == pyarrow.table(
{
"1": [1],
"foo": ["foo"],
"2.0": [2.0],
}
)
@pytest.mark.sqlite
def test_query_fetch_df(sqlite) -> None:
with sqlite.cursor() as cur:
cur.execute("SELECT 1, 'foo' AS foo, 2.0")
assert_frame_equal(
cur.fetch_df(),
pandas.DataFrame(
{
"1": [1],
"foo": ["foo"],
"2.0": [2.0],
}
),
)
@pytest.mark.sqlite
@pytest.mark.parametrize(
"parameters",
[
(1.0, 2),
pyarrow.record_batch([[1.0], [2]], names=["float", "int"]),
pyarrow.table([[1.0], [2]], names=["float", "int"]),
ArrayWrapper(pyarrow.record_batch([[1.0], [2]], names=["float", "int"])),
StreamWrapper(pyarrow.table([[1.0], [2]], names=["float", "int"])),
],
)
def test_execute_parameters(sqlite, parameters) -> None:
with sqlite.cursor() as cur:
cur.execute("SELECT ? + 1, ?", parameters)
assert cur.fetchall() == [(2.0, 2)]
@pytest.mark.sqlite
def test_execute_parameters_name(sqlite) -> None:
with sqlite.cursor() as cur:
cur.execute("SELECT @a + 1, @b", {"@b": 2, "@a": 1})
assert cur.fetchall() == [(2, 2)]
# Ensure the state of the cursor isn't affected
cur.execute("SELECT ?2 + 1, ?1", [2, 1])
assert cur.fetchall() == [(2, 2)]
cur.execute("SELECT @a + 1, @b + @b", {"@b": 2, "@a": 1})
assert cur.fetchall() == [(2, 4)]
data = pyarrow.record_batch([[1.0], [2]], names=["float", "int"])
cur.adbc_ingest("ingest_tester", data)
cur.execute("SELECT * FROM ingest_tester")
assert cur.fetchall() == [(1.0, 2)]
@pytest.mark.sqlite
def test_executemany_parameters_name(sqlite) -> None:
with sqlite.cursor() as cur:
cur.execute("CREATE TABLE executemany_params (a, b)")
cur.executemany(
"INSERT INTO executemany_params VALUES (@a, @b)",
[{"@b": 2, "@a": 1}, {"@b": 3, "@a": 2}],
)
cur.executemany(
"INSERT INTO executemany_params VALUES (?, ?)", [(3, 4), (4, 5)]
)
cur.execute("SELECT * FROM executemany_params ORDER BY a ASC")
assert cur.fetchall() == [(1, 2), (2, 3), (3, 4), (4, 5)]
@pytest.mark.sqlite
@pytest.mark.parametrize(
"parameters",
[
[(1, "a"), (3, None)],
pyarrow.record_batch([[1, 3], ["a", None]], names=["float", "str"]),
pyarrow.table([[1, 3], ["a", None]], names=["float", "str"]),
pyarrow.table([[1, 3], ["a", None]], names=["float", "str"]).to_batches()[0],
ArrayWrapper(
pyarrow.record_batch([[1, 3], ["a", None]], names=["float", "str"])
),
StreamWrapper(pyarrow.table([[1, 3], ["a", None]], names=["float", "str"])),
((x, y) for x, y in ((1, "a"), (3, None))),
],
)
def test_executemany_parameters(sqlite, parameters) -> None:
with sqlite.cursor() as cur:
cur.execute("DROP TABLE IF EXISTS executemany")
cur.execute("CREATE TABLE executemany (int, str)")
cur.executemany("INSERT INTO executemany VALUES (? * 2, ?)", parameters)
cur.execute("SELECT * FROM executemany ORDER BY int ASC")
assert cur.fetchall() == [(2, "a"), (6, None)]
@pytest.mark.sqlite
@pytest.mark.parametrize(
"parameters",
[
[],
pyarrow.record_batch([[]], schema=pyarrow.schema([("v", pyarrow.int64())])),
pyarrow.table([[]], schema=pyarrow.schema([("v", pyarrow.int64())])),
],
)
def test_executemany_empty(sqlite, parameters) -> None:
# Regression test for https://github.com/apache/arrow-adbc/issues/3319
with sqlite.cursor() as cur:
# With an empty sequence, it should be the same as not executing the
# query at all.
cur.execute("DROP TABLE IF EXISTS executemany")
cur.execute("CREATE TABLE executemany (v)")
cur.executemany("INSERT INTO executemany VALUES (?)", parameters)
cur.execute("SELECT * FROM executemany")
assert cur.fetchall() == []
@pytest.mark.sqlite
def test_executemany_none(sqlite) -> None:
# Regression test for https://github.com/apache/arrow-adbc/issues/3319
with sqlite.cursor() as cur:
# With None, it should be the same as executing the query once.
cur.execute("DROP TABLE IF EXISTS executemany")
cur.execute("CREATE TABLE executemany (v)")
with pytest.raises(sqlite.Error):
cur.executemany("INSERT INTO executemany VALUES (?)", None)
@pytest.mark.sqlite
def test_query_substrait(sqlite) -> None:
with sqlite.cursor() as cur:
with pytest.raises(dbapi.NotSupportedError):
cur.execute(b"Substrait plan")
@pytest.mark.sqlite
def test_executemany(sqlite) -> None:
with sqlite.cursor() as cur:
cur.execute("CREATE TABLE foo (a, b)")
cur.executemany(
"INSERT INTO foo VALUES (?, ?)",
[
(1, 2),
(3, 4),
(5, 6),
],
)
cur.execute("SELECT COUNT(*) FROM foo")
assert cur.fetchone() == (3,)
cur.execute("SELECT * FROM foo ORDER BY a ASC")
assert cur.rownumber == 0
assert next(cur) == (1, 2)
assert cur.rownumber == 1
assert next(cur) == (3, 4)
assert cur.rownumber == 2
assert next(cur) == (5, 6)
@pytest.mark.sqlite
def test_fetch_record_batch(sqlite) -> None:
dataset = [
[1, 2],
[3, 4],
[5, 6],
[7, 8],
[9, 10],
]
with sqlite.cursor() as cur:
cur.execute("CREATE TABLE foo (a, b)")
cur.executemany(
"INSERT INTO foo VALUES (?, ?)",
dataset,
)
cur.execute("SELECT * FROM foo")
rbr = cur.fetch_record_batch()
assert rbr.read_pandas().values.tolist() == dataset
@pytest.mark.sqlite
def test_fetch_empty(sqlite) -> None:
with sqlite.cursor() as cur:
cur.execute("CREATE TABLE foo (bar)")
cur.execute("SELECT * FROM foo")
assert cur.fetchall() == []
@pytest.mark.sqlite
def test_reader(sqlite, tmp_path) -> None:
# Regression test for https://github.com/apache/arrow-adbc/issues/1523
with sqlite.cursor() as cur:
cur.execute("SELECT 1")
reader = cur.fetch_record_batch()
pyarrow.dataset.write_dataset(reader, tmp_path, format="parquet")
@pytest.mark.sqlite
def test_prepare(sqlite) -> None:
with sqlite.cursor() as cur:
schema = cur.adbc_prepare("SELECT 1")
assert schema == pyarrow.schema([])
schema = cur.adbc_prepare("SELECT 1 + ?")
assert schema == pyarrow.schema([("0", "null")])
cur.execute("SELECT 1 + ?", (1,))
assert cur.fetchone() == (2,)
@pytest.mark.sqlite
def test_close_warning(sqlite) -> None:
with pytest.warns(
ResourceWarning,
match=r"A adbc_driver_manager.dbapi.Cursor was not explicitly close\(\)d",
):
cur = sqlite.cursor()
del cur
with pytest.warns(
ResourceWarning,
match=r"A adbc_driver_manager.dbapi.Connection was not explicitly close\(\)d",
):
conn = dbapi.connect(driver="adbc_driver_sqlite")
del conn
def _execute_schema(cursor) -> None:
try:
cursor.adbc_execute_schema("select 1")
except dbapi.NotSupportedError:
pass
@pytest.mark.sqlite
@pytest.mark.parametrize(
"op",
[
pytest.param(lambda cursor: cursor.execute("SELECT 1"), id="execute"),
pytest.param(
lambda cursor: cursor.executemany("SELECT ?", [[1]]), id="executemany"
),
pytest.param(
lambda cursor: cursor.adbc_ingest(
"test_release",
pyarrow.table([[1]], names=["ints"]),
mode="create_append",
),
id="ingest",
),
pytest.param(_execute_schema, id="execute_schema"),
pytest.param(lambda cursor: cursor.adbc_prepare("select 1"), id="prepare"),
pytest.param(
lambda cursor: cursor.executescript("select 1"), id="executescript"
),
],
)
def test_release(sqlite, op) -> None:
# Regression test. Ensure that subsequent operations free results of
# earlier operations.
with sqlite.cursor() as cur:
cur.execute("select 1")
# Do _not_ fetch the data so it is never imported.
assert cur._results._handle.is_valid
handle = cur._results._handle
op(cur)
if handle:
# The original handle (if it exists) should have been released
assert not handle.is_valid
def test_driver_path() -> None:
with pytest.raises(
dbapi.ProgrammingError,
match="(dlopen|LoadLibraryExW).*failed:",
):
with dbapi.connect(driver=pathlib.Path("/tmp/thisdriverdoesnotexist")):
pass
@pytest.mark.sqlite
def test_dbapi_extensions(sqlite) -> None:
with sqlite.execute("SELECT ?", (1,)) as cur:
assert cur.fetchone() == (1,)
assert cur.fetchone() is None
assert cur.execute("SELECT 2").fetchall() == [(2,)]
with sqlite.cursor() as cur:
assert cur.execute("SELECT 1").fetchall() == [(1,)]
assert cur.execute("SELECT 42").fetchall() == [(42,)]
@pytest.mark.sqlite
def test_close_connection_with_open_cursor() -> None:
"""
Regression test for https://github.com/apache/arrow-adbc/issues/3713
Closing a connection should automatically close any open cursors
without raising RuntimeError about open AdbcStatement.
This test intentionally avoids context managers because the bug only
manifests when users manually call close() without first closing cursors.
"""
conn = dbapi.connect(driver="adbc_driver_sqlite")
cursor = conn.cursor()
cursor.execute("SELECT 1")
# Fetch the result to ensure the cursor is "active"
cursor.fetch_arrow_table()
# This should NOT raise:
# RuntimeError: Cannot close AdbcConnection with open AdbcStatement
conn.close()
# Verify cursor is also closed
assert cursor._closed
@pytest.mark.sqlite
def test_close_connection_with_multiple_open_cursors() -> None:
"""
Test that closing a connection closes all open cursors.
This test intentionally avoids context managers because the bug only
manifests when users manually call close() without first closing cursors.
"""
conn = dbapi.connect(driver="adbc_driver_sqlite")
cursor1 = conn.cursor()
cursor2 = conn.cursor()
cursor3 = conn.cursor()
cursor1.execute("SELECT 1")
cursor2.execute("SELECT 2")
# cursor3 is not executed
# This should close all cursors
conn.close()
assert cursor1._closed
assert cursor2._closed
assert cursor3._closed
@pytest.mark.sqlite
def test_close_connection_cursor_already_closed() -> None:
"""
Test that closing a connection works even if some cursors are already closed.
This test intentionally avoids context managers because the bug only
manifests when users manually call close() without first closing cursors.
"""
conn = dbapi.connect(driver="adbc_driver_sqlite")
cursor1 = conn.cursor()
cursor2 = conn.cursor()
cursor1.execute("SELECT 1")
cursor1.close() # Manually close cursor1
cursor2.execute("SELECT 2")
# cursor2 is still open
# This should work without issues
conn.close()
assert cursor1._closed
assert cursor2._closed
@pytest.mark.sqlite
def test_close_connection_via_context_manager_with_open_cursors() -> None:
"""
Test that exiting a connection context manager closes open cursors.
This test uses a context manager for the connection but intentionally
avoids context managers for cursors to verify that Connection.__exit__
properly closes any open cursors.
"""
with dbapi.connect(driver="adbc_driver_sqlite") as conn:
cursor1 = conn.cursor()
cursor2 = conn.cursor()
cursor1.execute("SELECT 1")
cursor2.execute("SELECT 2")
# Cursors are open at this point
assert not cursor1._closed
assert not cursor2._closed
# After exiting the context manager, cursors should be closed
assert cursor1._closed
assert cursor2._closed
@pytest.mark.sqlite
def test_close_connection_suppresses_cursor_close_error() -> None:
"""
Test that closing a connection suppresses exceptions from cursor.close().
When a cursor's close() method raises an exception, the connection should
still close successfully without propagating the error, but should emit
a ResourceWarning.
"""
from unittest.mock import MagicMock
conn = dbapi.connect(driver="adbc_driver_sqlite")
# Create a real cursor so we have something legitimate in _cursors
real_cursor = conn.cursor()
real_cursor.execute("SELECT 1")
# Create a mock cursor that raises on close
mock_cursor = MagicMock()
mock_cursor.close.side_effect = RuntimeError("Simulated cursor close failure")
# Add the mock cursor to the connection's cursor set
conn._cursors.add(mock_cursor)
# This should NOT raise despite the mock cursor raising on close,
# but should emit a ResourceWarning
with pytest.warns(ResourceWarning, match="Failed to close cursor"):
conn.close()
# Verify the connection is closed
assert conn._closed
# Verify the mock cursor's close was called
mock_cursor.close.assert_called_once()
# Verify the real cursor is also closed
assert real_cursor._closed
def test_requires_pyarrow_late_install(monkeypatch) -> None:
"""Test that _requires_pyarrow recovers if PyArrow becomes available
after import."""
# Simulate the state where PyArrow wasn't available at module load time
monkeypatch.setattr(dbapi, "_has_pyarrow", False)
monkeypatch.delattr(dbapi, "_reader", raising=False)
monkeypatch.delattr(dbapi, "pyarrow", raising=False)
# Should not raise because PyArrow is actually importable now
dbapi._requires_pyarrow()
# The flag should be updated so subsequent calls don't re-import
assert dbapi._has_pyarrow is True
# The _reader Cython extension should now be available at module level
assert hasattr(dbapi, "_reader")
assert hasattr(dbapi._reader, "AdbcRecordBatchReader")
# pyarrow must be in module globals for callers that use it directly
assert hasattr(dbapi, "pyarrow")
def test_requires_pyarrow_truly_missing(monkeypatch) -> None:
"""Test that _requires_pyarrow still raises when PyArrow is genuinely missing."""
import builtins
real_import = builtins.__import__
def mock_import(name, *args, **kwargs):
if name.startswith("pyarrow"):
raise ImportError("mocked")
return real_import(name, *args, **kwargs)
monkeypatch.setattr(dbapi, "_has_pyarrow", False)
monkeypatch.setattr(builtins, "__import__", mock_import)
with pytest.raises(dbapi.ProgrammingError, match="requires PyArrow"):
dbapi._requires_pyarrow()
@pytest.mark.sqlite
def test_connect(tmp_path: pathlib.Path, monkeypatch) -> None:
with dbapi.connect(driver="adbc_driver_sqlite") as conn:
with conn.cursor() as cur:
cur.execute("SELECT 1")
assert cur.fetchone() == (1,)
# https://github.com/apache/arrow-adbc/issues/3517: allow positional
# argument
with dbapi.connect("adbc_driver_sqlite") as conn:
with conn.cursor() as cur:
cur.execute("SELECT 1")
assert cur.fetchone() == (1,)
# https://github.com/apache/arrow-adbc/issues/3517: allow URI argument
db = tmp_path / "test.db"
with dbapi.connect("adbc_driver_sqlite", db.as_uri()) as conn:
with conn.cursor() as cur:
cur.execute("CREATE TABLE foo (a)")
cur.execute("INSERT INTO foo VALUES (1)")
conn.commit()
with dbapi.connect(driver="adbc_driver_sqlite", uri=db.as_uri()) as conn:
with conn.cursor() as cur:
cur.execute("SELECT * FROM foo")
assert cur.fetchone() == (1,)
monkeypatch.setenv("ADBC_DRIVER_PATH", str(tmp_path))
with (tmp_path / "foobar.toml").open("w") as f:
f.write(
"""
[Driver]
shared = "adbc_driver_foobar"
"""
)
# Just check that the driver gets detected and loaded (should fail)
with pytest.raises(dbapi.ProgrammingError, match="NOT_FOUND"):
with dbapi.connect("foobar://localhost:5439"):
pass
# https://github.com/apache/arrow-adbc/issues/4077: allow profile argument
# Just check that the profile gets detected and loaded (should fail)
with pytest.raises(dbapi.ProgrammingError, match="NOT_FOUND.*Profile not found"):
with dbapi.connect(profile="nonexistent"):
pass
with pytest.raises(TypeError):
with dbapi.connect():
pass