Skip to content

feat(lint): recognize arrowbricks SQL calls - #82

Merged
aersam merged 3 commits into
mainfrom
lint-arrowbricks
Oct 12, 2026
Merged

aersam merged 3 commits into
mainfrom
lint-arrowbricks

Conversation

@aersam

@aersam aersam commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

bdt lint and bdt find-injection now recognize arrowbricks (the Databricks SQL client), which gained t-strings and LiteralString typing in 6.1.0.

What

  • Sinks: Cursor.execute/execute_streamed, DatabricksClient.query_agent/stream_query_json, and the free arrowbricks.stream_query_json(client, sql) (SQL is its second argument; also under from arrowbricks import stream_query_json as x and arrowbricks[._streaming].…). sql= keyword works. The three new names match by name anywhere, like pgdevkit's fetch_all (a client injected from another module is the common case).
  • Rules: only the injection rules (f-string / concat / % / .format()) apply to them, with a fix hint pointing at an arrowbricks t-string ({value} binds, {name:i} identifier) or parameters=[...]/params=[...]. The postgres house style (sql-inline-too-complex, sql-positional-param, sql-forbidden-join) is not applied: Databricks SQL isn't postgres.
  • Dialect: the probe is also parsed as Databricks SQL (backticks, QUALIFY, …), so such f-strings are no longer missed. A plain .execute() gets the extra dialect only in a file that imports arrowbricks (and keeps the postgres rules there, since it may be a psycopg cursor); everything else is unchanged.
  • unsafe_sql(x) (arrowbricks' typing-only escape hatch) is treated like cast(LiteralString, x): only as safe as x; bdt find-injection raises sql-unverified-cast for unsafe_sql(request_sql). t-strings were already trusted.
  • A *_sql literal passed only to an arrowbricks call skips the complexity check at its assignment (not when the same name also reaches a psycopg sink).
  • Version 0.35.0 → 0.36.0.

Verification

  • 13 of the new tests fail when the arrowbricks sinks are removed (mutation check); full suite 989 passed, ruff/ty clean.
  • Dogfooded on arrowbricks' own FastAPI examples/CLI: bdt lint is clean, bdt find-injection lists each unsafe_sql(sql) for review, as intended.
  • Reviewed by parallel correctness / docs+tests+robustness reviewers; findings fixed in the second commit.

Known trade-offs

  • An unrelated agent.query_agent(f"select …") whose text parses as SQL is flagged (same by-name approach as pgdevkit).
  • unsafe_sql is recognized in any file, not only arrowbricks ones.

🤖 Generated with Claude Code

Claude Session: 5d82abe3-e5c6-5795-9028-095258572af3

@aersam
aersam marked this pull request as ready for review October 12, 2026 07:36
@aersam
aersam merged commit cfd8d1f into main Oct 12, 2026
1 check passed
aersam and others added 3 commits October 12, 2026 09:28
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…agent, stream_query_json)

Injection rules apply with Databricks-dialect parsing and arrowbricks-specific
fix hints; the postgres house style is not applied to them. unsafe_sql(x) is
checked like cast(LiteralString, x).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- scope the *_sql complexity suppression: a name also handed to a psycopg sink keeps the check
- recognise execute(sql=...) in arrowbricks files, import aliases and attribute chains of the
  free stream_query_json, a starred first argument with the sql= keyword
- fix hints name params=/parameters= correctly and stay neutral for a plain .execute()
- tests prove recognition (13 fail without the feature), plus find-injection coverage

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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