sql-diff-explainer 0.1.2__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. sql_diff_explainer-0.1.4/PKG-INFO +284 -0
  2. sql_diff_explainer-0.1.4/README.md +270 -0
  3. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/pyproject.toml +1 -1
  4. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/src/sql_diff_explainer/__init__.py +12 -1
  5. sql_diff_explainer-0.1.4/src/sql_diff_explainer/cli.py +146 -0
  6. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/src/sql_diff_explainer/core.py +256 -0
  7. sql_diff_explainer-0.1.4/src/sql_diff_explainer/reports.py +219 -0
  8. sql_diff_explainer-0.1.4/tests/test_core.py +534 -0
  9. sql_diff_explainer-0.1.2/PKG-INFO +0 -147
  10. sql_diff_explainer-0.1.2/README.md +0 -133
  11. sql_diff_explainer-0.1.2/src/sql_diff_explainer/cli.py +0 -60
  12. sql_diff_explainer-0.1.2/tests/test_core.py +0 -204
  13. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/.github/workflows/ci.yml +0 -0
  14. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/.gitignore +0 -0
  15. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/LICENSE +0 -0
  16. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/examples/after.sql +0 -0
  17. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/examples/aggregation_after.sql +0 -0
  18. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/examples/aggregation_before.sql +0 -0
  19. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/examples/before.sql +0 -0
  20. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/examples/distinct_after.sql +0 -0
  21. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/examples/distinct_before.sql +0 -0
  22. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/examples/filter_after.sql +0 -0
  23. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/examples/filter_before.sql +0 -0
  24. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/examples/join_after.sql +0 -0
  25. {sql_diff_explainer-0.1.2 → sql_diff_explainer-0.1.4}/examples/join_before.sql +0 -0
