Skip to content

Commit f2dcfac

Browse files
flyersworderclaude
andcommitted
fix: separate multi-statement SQL with newlines instead of concatenating (v0.3.2)
Closes #3 — multiple SQL statements in a single cell were being concatenated on one line after the semicolon. Switched from sqlglot.parse_one() to sqlglot.parse() so each statement is formatted independently and joined with ";\n". Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 089a4c8 commit f2dcfac

7 files changed

Lines changed: 91 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.3.2] - 2026-03-16
9+
10+
### Fixed
11+
12+
- Multiple SQL statements in a single cell are no longer concatenated on one line — each statement now starts on its own line after the `;` separator ([#3](https://github.com/flyersworder/sqlnbfmt/issues/3))
13+
14+
### Changed
15+
16+
- SQL parsing switched from `sqlglot.parse_one()` to `sqlglot.parse()` to correctly handle multi-statement inputs
17+
18+
### Added
19+
20+
- `multi_statement` eval fixture case covering `DROP TABLE` + `CREATE TABLE` sequences
21+
822
## [0.3.1] - 2026-03-15
923

1024
### Fixed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "sqlnbfmt"
3-
version = "0.3.1"
3+
version = "0.3.2"
44
description = "A tool to format SQL code in Jupyter notebooks."
55
authors = [
66
{ name = "Qing", email = "qingye779@gmail.com" }

sqlnbfmt/formatter.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import nbformat
1212
import yaml
13-
from sqlglot import parse_one, errors
13+
from sqlglot import parse, errors
1414

1515
DEFAULT_SQL_KEYWORDS = frozenset(
1616
{
@@ -206,10 +206,18 @@ def format_sql_code(
206206
temp_sql = temp_sql.strip()
207207

208208
# Parse and format SQL
209-
parsed = parse_one(temp_sql, read=dialect)
210-
formatted_sql = parsed.sql(
211-
pretty=not force_single_line, indent=config.indent_width, dialect=dialect
212-
)
209+
statements = parse(temp_sql, read=dialect)
210+
formatted_parts = []
211+
for stmt in statements:
212+
formatted_parts.append(
213+
stmt.sql(
214+
pretty=not force_single_line,
215+
indent=config.indent_width,
216+
dialect=dialect,
217+
)
218+
)
219+
separator = "; " if force_single_line else ";\n"
220+
formatted_sql = separator.join(formatted_parts)
213221

214222
# Apply formatting based on context
215223
if is_magic_command and not is_cell_magic:

tests/eval/generate_fixtures.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,20 @@ def non_sql_unchanged():
223223
return nb
224224

225225

226+
def multi_statement():
227+
"""Multiple SQL statements separated by semicolons — each on its own line."""
228+
nb = nbf.new_notebook()
229+
nb.cells = [
230+
nbf.new_code_cell(
231+
"%%sql\n"
232+
"DROP TABLE IF EXISTS Contract; DROP TABLE IF EXISTS Permanent; "
233+
"CREATE TABLE Employee (eid INT, name VARCHAR(80) NOT NULL, PRIMARY KEY (eid)); "
234+
"CREATE TABLE Department (did INT, name VARCHAR(30) NOT NULL, PRIMARY KEY (did))"
235+
),
236+
]
237+
return nb
238+
239+
226240
def skip_hint():
227241
"""Cell with # sqlnbfmt: skip — must remain untouched."""
228242
nb = nbf.new_notebook()
@@ -259,6 +273,7 @@ def skip_hint():
259273
"in_function_sql": in_function_sql,
260274
"idempotency": idempotency,
261275
"non_sql_unchanged": non_sql_unchanged,
276+
"multi_statement": multi_statement,
262277
"skip_hint": skip_hint,
263278
}
264279

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"id": "70bf528e",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"%%sql\n",
11+
"DROP TABLE IF EXISTS Contract;\n",
12+
"DROP TABLE IF EXISTS Permanent;\n",
13+
"CREATE TABLE Employee (\n",
14+
" eid INT,\n",
15+
" name VARCHAR(80) NOT NULL,\n",
16+
" PRIMARY KEY (eid)\n",
17+
");\n",
18+
"CREATE TABLE Department (\n",
19+
" did INT,\n",
20+
" name VARCHAR(30) NOT NULL,\n",
21+
" PRIMARY KEY (did)\n",
22+
")"
23+
]
24+
}
25+
],
26+
"metadata": {},
27+
"nbformat": 4,
28+
"nbformat_minor": 5
29+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"id": "70bf528e",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"%%sql\n",
11+
"DROP TABLE IF EXISTS Contract; DROP TABLE IF EXISTS Permanent; CREATE TABLE Employee (eid INT, name VARCHAR(80) NOT NULL, PRIMARY KEY (eid)); CREATE TABLE Department (did INT, name VARCHAR(30) NOT NULL, PRIMARY KEY (did))"
12+
]
13+
}
14+
],
15+
"metadata": {},
16+
"nbformat": 4,
17+
"nbformat_minor": 5
18+
}

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)