Skip to content

fix(mssql): strip trailing semicolons before sqlglot LIMIT rewrite - #2476

Merged
goldmedal merged 6 commits into
Canner:mainfrom
Bartok9:fix/mssql-strip-semicolon-before-parse
Jul 23, 2026
Merged

fix(mssql): strip trailing semicolons before sqlglot LIMIT rewrite#2476
goldmedal merged 6 commits into
Canner:mainfrom
Bartok9:fix/mssql-strip-semicolon-before-parse

Conversation

@Bartok9

@Bartok9 Bartok9 commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Strip the terminating ;/whitespace run before MSSQL sqlglot parse paths (_raw_cursor_sql with limit, _flatten_pagination_limit).
  • SELECT 1;; previously parsed as a Block, so LIMIT injection silently no-oped and the driver still saw multi-statement noise.

Motivation

Apache-2.0 core/wren/**. Proof on sqlglot tsql:

  • SELECT 1; → Select
  • SELECT 1;; / SELECT 1; ; → Block (LIMIT rewrite skipped)

Verification

...                                                                      [100%]
3 passed in 0.17s

Duplicates

No open PR for this sqlglot/Block interaction.

Summary by CodeRabbit

  • Bug Fixes
    • Improved MSSQL processing of SQL statements with trailing semicolons and surrounding whitespace, including multiple terminators.
    • Made pagination/limit rewriting more reliable for semicolon-terminated queries.
    • Preserves the expected SQL output when no pagination limit is requested.
  • Tests
    • Added unit tests covering trailing-semicolon stripping and correct MSSQL pagination behavior for semicolon-terminated input.

@github-actions github-actions Bot added python Pull requests that update Python code core labels Jul 11, 2026
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The MSSQL connector strips trailing semicolons before sqlglot-based pagination processing. Unit tests cover multi-semicolon removal, limited-query rewriting, and preservation of a single semicolon when no limit is applied.

Changes

MSSQL semicolon handling

Layer / File(s) Summary
Trailing-semicolon stripping contract
core/wren/src/wren/connector/mssql.py, core/wren/tests/unit/test_mssql_semicolon.py
Adds _strip_trailing_semicolon and tests removal of trailing semicolon runs and whitespace.
Pagination rewrite integration
core/wren/src/wren/connector/mssql.py, core/wren/tests/unit/test_mssql_semicolon.py
Applies stripping before pagination parsing and validates limited and unlimited SQL behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • Canner/WrenAI issue 2441 — Concerns consolidating trailing-semicolon handling into a shared connector helper.

Possibly related PRs

  • Canner/WrenAI#2407 — Applies the same helper pattern to PostgreSQL pagination rewriting.
  • Canner/WrenAI#2420 — Applies matching preprocessing before Redshift SQL rewriting.
  • Canner/WrenAI#2430 — Applies matching preprocessing before DataFusion pagination wrapping.

Suggested reviewers: goldmedal

Poem

A bunny found semicolons in a row,
Before pagination made them go.
Sqlglot now sees queries clear,
While tidy tests hop far and near.
“No double tails!” the rabbit sings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main MSSQL change: stripping trailing semicolons before sqlglot LIMIT rewriting.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (2)
core/wren/tests/unit/test_mssql_semicolon.py (2)

6-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding coverage for _flatten_pagination_limit with trailing semicolons.

_flatten_pagination_limit was also modified to strip trailing semicolons (line 139 in mssql.py), but no test exercises that path with multi-semicolon input. A test like MSSqlConnector()._flatten_pagination_limit("SELECT * FROM (SELECT 1) t LIMIT 5;;") would verify the strip + flatten interaction.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/wren/tests/unit/test_mssql_semicolon.py` around lines 6 - 20, Add a unit
test covering MSSqlConnector._flatten_pagination_limit with SQL ending in
multiple semicolons, such as “SELECT * FROM (SELECT 1) t LIMIT 5;;”. Assert the
result strips the trailing semicolons and correctly flattens the pagination
limit.

11-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tighten the limit-injection assertion.

The "5" in out condition is extremely weak — any output containing the digit 5 would satisfy the or chain, potentially masking a failure where LIMIT injection didn't work. Since "FETCH NEXT" is the real success signal for tsql dialect, consider asserting it directly and removing the catch-all "5" in out.

♻️ Proposed fix
 def test_raw_cursor_sql_injects_limit_after_multi_semicolon():
     out = MSSqlConnector._raw_cursor_sql("SELECT 1;;", 5)
-    assert "FETCH NEXT" in out.upper() or "TOP" in out.upper() or "LIMIT" in out.upper() or "5" in out
+    assert "FETCH NEXT" in out.upper()
     # Must not leave the double terminator which becomes a Block parse
     assert ";;" not in out
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/wren/tests/unit/test_mssql_semicolon.py` around lines 11 - 15,
Strengthen test_raw_cursor_sql_injects_limit_after_multi_semicolon by removing
the weak catch-all `"5" in out` condition and asserting the expected tsql
limit-injection marker directly, such as `"FETCH NEXT" in out.upper()`. Keep the
separate assertion that the output contains no double semicolon.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@core/wren/tests/unit/test_mssql_semicolon.py`:
- Around line 6-20: Add a unit test covering
MSSqlConnector._flatten_pagination_limit with SQL ending in multiple semicolons,
such as “SELECT * FROM (SELECT 1) t LIMIT 5;;”. Assert the result strips the
trailing semicolons and correctly flattens the pagination limit.
- Around line 11-15: Strengthen
test_raw_cursor_sql_injects_limit_after_multi_semicolon by removing the weak
catch-all `"5" in out` condition and asserting the expected tsql limit-injection
marker directly, such as `"FETCH NEXT" in out.upper()`. Keep the separate
assertion that the output contains no double semicolon.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cd1e510c-8a25-44dc-8edc-cf63d70417f0

📥 Commits

Reviewing files that changed from the base of the PR and between bdaefa9 and c60a9da.

📒 Files selected for processing (2)
  • core/wren/src/wren/connector/mssql.py
  • core/wren/tests/unit/test_mssql_semicolon.py

@goldmedal

Copy link
Copy Markdown
Collaborator

The shared CI failure here (maturin failedcannot update the lock file core/wren-core-py/Cargo.lock because --locked was passed) came from the Rust core 0.2.0 bump in #2467 leaving core/wren-core-py/Cargo.lock out of sync. That's now fixed on main by #2478 — please rebase onto latest main to pick up the lockfile fix and turn CI green. 🙏

@Bartok9
Bartok9 force-pushed the fix/mssql-strip-semicolon-before-parse branch from a8e23da to bf141c6 Compare July 13, 2026 02:32
@Bartok9

Bartok9 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Done — rebased onto latest main to pick up the core/wren-core-py/Cargo.lock fix from #2478. CI should go green now. Thanks @goldmedal! 🙏

@goldmedal

Copy link
Copy Markdown
Collaborator

@Bartok9, we should use the shared trailing function in this pr.

Bartok9 added 5 commits July 21, 2026 22:01
sqlglot parses SELECT 1;; as a Block, so LIMIT injection silently
no-oped. Strip the terminating ;/whitespace run first.
sqlglot tsql emits SELECT TOP n for simple selects; the unit test
required FETCH NEXT only. Accept TOP or FETCH NEXT after strip.
@Bartok9
Bartok9 force-pushed the fix/mssql-strip-semicolon-before-parse branch from 0875477 to b1f7e8f Compare July 22, 2026 02:02
@Bartok9

Bartok9 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@goldmedal Done — refactored to use the shared strip_trailing_semicolon helper from connector/base.py (dropped the per-connector _strip_trailing_semicolon copy) in both the LIMIT-inject and pagination-flatten paths, and rebased onto current main which includes it via #2480. Tests now import the shared helper; all pass. Thanks! 🙏

F821/NameError: `_strip_trailing_semicolon` is not defined after the shared helper
rename; call the public `strip_trailing_semicolon` import.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core python Pull requests that update Python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants