Skip to content

feat: rewrote dialect as a subclass of sqlalchemy-cloudflare-d1 (0.2.0) - #1

Merged
danielalyoshin merged 14 commits into
mainfrom
feat/0.2.0-shim
Sep 21, 2026
Merged

danielalyoshin merged 14 commits into
mainfrom
feat/0.2.0-shim

Conversation

@danielalyoshin

@danielalyoshin danielalyoshin commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

Summary

Version 0.2.0. D1Dialect is now a subclass of CloudflareD1Dialect from sqlalchemy-cloudflare-d1. The d1:// connection string and the d1 entry point stay the same, so nothing changes for existing Superset connections.

Superset has moved to SQLAlchemy 2.0. Version 0.1.0 pins sqlalchemy<2, so the d1 extra in Superset is blocked today. This removes the block.

Still a draft, but ready for review. The end-to-end check inside Superset has passed on this exact code, and so has a code review. Nothing is on PyPI yet. Merging and tagging v0.2.0 is what publishes it.


What changed

Area Change
Dependencies sqlalchemy>=2.0,<2.1 and sqlalchemy-cloudflare-d1>=0.3.11,<0.5. dbapi-d1 is dropped.
Python >=3.11. The <3.12 cap is gone. Tested on 3.11 to 3.14.
Source New dialect.py and types.py, about 220 lines. The empty compiler.py and type_compiler.py are deleted.
License LICENSE file and license = "Apache-2.0" in pyproject.toml.
Tests 47 unit tests that run offline and 8 integration tests against a real D1 database.
CI ruff, mypy and the unit tests on Python 3.11 to 3.14, a job on the lowest allowed versions, and a weekly run that notices a breaking upstream release.
Release New release.yml. A v* tag builds the package and publishes it to PyPI with trusted publishing.
Docs README.md and CHANGELOG.md.

What the dialect adds on top of upstream

  • Date and boolean reflection. Columns declared as DATETIME, TIMESTAMP, DATE, TIME, BOOLEAN or BOOL are reflected as the types in sqlalchemy_d1.types. They behave like the upstream date and boolean types and keep the declared name. Superset reads that name to decide if a column is a date. Upstream reflects these columns as TEXT.
  • DECIMAL columns are reflected as numeric.
  • autoincrement on reflected columns. It is true only for the column SQLite fills in by itself.
  • Primary key order. A composite key is reflected in the order of the key.
  • get_schema_names, get_view_names and get_view_definition. Upstream has none of these, and Superset needs the first two. has_table is also true for a view.
  • _cf_ filtering for tables and views.

Everything else comes from upstream unchanged. That includes the connection, the cursor, the SQL compiler, and the reflection of foreign keys, indexes and unique constraints.

Tables created through SQLAlchemy are identical to the ones upstream creates. A DateTime column is still declared as TEXT, so it reads back as text. A table reflected from D1 keeps its declared types when it is created again on D1. On another database the same columns compile like SQLAlchemy's generic types. The known limits are in the README.


Testing

  • Unit tests pass offline. GitHub CI is green on Python 3.11, 3.12, 3.13 and 3.14.
  • The unit tests also pass with the lowest allowed versions, SQLAlchemy 2.0.0 and upstream 0.3.11.
  • Integration tests pass against a real D1 database. They create and drop their own tables.
  • ruff check, ruff format --check and mypy src are clean.
  • python -m build and twine check pass.
  • End to end in Superset. A wheel built from this branch was installed into Superset's development stack (SQLAlchemy 2.0.52), without superset-engine-d1 or dbapi-d1, against a real D1 database. All of these passed:
    • Test connection.
    • A query with zero rows keeps its column names.
    • Date columns are marked temporal with no manual step.
    • A chart with a daily time grain and a time range filter.
    • A view works as a dataset.
    • _cf_KV is hidden.
    • A dashboard loads and refreshes.

Notes for review

  • The code was reviewed once already. The review replaced the first type compiler with the reflection types, made autoincrement and the key order exact, and added the view methods. Its commits are on this branch.
  • get_columns and get_pk_constraint read PRAGMA table_info themselves, because upstream throws away the declared type and the position in the key. Worth a second look whenever the upstream pin moves.
  • There is no license classifier. Poetry warns that license classifiers are deprecated once license is set, so only the license field is used.
  • D1 refuses any name that starts with _cf_ with SQLITE_AUTH. The _cf_ view filter is covered by a unit test with a fake connection.
  • Dev dependencies moved to a dev extra so that pip install -e ".[dev]" works. With Poetry, use poetry install --all-extras.
  • The integration CI job only runs on manual dispatch. It needs the repo secrets CF_ACCOUNT_ID, CF_API_TOKEN and CF_D1_DATABASE_ID.
  • Before tagging: PyPI needs a trusted publisher for this repo (owner sqlalchemy-cf-d1, repository sqlalchemy-d1, workflow release.yml, environment pypi). Only a project Owner on PyPI can add it.

Moved to SQLAlchemy 2.0, dropped dbapi-d1 and lifted the python cap. The
dialect keeps the d1 name and adds date and boolean type names, date and
boolean reflection, autoincrement, schema and view listing and _cf_
filtering on top of upstream.
- Reflected DECIMAL as numeric and matched declared types on the first word
- Reported autoincrement only for a single INTEGER primary key column in a
  table that is not WITHOUT ROWID
- Reflected composite primary keys in key order
- Ignored URL query parameters so base_url cannot reach the driver
- Let get_view_names errors propagate instead of raising RuntimeError
- Added get_view_definition, get_table_comment and get_check_constraints
- Made has_table true for views
- Replaced the visit method overrides with reflection-only types in
  sqlalchemy_d1.types, so generic types compile the same as upstream
- Fell back to a placeholder version when package metadata is missing
- Moved the get_columns tests to an in-memory SQLite suite
@danielalyoshin
danielalyoshin marked this pull request as ready for review September 21, 2026 21:46
@danielalyoshin
danielalyoshin merged commit d60c98b into main Sep 21, 2026
6 checks passed
@danielalyoshin
danielalyoshin deleted the feat/0.2.0-shim branch September 21, 2026 22:25
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.

1 participant