@@ -0,0 +1,284 @@
1
+ Metadata-Version: 2.5
2
+ Name: sql-diff-explainer
3
+ Version: 0.1.4
4
+ Summary: Explain SQL schema and migration differences
5
+ Author: zmodz1010
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: sqlglot>=25.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=8.0; extra == 'dev'
12
+ Requires-Dist: ruff>=0.6; extra == 'dev'
13
+ Description-Content-Type: text/markdown
14
+
15
+ # SQL Diff Explainer
16
+
17
+ **See what changed in a SQL query—and why it might matter.**
18
+
19
+ SQL Diff Explainer compares the structure of two `SELECT` queries and returns
20
+ the changes with a static risk signal, so reviews focus on query behavior rather
21
+ than formatting.
22
+
23
+ [![PyPI version](https://img.shields.io/pypi/v/sql-diff-explainer?cacheSeconds=60)](https://pypi.org/project/sql-diff-explainer/)
24
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://pypi.org/project/sql-diff-explainer/)
25
+ [![License: MIT](https://img.shields.io/pypi/l/sql-diff-explainer)](LICENSE)
26
+
27
+ ## Contents
28
+
29
+ - [Quick start](#quick-start)
30
+ - [Command-line usage](#command-line-usage)
31
+ - [Directory comparison](#directory-comparison)
32
+ - [Git-aware pull-request comparison](#git-aware-pull-request-comparison)
33
+ - [SARIF and HTML reports](#sarif-and-html-reports)
34
+ - [What it compares](#what-it-compares)
35
+ - [Use cases](#use-cases)
36
+ - [Scope and limitations](#scope-and-limitations)
37
+ - [Development](#development)
38
+ - [Contributing](#contributing)
39
+ - [License](#license)
40
+
41
+ ## Quick start
42
+
43
+ Install the latest release from [PyPI](https://pypi.org/project/sql-diff-explainer/):
44
+
45
+ ```console
46
+ python -m pip install sql-diff-explainer
47
+ ```
48
+
49
+ Compare two queries from Python:
50
+
51
+ ```python
52
+ from sql_diff_explainer import explain_sql_change
53
+
54
+ before = """
55
+ SELECT c.customer_id, c.segment, o.order_id
56
+ FROM customers AS c
57
+ LEFT JOIN orders AS o ON c.customer_id = o.customer_id
58
+ """
59
+ after = """
60
+ SELECT c.customer_id, c.segment, o.order_id
61
+ FROM customers AS c
62
+ INNER JOIN orders AS o ON c.customer_id = o.customer_id
63
+ """
64
+
65
+ result = explain_sql_change(before, after)
66
+ print(result.summary)
67
+ for change in result.changes:
68
+ print(
69
+ f"{change.category}: {change.before} -> {change.after} "
70
+ f"({change.risk} risk)"
71
+ )
72
+ ```
73
+
74
+ Output:
75
+
76
+ ```text
77
+ 1 SQL change(s) detected; overall risk is high.
78
+ join: LEFT JOIN orders AS o ON c.customer_id = o.customer_id -> INNER JOIN orders AS o ON c.customer_id = o.customer_id (high risk)
79
+ ```
80
+
81
+ The change is classified as high risk because an `INNER JOIN` excludes
82
+ customers without a matching order, whereas a `LEFT JOIN` retains them. The
83
+ result is structured: inspect `result.risk_level` and `result.changes`, or call
84
+ `result.to_dict()` for JSON-ready data. Pass `dialect="postgres"` (or another
85
+ [sqlglot dialect](https://sqlglot.com/sqlglot/dialects.html)) for
86
+ dialect-specific SQL. Invalid SQL raises `SQLParseError`; non-`SELECT` input
87
+ raises `UnsupportedSQL`.
88
+
89
+ ## Command-line usage
90
+
91
+ The CLI compares two SQL files and prints a human-readable report by default:
92
+
93
+ ```console
94
+ sql-diff-explainer examples/join_before.sql examples/join_after.sql
95
+ ```
96
+
97
+ Choose JSON output, set a parsing dialect, or make a risk threshold fail a CI
98
+ step:
99
+
100
+ ```console
101
+ sql-diff-explainer before.sql after.sql --format json
102
+ sql-diff-explainer before.sql after.sql --dialect postgres
103
+ sql-diff-explainer before.sql after.sql --fail-on-risk medium
104
+ ```
105
+
106
+ `--fail-on-risk` accepts `low`, `medium`, or `high`. It exits with status `1`
107
+ when detected changes meet or exceed the threshold; the text or JSON report is
108
+ still printed. No detected changes exit successfully. Input-file errors and
109
+ SQL parse/support errors exit with status `2`.
110
+
111
+ Try the runnable before/after pairs in [`examples/`](examples/):
112
+
113
+ ```console
114
+ sql-diff-explainer examples/filter_before.sql examples/filter_after.sql --format json
115
+ sql-diff-explainer examples/aggregation_before.sql examples/aggregation_after.sql
116
+ sql-diff-explainer examples/distinct_before.sql examples/distinct_after.sql
117
+ ```
118
+
119
+ ### Directory comparison
120
+
121
+ Compare two directory trees recursively. SQL files are paired by their exact
122
+ relative path (with `/` separators); the `.sql` extension is matched
123
+ case-insensitively. Files present on only one side are reported as additions
124
+ or removals rather than parsed as empty SQL, and raise the aggregate risk to
125
+ high:
126
+
127
+ ```console
128
+ sql-diff-explainer --compare-dirs models/main models/review --format json
129
+ sql-diff-explainer --compare-dirs models/main models/review --fail-on-risk high
130
+ ```
131
+
132
+ The JSON report includes each matched file's ordinary SQL diff, plus
133
+ `unmatched_before` and `unmatched_after` path lists. A missing or non-directory
134
+ input, unreadable file, or invalid SQL exits with status `2`.
135
+
136
+ The same operation is available in Python:
137
+
138
+ ```python
139
+ from sql_diff_explainer import compare_sql_directories
140
+
141
+ result = compare_sql_directories("models/main", "models/review")
142
+ print(result.to_dict())
143
+ ```
144
+
145
+ ### Git-aware pull-request comparison
146
+
147
+ Compare SQL paths Git reports as changed between a base ref and the current
148
+ worktree. Modified files and SQL-to-SQL renames are compared semantically;
149
+ added or removed SQL files are reported explicitly as high risk. Untracked
150
+ files are not included. The base can be a branch, tag, or commit:
151
+
152
+ ```console
153
+ sql-diff-explainer --git-base origin/main --format json
154
+ sql-diff-explainer --git-base "$GITHUB_BASE_REF" --format sarif --output sql-diff.sarif
155
+ sql-diff-explainer --git-base origin/main --fail-on-risk high
156
+ ```
157
+
158
+ For GitHub Actions, fetch the base history and use the pull request's base
159
+ commit. This example checks out the pull-request worktree, installs this
160
+ repository's source (so the CLI includes the feature), emits SARIF, and fails
161
+ the job on high-risk changes:
162
+
163
+ ```yaml
164
+ name: SQL review
165
+ on:
166
+ pull_request:
167
+
168
+ jobs:
169
+ sql-diff:
170
+ runs-on: ubuntu-latest
171
+ steps:
172
+ - uses: actions/checkout@v4
173
+ with:
174
+ fetch-depth: 0
175
+ - uses: actions/setup-python@v5
176
+ with:
177
+ python-version: "3.12"
178
+ - run: python -m pip install .
179
+ - name: Analyze changed SQL
180
+ run: >-
181
+ sql-diff-explainer
182
+ --git-base "${{ github.event.pull_request.base.sha }}"
183
+ --format sarif
184
+ --output sql-diff.sarif
185
+ --fail-on-risk high
186
+ - name: Upload SQL findings
187
+ if: always() && hashFiles('sql-diff.sarif') != ''
188
+ uses: github/codeql-action/upload-sarif@v3
189
+ with:
190
+ sarif_file: sql-diff.sarif
191
+ ```
192
+
193
+ An unresolved base ref, Git failure, unreadable SQL, or invalid SQL exits with
194
+ status `2` and a diagnostic. Renames appear in every report; a rename does not
195
+ raise risk by itself, while semantic changes in its content retain their
196
+ normal risk level.
197
+
198
+ ### SARIF and HTML reports
199
+
200
+ Emit SARIF 2.1.0 results for GitHub code scanning integrations, or generate a
201
+ self-contained HTML report that can be attached to a review:
202
+
203
+ ```console
204
+ sql-diff-explainer before.sql after.sql --format sarif --output sql-diff.sarif
205
+ sql-diff-explainer --compare-dirs models/main models/review --format html --output review.html
206
+ ```
207
+
208
+ Both formats work with the existing `--fail-on-risk` threshold. SARIF maps
209
+ high, medium, and low risk to `error`, `warning`, and `note`; report output is
210
+ still written when the threshold causes exit status `1`. HTML escapes SQL text
211
+ and file paths. Python callers can use `to_sarif(result)` or
212
+ `render_html_report(result)` with either `SQLDiffResult` or
213
+ `SQLDirectoryDiffResult`.
214
+
215
+ ## What it compares
216
+
217
+ The analyzer compares supported `SELECT` query features and reports additions,
218
+ removals, or changes with a low, medium, or high risk classification:
219
+
220
+ | Feature | Examples of changes |
221
+ | --- | --- |
222
+ | Projections | Selected columns and expressions |
223
+ | Joins | Join target, predicate, or type |
224
+ | Filters | `WHERE` predicates |
225
+ | Aggregation | Aggregate functions and `GROUP BY` expressions |
226
+ | Ordering and pagination | `ORDER BY`, `LIMIT`, and `OFFSET` |
227
+ | Duplicate handling | `DISTINCT` added or removed |
228
+
229
+ Join or filter changes can alter which rows qualify; grouping or aggregation
230
+ changes can change a metric's meaning or result grain; and pagination changes
231
+ can affect which rows downstream consumers receive.
232
+
233
+ ## Use cases
234
+
235
+ - Review dbt models or warehouse SQL in a pull request.
236
+ - Spot join and filter changes that could alter row counts.
237
+ - Check grouping and aggregation changes that could shift metric grain.
238
+ - Add a lightweight risk threshold to a CI check.
239
+ - Explain generated SQL changes without treating formatting-only differences
240
+ as query changes.
241
+
242
+ ## Scope and limitations
243
+
244
+ This is static analysis, not a SQL execution engine or correctness proof. It
245
+ does not connect to a database, validate schemas or permissions, estimate query
246
+ plans or cost, inspect table statistics or null rates, or decide whether a
247
+ business metric is correct. It compares supported query structure; it does not
248
+ analyze a multi-statement migration file or guarantee complete semantic
249
+ equivalence across SQL dialects. Risk levels are review signals, not
250
+ predictions. Pair the report with warehouse tests, representative row-count
251
+ checks, query-plan review, and domain-owner input.
252
+
253
+ ## Development
254
+
255
+ The package requires Python 3.10 or newer and uses a `src/` layout.
256
+
257
+ ```console
258
+ python -m venv .venv
259
+ ```
260
+
261
+ Activate the environment, then install the development dependencies and run
262
+ the checks:
263
+
264
+ ```console
265
+ # macOS / Linux
266
+ source .venv/bin/activate
267
+
268
+ # Windows PowerShell
269
+ .venv\Scripts\Activate.ps1
270
+
271
+ python -m pip install -e ".[dev]"
272
+ python -m pytest
273
+ ruff check .
274
+ ```
275
+
276
+ ## Contributing
277
+
278
+ Bug reports, focused improvements, and pull requests are welcome. Please
279
+ include or update tests for behavior changes and keep documentation aligned
280
+ with the supported API.
281
+
282
+ ## License
283
+
284
+ Distributed under the [MIT License](LICENSE).
@@ -0,0 +1,270 @@
1
+ # SQL Diff Explainer
2
+
3
+ **See what changed in a SQL query—and why it might matter.**
4
+
5
+ SQL Diff Explainer compares the structure of two `SELECT` queries and returns
6
+ the changes with a static risk signal, so reviews focus on query behavior rather
7
+ than formatting.
8
+
9
+ [![PyPI version](https://img.shields.io/pypi/v/sql-diff-explainer?cacheSeconds=60)](https://pypi.org/project/sql-diff-explainer/)
10
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://pypi.org/project/sql-diff-explainer/)
11
+ [![License: MIT](https://img.shields.io/pypi/l/sql-diff-explainer)](LICENSE)
12
+
13
+ ## Contents
14
+
15
+ - [Quick start](#quick-start)
16
+ - [Command-line usage](#command-line-usage)
17
+ - [Directory comparison](#directory-comparison)
18
+ - [Git-aware pull-request comparison](#git-aware-pull-request-comparison)
19
+ - [SARIF and HTML reports](#sarif-and-html-reports)
20
+ - [What it compares](#what-it-compares)
21
+ - [Use cases](#use-cases)
22
+ - [Scope and limitations](#scope-and-limitations)
23
+ - [Development](#development)
24
+ - [Contributing](#contributing)
25
+ - [License](#license)
26
+
27
+ ## Quick start
28
+
29
+ Install the latest release from [PyPI](https://pypi.org/project/sql-diff-explainer/):
30
+
31
+ ```console
32
+ python -m pip install sql-diff-explainer
33
+ ```
34
+
35
+ Compare two queries from Python:
36
+
37
+ ```python
38
+ from sql_diff_explainer import explain_sql_change
39
+
40
+ before = """
41
+ SELECT c.customer_id, c.segment, o.order_id
42
+ FROM customers AS c
43
+ LEFT JOIN orders AS o ON c.customer_id = o.customer_id
44
+ """
45
+ after = """
46
+ SELECT c.customer_id, c.segment, o.order_id
47
+ FROM customers AS c
48
+ INNER JOIN orders AS o ON c.customer_id = o.customer_id
49
+ """
50
+
51
+ result = explain_sql_change(before, after)
52
+ print(result.summary)
53
+ for change in result.changes:
54
+ print(
55
+ f"{change.category}: {change.before} -> {change.after} "
56
+ f"({change.risk} risk)"
57
+ )
58
+ ```
59
+
60
+ Output:
61
+
62
+ ```text
63
+ 1 SQL change(s) detected; overall risk is high.
64
+ join: LEFT JOIN orders AS o ON c.customer_id = o.customer_id -> INNER JOIN orders AS o ON c.customer_id = o.customer_id (high risk)
65
+ ```
66
+
67
+ The change is classified as high risk because an `INNER JOIN` excludes
68
+ customers without a matching order, whereas a `LEFT JOIN` retains them. The
69
+ result is structured: inspect `result.risk_level` and `result.changes`, or call
70
+ `result.to_dict()` for JSON-ready data. Pass `dialect="postgres"` (or another
71
+ [sqlglot dialect](https://sqlglot.com/sqlglot/dialects.html)) for
72
+ dialect-specific SQL. Invalid SQL raises `SQLParseError`; non-`SELECT` input
73
+ raises `UnsupportedSQL`.
74
+
75
+ ## Command-line usage
76
+
77
+ The CLI compares two SQL files and prints a human-readable report by default:
78
+
79
+ ```console
80
+ sql-diff-explainer examples/join_before.sql examples/join_after.sql
81
+ ```
82
+
83
+ Choose JSON output, set a parsing dialect, or make a risk threshold fail a CI
84
+ step:
85
+
86
+ ```console
87
+ sql-diff-explainer before.sql after.sql --format json
88
+ sql-diff-explainer before.sql after.sql --dialect postgres
89
+ sql-diff-explainer before.sql after.sql --fail-on-risk medium
90
+ ```
91
+
92
+ `--fail-on-risk` accepts `low`, `medium`, or `high`. It exits with status `1`
93
+ when detected changes meet or exceed the threshold; the text or JSON report is
94
+ still printed. No detected changes exit successfully. Input-file errors and
95
+ SQL parse/support errors exit with status `2`.
96
+
97
+ Try the runnable before/after pairs in [`examples/`](examples/):
98
+
99
+ ```console
100
+ sql-diff-explainer examples/filter_before.sql examples/filter_after.sql --format json
101
+ sql-diff-explainer examples/aggregation_before.sql examples/aggregation_after.sql
102
+ sql-diff-explainer examples/distinct_before.sql examples/distinct_after.sql
103
+ ```
104
+
105
+ ### Directory comparison
106
+
107
+ Compare two directory trees recursively. SQL files are paired by their exact
108
+ relative path (with `/` separators); the `.sql` extension is matched
109
+ case-insensitively. Files present on only one side are reported as additions
110
+ or removals rather than parsed as empty SQL, and raise the aggregate risk to
111
+ high:
112
+
113
+ ```console
114
+ sql-diff-explainer --compare-dirs models/main models/review --format json
115
+ sql-diff-explainer --compare-dirs models/main models/review --fail-on-risk high
116
+ ```
117
+
118
+ The JSON report includes each matched file's ordinary SQL diff, plus
119
+ `unmatched_before` and `unmatched_after` path lists. A missing or non-directory
120
+ input, unreadable file, or invalid SQL exits with status `2`.
121
+
122
+ The same operation is available in Python:
123
+
124
+ ```python
125
+ from sql_diff_explainer import compare_sql_directories
126
+
127
+ result = compare_sql_directories("models/main", "models/review")
128
+ print(result.to_dict())
129
+ ```
130
+
131
+ ### Git-aware pull-request comparison
132
+
133
+ Compare SQL paths Git reports as changed between a base ref and the current
134
+ worktree. Modified files and SQL-to-SQL renames are compared semantically;
135
+ added or removed SQL files are reported explicitly as high risk. Untracked
136
+ files are not included. The base can be a branch, tag, or commit:
137
+
138
+ ```console
139
+ sql-diff-explainer --git-base origin/main --format json
140
+ sql-diff-explainer --git-base "$GITHUB_BASE_REF" --format sarif --output sql-diff.sarif
141
+ sql-diff-explainer --git-base origin/main --fail-on-risk high
142
+ ```
143
+
144
+ For GitHub Actions, fetch the base history and use the pull request's base
145
+ commit. This example checks out the pull-request worktree, installs this
146
+ repository's source (so the CLI includes the feature), emits SARIF, and fails
147
+ the job on high-risk changes:
148
+
149
+ ```yaml
150
+ name: SQL review
151
+ on:
152
+ pull_request:
153
+
154
+ jobs:
155
+ sql-diff:
156
+ runs-on: ubuntu-latest
157
+ steps:
158
+ - uses: actions/checkout@v4
159
+ with:
160
+ fetch-depth: 0
161
+ - uses: actions/setup-python@v5
162
+ with:
163
+ python-version: "3.12"
164
+ - run: python -m pip install .
165
+ - name: Analyze changed SQL
166
+ run: >-
167
+ sql-diff-explainer
168
+ --git-base "${{ github.event.pull_request.base.sha }}"
169
+ --format sarif
170
+ --output sql-diff.sarif
171
+ --fail-on-risk high
172
+ - name: Upload SQL findings
173
+ if: always() && hashFiles('sql-diff.sarif') != ''
174
+ uses: github/codeql-action/upload-sarif@v3
175
+ with:
176
+ sarif_file: sql-diff.sarif
177
+ ```
178
+
179
+ An unresolved base ref, Git failure, unreadable SQL, or invalid SQL exits with
180
+ status `2` and a diagnostic. Renames appear in every report; a rename does not
181
+ raise risk by itself, while semantic changes in its content retain their
182
+ normal risk level.
183
+
184
+ ### SARIF and HTML reports
185
+
186
+ Emit SARIF 2.1.0 results for GitHub code scanning integrations, or generate a
187
+ self-contained HTML report that can be attached to a review:
188
+
189
+ ```console
190
+ sql-diff-explainer before.sql after.sql --format sarif --output sql-diff.sarif
191
+ sql-diff-explainer --compare-dirs models/main models/review --format html --output review.html
192
+ ```
193
+
194
+ Both formats work with the existing `--fail-on-risk` threshold. SARIF maps
195
+ high, medium, and low risk to `error`, `warning`, and `note`; report output is
196
+ still written when the threshold causes exit status `1`. HTML escapes SQL text
197
+ and file paths. Python callers can use `to_sarif(result)` or
198
+ `render_html_report(result)` with either `SQLDiffResult` or
199
+ `SQLDirectoryDiffResult`.
200
+
201
+ ## What it compares
202
+
203
+ The analyzer compares supported `SELECT` query features and reports additions,
204
+ removals, or changes with a low, medium, or high risk classification:
205
+
206
+ | Feature | Examples of changes |
207
+ | --- | --- |
208
+ | Projections | Selected columns and expressions |
209
+ | Joins | Join target, predicate, or type |
210
+ | Filters | `WHERE` predicates |
211
+ | Aggregation | Aggregate functions and `GROUP BY` expressions |
212
+ | Ordering and pagination | `ORDER BY`, `LIMIT`, and `OFFSET` |
213
+ | Duplicate handling | `DISTINCT` added or removed |
214
+
215
+ Join or filter changes can alter which rows qualify; grouping or aggregation
216
+ changes can change a metric's meaning or result grain; and pagination changes
217
+ can affect which rows downstream consumers receive.
218
+
219
+ ## Use cases
220
+
221
+ - Review dbt models or warehouse SQL in a pull request.
222
+ - Spot join and filter changes that could alter row counts.
223
+ - Check grouping and aggregation changes that could shift metric grain.
224
+ - Add a lightweight risk threshold to a CI check.
225
+ - Explain generated SQL changes without treating formatting-only differences
226
+ as query changes.
227
+
228
+ ## Scope and limitations
229
+
230
+ This is static analysis, not a SQL execution engine or correctness proof. It
231
+ does not connect to a database, validate schemas or permissions, estimate query
232
+ plans or cost, inspect table statistics or null rates, or decide whether a
233
+ business metric is correct. It compares supported query structure; it does not
234
+ analyze a multi-statement migration file or guarantee complete semantic
235
+ equivalence across SQL dialects. Risk levels are review signals, not
236
+ predictions. Pair the report with warehouse tests, representative row-count
237
+ checks, query-plan review, and domain-owner input.
238
+
239
+ ## Development
240
+
241
+ The package requires Python 3.10 or newer and uses a `src/` layout.
242
+
243
+ ```console
244
+ python -m venv .venv
245
+ ```
246
+
247
+ Activate the environment, then install the development dependencies and run
248
+ the checks:
249
+
250
+ ```console
251
+ # macOS / Linux
252
+ source .venv/bin/activate
253
+
254
+ # Windows PowerShell
255
+ .venv\Scripts\Activate.ps1
256
+
257
+ python -m pip install -e ".[dev]"
258
+ python -m pytest
259
+ ruff check .
260
+ ```
261
+
262
+ ## Contributing
263
+
264
+ Bug reports, focused improvements, and pull requests are welcome. Please
265
+ include or update tests for behavior changes and keep documentation aligned
266
+ with the supported API.
267
+
268
+ ## License
269
+
270
+ Distributed under the [MIT License](LICENSE).
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sql-diff-explainer"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "Explain SQL schema and migration differences"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -4,23 +4,34 @@ from .core import (
4
4
  SQLChange,
5
5
  SQLDiffError,
6
6
  SQLDiffResult,
7
+ SQLDirectoryDiffResult,
8
+ SQLFileDiff,
7
9
  SqlglotParser,
8
10
  SQLParseError,
9
11
  SQLParser,
10
12
  UnsupportedSQL,
13
+ compare_sql_directories,
14
+ compare_sql_git,
11
15
  explain_diff,
12
16
  explain_sql_change,
13
17
  )
18
+ from .reports import render_html_report, to_sarif
14
19
 
15
20
  __all__ = [
16
21
  "SQLChange",
22
+ "SQLDirectoryDiffResult",
17
23
  "SQLDiffError",
18
24
  "SQLDiffResult",
25
+ "SQLFileDiff",
19
26
  "SQLParseError",
20
27
  "SQLParser",
21
28
  "SqlglotParser",
22
29
  "UnsupportedSQL",
30
+ "compare_sql_directories",
31
+ "compare_sql_git",
23
32
  "explain_diff",
24
33
  "explain_sql_change",
34
+ "render_html_report",
35
+ "to_sarif",
25
36
  ]
26
- __version__ = "0.1.2"
37
+ __version__ = "0.1.4"