Skip to content

fix(d1): support SQLAlchemy 2 with sqlalchemy-d1 0.2.0 - #44505

Open
danielalyoshin wants to merge 11 commits into
apache:masterfrom
sqlalchemy-cf-d1:fix/d1-sqlalchemy-2
Open

danielalyoshin wants to merge 11 commits into
apache:masterfrom
sqlalchemy-cf-d1:fix/d1-sqlalchemy-2

Conversation

@danielalyoshin

@danielalyoshin danielalyoshin commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

SUMMARY

The d1 extra is one of the connector extras still held back from SQLAlchemy 2. UPDATING.md lists it under "the upstream dialect package has no SQLAlchemy 2.0 support yet at all", and superset/db_engine_specs/d1.py carries a known_incompatibilities entry whose tracking_url points at our repo. This PR lifts that block. I am one of the maintainers of the sqlalchemy-cf-d1 packages.

What changed on our side:

  • sqlalchemy-d1 0.2.0 supports SQLAlchemy 2. It keeps the d1:// scheme and is a thin layer over the community dialect sqlalchemy-cloudflare-d1. It is on PyPI: sqlalchemy-d1 0.2.0.
  • superset-engine-d1 and dbapi-d1 are retired. The engine spec of record is the one in this repo. It has taken priority over the external one for d1:// since 6.1.0. sqlalchemy-d1 0.2.0 gets its DBAPI from sqlalchemy-cloudflare-d1, so nothing needs dbapi-d1 any more.

What this PR changes:

  • pyproject.toml: the d1 extra installs sqlalchemy-d1>=0.2.0, <1 only.
  • superset/db_engine_specs/d1.py:
    • pypi_packages names sqlalchemy-d1, and the known_incompatibilities entry is removed.
    • install_instructions is pip install "apache-superset[d1]" with a version_requirements note, as fix(presto): require PyHive 0.7 for SQLAlchemy 2 #44285 did for Presto. A bare pip install sqlalchemy-d1 would call the requirement met on an environment that still has 0.1.0.
    • An execute override drops comments ahead of a statement. The DBAPI in sqlalchemy-cloudflare-d1 only reports column names when the statement text starts with SELECT, PRAGMA or WITH, so a query with a leading comment came back as rows without a cursor description and Superset failed on it. allows_sql_comments = False was not enough, because only the SQL Lab executor reads it. Every query path goes through execute: SQL Lab, datasets made from SQL, charts, alerts, and comments added by SQL_QUERY_MUTATOR. Comments elsewhere in the statement are left alone.
    • disallow_uri_query_params refuses base_url in the connection URI, the way mysql.py refuses local_infile. The driver sends the API token to the host that base_url names.
    • Errors show D1's own message. On an HTTP error the DBAPI puts the whole JSON reply of the D1 API in the exception, and the "no such column" pattern inherited from SQLite took the rest of it as the column name: We can't seem to resolve the column "nope at offset 9: SQLITE_ERROR"}]}". It now reads We can't seem to resolve the column "nope". The message text is the one SQLite already uses, so messages.pot does not change.
    • supports_file_upload = False. D1 has no transactions, so an upload that fails partway leaves the rows already written, and nothing undoes them.
    • default_driver is httpx, the driver name of the d1 dialect in sqlalchemy-d1 0.2.0. It was d1.
    • convert_dttm writes midnight as a bare date for DATE columns. D1 keeps a DATE as text such as 2026-09-20 and compares it as text, and the inherited literal '2026-09-20 00:00:00.000000' sorts after that day, so a time range on a DATE column started and ended one day late: 09-20 to 09-22 returned 09-21 and 09-22. sqlalchemy-d1 0.2.0 marks DATE columns temporal on its own, so these filters apply by default. A time other than midnight keeps its time part, which sorts between two days, so each day still counts as its midnight. SqliteEngineSpec has the same problem on plain SQLite; that is left for a separate PR.
  • superset/sql/parse.py: d1 maps to sqlglot's SQLite dialect. With the generic dialect, bracket identifiers and blob literals fail to parse, and data ->> '$.a' is rewritten to JSON_EXTRACT_SCALAR(data, '$.a'), which D1 does not have.
  • UPDATING.md: d1 is taken off the list of blocked extras, and a note tells existing installs to uninstall the two retired packages and that file upload is now off for D1.
  • docs/src/data/databases.json: the matching lines for D1, edited by hand as fix(docs): use modern driver package for CrateDB #42125 and fix(presto): require PyHive 0.7 for SQLAlchemy 2 #44285 did. The driver, file upload and score lines match what generate_yaml_docs() gives for the new spec.
  • tests/unit_tests/db_engine_specs/test_d1.py: new. Covers the execute override, the base_url check, the metadata, the error messages, the driver and file upload settings, SQL parsed as SQLite, time filters on DATE columns (run on an in-memory SQLite), and that the type names the dialect reports (DATETIME, TIMESTAMP, DATE, TIME, BOOLEAN) map to temporal and boolean columns.

