Skip to content

Commit f03f153

Browse files
authored
Add dagster-adbc integration (#272)
Co-authored-by: Emil Sadek <esadek@users.noreply.github.com>
1 parent d1669cb commit f03f153

9 files changed

Lines changed: 1619 additions & 0 deletions

File tree

libraries/dagster-adbc/Makefile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
install:
2+
uv sync
3+
4+
build:
5+
uv build
6+
7+
test:
8+
uv run pytest
9+
10+
ruff:
11+
uv run ruff check --fix
12+
uv run ruff format
13+
14+
check:
15+
uv run pyright

libraries/dagster-adbc/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# dagster-adbc
2+
3+
A Dagster module that provides an integration with [ADBC](https://arrow.apache.org/adbc/current/index.html).
4+
5+
## Installation
6+
7+
The `dagster_adbc` module is available as a PyPI package - install with your preferred python environment manager (We recommend [uv](https://github.com/astral-sh/uv)).
8+
9+
```sh
10+
uv venv
11+
source .venv/bin/activate
12+
uv pip install dagster-adbc
13+
```
14+
15+
Additionally, the ADBC driver for your database must be installed.
16+
We recommend [dbc](https://github.com/columnar-tech/dbc) for installing drivers.
17+
18+
```sh
19+
uv pip install dbc
20+
dbc install flightsql
21+
```
22+
23+
## Example Usage
24+
25+
```python
26+
from dagster import Definitions, EnvVar, asset
27+
from dagster_adbc import ADBCResource
28+
29+
30+
@asset
31+
def my_table(dremio: ADBCResource) -> None:
32+
with dremio.get_connection() as connection, connection.cursor() as cursor:
33+
cursor.execute("SELECT * FROM my_table")
34+
table = cursor.fetch_arrow_table()
35+
36+
37+
defs = Definitions(
38+
assets=[my_table],
39+
resources={
40+
"dremio": ADBCResource(
41+
driver="flightsql",
42+
uri="grpc+tcp://localhost:32010",
43+
db_kwargs={"username": "admin", "password": EnvVar("DREMIO_PASSWORD")},
44+
)
45+
},
46+
)
47+
```
48+
49+
## Development
50+
51+
The `Makefile` provides the tools required to test and lint your local installation.
52+
53+
```sh
54+
make test
55+
make ruff
56+
make check
57+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from dagster._core.libraries import DagsterLibraryRegistry
2+
3+
from dagster_adbc.resource import ADBCResource
4+
5+
__all__ = ["ADBCResource"]
6+
__version__ = "0.0.1"
7+
8+
DagsterLibraryRegistry.register("dagster-adbc", __version__, is_dagster_package=False)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from collections.abc import Mapping
2+
from contextlib import contextmanager
3+
from typing import Any, Generator
4+
5+
from adbc_driver_manager import dbapi
6+
from dagster import ConfigurableResource
7+
from pydantic import Field
8+
9+
10+
class ADBCResource(ConfigurableResource):
11+
"""Resource for interacting with a database via ADBC.
12+
Wraps an underlying adbc_driver_manager.dbapi connection.
13+
14+
Examples:
15+
.. code-block:: python
16+
from dagster import Definitions, EnvVar, asset
17+
from dagster_adbc import ADBCResource
18+
19+
20+
@asset
21+
def my_table(dremio: ADBCResource) -> None:
22+
with dremio.get_connection() as connection, connection.cursor() as cursor:
23+
cursor.execute("SELECT * FROM my_table")
24+
table = cursor.fetch_arrow_table()
25+
26+
27+
defs = Definitions(
28+
assets=[my_table],
29+
resources={
30+
"dremio": ADBCResource(
31+
driver="flightsql",
32+
uri="grpc+tcp://localhost:32010",
33+
db_kwargs={"username": "admin", "password": EnvVar("DREMIO_PASSWORD")},
34+
)
35+
},
36+
)
37+
"""
38+
39+
driver: str | None = Field(default=None, description="The driver to use.")
40+
uri: str | None = Field(
41+
default=None, description='The "uri" parameter to the database (if applicable).'
42+
)
43+
profile: str | None = Field(default=None, description="A connection profile to load.")
44+
entrypoint: str | None = Field(
45+
default=None,
46+
description="The driver-specific entrypoint, if different than the default.",
47+
)
48+
db_kwargs: Mapping[str, str] | None = Field(
49+
default=None,
50+
description="Key-value parameters to pass to the driver to initialize the database.",
51+
)
52+
conn_kwargs: Mapping[str, str] | None = Field(
53+
default=None,
54+
description="Key-value parameters to pass to the driver to initialize the connection.",
55+
)
56+
autocommit: bool = Field(default=False, description="Whether to enable autocommit.")
57+
58+
@classmethod
59+
def _is_dagster_mainatained(cls) -> bool:
60+
return False
61+
62+
@contextmanager
63+
def get_connection(self) -> Generator[dbapi.Connection, Any, None]:
64+
connection = dbapi.connect(
65+
driver=self.driver,
66+
uri=self.uri,
67+
profile=self.profile,
68+
entrypoint=self.entrypoint,
69+
db_kwargs=self.db_kwargs,
70+
conn_kwargs=self.conn_kwargs,
71+
autocommit=self.autocommit,
72+
)
73+
try:
74+
yield connection
75+
finally:
76+
connection.close()

libraries/dagster-adbc/dagster_adbc_tests/__init__.py

Whitespace-only changes.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import subprocess
2+
from typing import Any, Generator
3+
4+
import pyarrow as pa
5+
import pytest
6+
7+
from dagster_adbc import ADBCResource
8+
9+
10+
@pytest.fixture(autouse=True)
11+
def sqlite_adbc_driver() -> Generator[None, Any, None]:
12+
subprocess.run(["dbc", "install", "sqlite"])
13+
yield
14+
subprocess.run(["dbc", "uninstall", "sqlite"])
15+
16+
17+
def test_connection() -> None:
18+
resource = ADBCResource(driver="sqlite", uri=":memory:")
19+
with resource.get_connection() as connection, connection.cursor() as cursor:
20+
cursor.execute("SELECT 1 AS value")
21+
table = cursor.fetch_arrow_table()
22+
23+
assert isinstance(table, pa.Table)
24+
assert table.num_rows == 1
25+
assert table.column("value")[0].as_py() == 1
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from importlib.metadata import version
2+
3+
import dagster_adbc
4+
5+
6+
def test_version() -> None:
7+
assert version("dagster-adbc") == dagster_adbc.__version__
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[project]
2+
name = "dagster-adbc"
3+
description = "Dagster integration with ADBC"
4+
readme = "README.md"
5+
requires-python = ">=3.10"
6+
dependencies = [
7+
"adbc-driver-manager>=1.11.0",
8+
"dagster>=1.13.0",
9+
"pyarrow>=23.0.1",
10+
]
11+
dynamic = ["version"]
12+
13+
[dependency-groups]
14+
dev = [
15+
"dbc>=0.2.0",
16+
"pyright>=1.1.408",
17+
"pytest>=9.0.3",
18+
"ruff>=0.15.9",
19+
]
20+
21+
[build-system]
22+
requires = ["setuptools>=42"]
23+
build-backend = "setuptools.build_meta"
24+
25+
[tool.setuptools]
26+
packages = ["dagster_adbc"]
27+
28+
[tool.setuptools.dynamic]
29+
version = {attr = "dagster_adbc.__version__"}
30+
31+
[tool.ruff]
32+
line-length = 100
33+
34+
[tool.ruff.lint]
35+
select = ["ANN", "E", "F", "I"]

0 commit comments

Comments
 (0)