D1 keeps internal _cf_* tables (for example _cf_KV) in every database. The sqlalchemy-d1 dialect hides them, so the spec does not need to.

Existing d1:// connections keep working. The connection string does not change.

Request: please consider this for 7.0. The 7.0 branch is the first on SQLAlchemy 2 and still lists all three packages in the d1 extra. superset-engine-d1 0.1.0 and dbapi-d1 0.1.0 both require Python below 3.12, so without this change pip install apache-superset[d1] on 7.0 only works on Python 3.11. The change is small and cherry-picks onto 7.0 with one trivial conflict in superset/sql/parse.py: 7.0 has # "databend": ??? on the line after the new "d1" entry, where master has the Databend dialect. Keep 7.0's line and add "d1": Dialects.SQLITE, above it. With that, the D1 tests pass on 7.0 (checked against 78611f6c0b). If a committer agrees, a v7.0 label would be appreciated.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

1. Test connection succeeds for a d1:// database (connection string blurred).

01-test-connection

2. SQL Lab lists the tables and the view but not _cf_KV, and a DATETIME column holding a number, an empty string and 09/21/2026 still previews.

02-sql-lab-table-list-and-messy-dates

3. A dataset from a D1 table marks its DATETIME and DATE columns temporal on its own.

03-dataset-temporal-columns

4. A line chart with a daily time grain.

04-daily-line-chart

TESTING INSTRUCTIONS

Unit tests:

pytest tests/unit_tests/db_engine_specs/test_d1.py

By hand, against a real D1 database:

  1. pip install "apache-superset[d1]", or pip install "sqlalchemy-d1>=0.2.0" into an existing Superset environment. Do not install superset-engine-d1 or dbapi-d1.
  2. Add a database with d1://{cloudflare_account_id}:{cloudflare_api_token}@{cloudflare_d1_database_id}. Test connection succeeds.
  3. In SQL Lab, SELECT * FROM <table> WHERE 1=0 returns the column headers and no rows.
  4. Create a dataset from a table with DATETIME and DATE columns. Both are marked temporal. A line chart with a daily time grain renders.
  5. _cf_KV is not in the table list.
  6. In SQL Lab, a query that starts with a -- comment line runs and shows its columns.
  7. In SQL Lab, SELECT nope FROM <table> says We can't seem to resolve the column "nope".
  8. The D1 database's settings no longer offer Allow file uploads to database, and an upload to a D1 database that already had it on is refused.
  9. A time range filter on a DATE column returns the days in the range: 09-20 to 09-22 gives 09-20 and 09-21.

Results. I reran the list on 2026-09-24 at the last commit (8072d71) against a real D1 database, with sqlalchemy-d1 0.2.0 installed from PyPI (SQLAlchemy 2.0.52, sqlalchemy-cloudflare-d1 0.4.0) and neither retired package, on Python 3.12, driving Superset through its REST API. The first run, on 2026-09-21 at 3838685, went through the UI in the docker-compose-light.yml stack on Python 3.11.14; the screenshots above are from it.

Check Result
get_engine_spec("d1") returns CloudflareD1EngineSpec, no spec load warnings in the log pass
Cloudflare D1 is in the available databases with driver httpx and file upload not supported pass
Test connection and saving a database both refuse ?base_url= in the URI (HTTP 422) pass
Test connection succeeds pass
SQL Lab: SELECT 1 pass
SQL Lab: a query with zero rows returns its column names and no error pass
SQL Lab: a query that starts with a -- or a /* */ comment runs and returns its columns pass
SQL Lab: SELECT nope FROM <table> says We can't seem to resolve the column "nope" pass
SQL Lab: SELECT * FROM nope says d1 error: no such table: nope: SQLITE_ERROR, without the JSON reply pass
SQL Lab: ->>, [bracket] identifiers and IIF run pass
A table whose DATETIME column holds a number, an empty string and 09/21/2026 still previews pass
The table list shows the tables and the view, not _cf_KV pass
A dataset from a table marks its DATETIME and DATE columns temporal on its own pass
Daily time grain with a time range filter on a DATETIME column pass
Time range filters on a DATE column, from midnight to midnight and from noon to noon pass
A dataset made from SQL that starts with a comment gets its columns, and a chart on it returns rows pass
A view works as a dataset, with a time range filter on its DATE column pass
A dashboard with a D1 chart loads and refreshes pass
An upload to a D1 database that has uploads turned on is refused (HTTP 422) and writes no table pass

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

The d1 extra installs sqlalchemy-d1>=0.2.0 only. That release supports
SQLAlchemy 2 and is built on sqlalchemy-cloudflare-d1. superset-engine-d1
and dbapi-d1 are retired, and their only releases cap Python below 3.12.

The engine spec metadata points at sqlalchemy-d1 and drops the SQLAlchemy
2.0 known incompatibility. The spec also hides D1's internal _cf_* tables
and views.
- Set allows_sql_comments = False. The DBAPI in sqlalchemy-cloudflare-d1
  only reports column names when the statement starts with SELECT, PRAGMA
  or WITH, so a query with a leading comment returned rows without a
  cursor description.
- Point the install text at the d1 extra and add a version_requirements
  note, so an environment with sqlalchemy-d1 0.1.0 is not told the
  requirement is met.
- Cap the d1 extra at <1.
- Drop the _cf_ table and view overrides; the sqlalchemy-d1 dialect
  already filters internal names.
- Drop tests that copied the SQLite convert_dttm test or only asserted
  constants, and trim the metadata test to the package and install text.
@bito-code-review

bito-code-review Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #4cdb4c

Actionable Suggestions - 0
Review Details
  • Files reviewed - 3 · Commit Range: 302d891..3838685
    • pyproject.toml
    • superset/db_engine_specs/d1.py
    • tests/unit_tests/db_engine_specs/test_d1.py
  • Files skipped - 2
    • UPDATING.md - Reason: Filter setting
    • docs/src/data/databases.json - Reason: Filter setting
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@github-actions github-actions Bot added the doc Namespace | Anything related to documentation label Sep 21, 2026
@netlify

netlify Bot commented Sep 21, 2026

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 3838685
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ab1b1c1195c8500096cf1a3
😎 Deploy Preview https://deploy-preview-44505--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit f27300e
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ab6e5906d93140008ddb371
😎 Deploy Preview https://deploy-preview-44505--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.87179% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.11%. Comparing base (4bce27f) to head (f27300e).

Files with missing lines Patch % Lines
superset/db_engine_specs/d1.py 94.87% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #44505      +/-   ##
==========================================
- Coverage   81.11%   81.11%   -0.01%     
==========================================
  Files        2955     2955              
  Lines      178330   178357      +27     
  Branches    41311    41307       -4     
==========================================
+ Hits       144651   144671      +20     
- Misses      30974    30980       +6     
- Partials     2705     2706       +1     
Flag Coverage Δ
hive 36.82% <51.28%> (+<0.01%) ⬆️
mysql 56.03% <51.28%> (-0.01%) ⬇️
postgres 56.03% <51.28%> (-0.01%) ⬇️
presto 38.73% <51.28%> (+<0.01%) ⬆️
python 85.39% <94.87%> (+<0.01%) ⬆️
sqlite 55.75% <51.28%> (-0.01%) ⬇️
unit 77.67% <94.87%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Replace allows_sql_comments = False with an execute override that drops
  comments ahead of a statement. The flag is only read by the SQL Lab
  executor, so a dataset made from SQL, an alert, or a comment added by
  SQL_QUERY_MUTATOR still reached the driver with a leading comment and came
  back as rows without a cursor description. Every query path goes through
  execute.
- Refuse base_url in the connection URI, the way MySQL refuses local_infile.
  The driver sends the API token to the host that base_url names.
@danielalyoshin
danielalyoshin marked this pull request as draft September 21, 2026 23:46
- Map the d1 engine to sqlglot's SQLite dialect. The generic dialect fails
  on bracket identifiers and blob literals, and rewrites `->>` into
  JSON_EXTRACT_SCALAR, which D1 does not have.
- Show D1's own error message instead of the whole JSON reply the DBAPI puts
  in the exception, and stop the "no such column" pattern from taking the
  offset and result code as part of the column name.
- Set default_driver to httpx, the driver name of the d1 dialect in
  sqlalchemy-d1 0.2.0.
- Turn off file upload. D1 has no transactions, so a failed upload could
  leave a half-written table.
Strings set when the module loads must be lazy, as tests/unit_tests/views/test_i18n_constants.py checks.
D1 keeps a DATE as text such as 2026-09-20 and compares it as text. The
inherited literal '2026-09-20 00:00:00' sorts after that day, so a time
range on a DATE column started and ended one day late. sqlalchemy-d1 0.2.0
marks DATE columns temporal on its own, so these filters apply by default.
@danielalyoshin
danielalyoshin marked this pull request as ready for review September 24, 2026 17:50
SQL Lab, datasets made from SQL, charts and alerts.
"""
stripped = LEADING_COMMENTS_REGEX.sub("", query, count=1)
super().execute(cursor, stripped or query, database, **kwargs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Leading comments added by SQL_QUERY_MUTATOR, including query-attribution or audit tags, are removed before execution, so D1 loses the mutator's intended database-visible tagging.

Assessment: 🟠 Major · 🔁 Occurrence: Sometimes · 🏷️ Logic error

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/db_engine_specs/d1.py
**Line:** 114:114
**Comment:**
	*Logic Error: Leading comments added by `SQL_QUERY_MUTATOR`, including query-attribution or audit tags, are removed before execution, so D1 loses the mutator's intended database-visible tagging.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is on purpose. The DBAPI in sqlalchemy-cloudflare-d1 only reports column names when the statement text starts with SELECT, PRAGMA or WITH. A query with a leading comment comes back as rows without a cursor description, and Superset fails on it (reported upstream as CollierKing/sqlalchemy-cloudflare-d1#32).

So the choice is between losing a leading tag and the query failing. Dropping the comment keeps the query working. Comments elsewhere in the statement are kept, and SQL_QUERY_MUTATOR is off by default, so this only affects deployments that prepend tags.

Once the driver reports column names for every statement, this override can go and leading comments will reach D1 again. Alternatively I can move leading comments to the end of the statement instead of dropping them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Customized review instruction saved!

Instruction:

Do not flag removing leading SQL comments before D1 execution; the sqlalchemy-cloudflare-d1 driver requires statements to start with SELECT, PRAGMA, or WITH to provide cursor column names, and preserving the comments would break query execution.

Applied to:

  • superset/db_engine_specs/d1.py

💡 To manage or update this instruction, visit: CodeAnt AI Settings

@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. The SQL_QUERY_MUTATOR often adds comments to SQL queries for auditing or attribution, but these are frequently stripped by database engine specifications before execution, causing the loss of this metadata. To resolve this, you should ensure that the SQL_QUERY_MUTATOR logic or the engine specification's mutate_sql method preserves these comments, or implement a mechanism to re-inject them if they are stripped during the query preparation phase. Since the file superset/db_engine_specs/d1.py is not present in the provided PR context, I cannot implement the specific fix for you. Please verify if there are other comments on this PR you would like me to address.

@danielalyoshin

Copy link
Copy Markdown
Contributor Author

@rusackas, you wrote superset/db_engine_specs/d1.py, so you may want to take a look. The spec changes are small: the metadata, an execute override for leading comments, convert_dttm for DATE columns, a tighter "no such column" pattern, and file upload turned off.

@villebro, since you are picking fixes into 7.0: as 7.0 stands, D1 does not really work there. Its d1.py tells users to install superset-engine-d1 and marks D1 as incompatible with SQLAlchemy 2. The d1 extra still lists the two retired packages, which only install on Python 3.11, so pip install "apache-superset[d1]" fails on 3.12 and later. On 3.11 it only works because pip happens to pick sqlalchemy-d1 0.2.0, and even then a time filter on a DATE column returns the wrong days and a query that starts with a comment fails. If this fits 7.0, the pick has one small conflict in superset/sql/parse.py, described in the PR description. I am happy to open a backport PR against 7.0 instead if that is easier.

@bito-code-review

bito-code-review Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #cf8dc1

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/db_engine_specs/d1.py - 1
    • Duplicated datetime format · Line 131-131
      The non-midnight DATE branch re-encodes the exact `isoformat(sep=" ", timespec="seconds")` literal already used in `SqliteEngineSpec.convert_dttm` (sqlite.py:168). If that format ever changes, D1's DATE filters diverge silently. Consider sharing a single formatter/constant.
Review Details
  • Files reviewed - 3 · Commit Range: 3838685..7dbac09
    • superset/db_engine_specs/d1.py
    • tests/unit_tests/db_engine_specs/test_d1.py
    • superset/sql/parse.py
  • Files skipped - 2
    • docs/src/data/databases.json - Reason: Filter setting
    • UPDATING.md - Reason: Filter setting
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@aminghadersohi aminghadersohi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared parse.py line changes no security verdict over 50 statement shapes (mutation detection strictly improves, no table reference lost), and all four master merges are byte-identical to a clean auto-merge. Four inlines; the base_url guard's only test bypasses its own lookup.

default_driver = "httpx"

# The driver sends the API token to the host named by ``base_url``
disallow_uri_query_params = {"httpx": {"base_url"}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_driver_name() splits on + only for d1+httpx://; for the bare d1:// form that metadata["connection_string"] documents it imports the dialect and returns dialect.driver. Measured: with a dialect reporting any other name, ?base_url= is allowed while this +httpx test still passes.

rows without a cursor description. Every query path goes through here:
SQL Lab, datasets made from SQL, charts and alerts.
"""
stripped = LEADING_COMMENTS_REGEX.sub("", query, count=1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

base.py:2658 get_column_description_retry_sql documents this exact driver limitation, and clickhouse.py:658 answers it by wrapping the untouched SQL rather than deleting the comment. That hook only covers get_columns_description, but your offered reposition would keep the tag on every path.

Comment on lines +43 to +45
COLUMN_DOES_NOT_EXIST_REGEX = re.compile(
r"no such column: (?P<column_name>.+?)(?: at offset \d+)?(?:: SQLITE_\w+)?$"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$ without re.MULTILINE makes this narrower than the parent it overrides: SqliteEngineSpec's pattern maps no such column: x\nsecond line to x, this one does not match at all, so a multi-line body falls back to the generic error.

Suggested change
COLUMN_DOES_NOT_EXIST_REGEX = re.compile(
r"no such column: (?P<column_name>.+?)(?: at offset \d+)?(?:: SQLITE_\w+)?$"
)
COLUMN_DOES_NOT_EXIST_REGEX = re.compile(
r"no such column: (?P<column_name>.+?)(?: at offset \d+)?(?:: SQLITE_\w+)?$",
re.MULTILINE,
)

return message

errors = reply.get("errors") if isinstance(reply, dict) else None
if isinstance(errors, list) and errors and isinstance(errors[0], dict):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mutation over the 35 new tests: dropping isinstance(errors, list), the errors truthiness, isinstance(errors[0], dict), or or message each leaves the suite at 35 passed. The first three make this method raise on a malformed reply, inside error handling.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

doc Namespace | Anything related to documentation size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants