sql-diff-explainer 0.1.4__tar.gz → 0.1.5__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.
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/PKG-INFO +75 -6
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/README.md +74 -5
- sql_diff_explainer-0.1.5/examples/cte_lineage_after.sql +7 -0
- sql_diff_explainer-0.1.5/examples/cte_lineage_before.sql +7 -0
- sql_diff_explainer-0.1.5/examples/migration.sql +4 -0
- sql_diff_explainer-0.1.5/examples/output_contract_after.sql +4 -0
- sql_diff_explainer-0.1.5/examples/output_contract_before.sql +5 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/pyproject.toml +1 -1
- sql_diff_explainer-0.1.5/src/sql_diff_explainer/__init__.py +65 -0
- sql_diff_explainer-0.1.5/src/sql_diff_explainer/analysis.py +804 -0
- sql_diff_explainer-0.1.5/src/sql_diff_explainer/cli.py +257 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/src/sql_diff_explainer/core.py +91 -1
- sql_diff_explainer-0.1.5/src/sql_diff_explainer/reports.py +412 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/tests/test_core.py +260 -0
- sql_diff_explainer-0.1.4/src/sql_diff_explainer/__init__.py +0 -37
- sql_diff_explainer-0.1.4/src/sql_diff_explainer/cli.py +0 -146
- sql_diff_explainer-0.1.4/src/sql_diff_explainer/reports.py +0 -219
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/.github/workflows/ci.yml +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/.gitignore +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/LICENSE +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/examples/after.sql +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/examples/aggregation_after.sql +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/examples/aggregation_before.sql +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/examples/before.sql +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/examples/distinct_after.sql +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/examples/distinct_before.sql +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/examples/filter_after.sql +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/examples/filter_before.sql +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/examples/join_after.sql +0 -0
- {sql_diff_explainer-0.1.4 → sql_diff_explainer-0.1.5}/examples/join_before.sql +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sql-diff-explainer
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Explain SQL schema and migration differences
|
|
5
5
|
Author: zmodz1010
|
|
6
6
|
License: MIT
|
|
@@ -31,6 +31,7 @@ than formatting.
|
|
|
31
31
|
- [Directory comparison](#directory-comparison)
|
|
32
32
|
- [Git-aware pull-request comparison](#git-aware-pull-request-comparison)
|
|
33
33
|
- [SARIF and HTML reports](#sarif-and-html-reports)
|
|
34
|
+
- [Additional SQL QA analyses](#additional-sql-qa-analyses)
|
|
34
35
|
- [What it compares](#what-it-compares)
|
|
35
36
|
- [Use cases](#use-cases)
|
|
36
37
|
- [Scope and limitations](#scope-and-limitations)
|
|
@@ -114,8 +115,15 @@ Try the runnable before/after pairs in [`examples/`](examples/):
|
|
|
114
115
|
sql-diff-explainer examples/filter_before.sql examples/filter_after.sql --format json
|
|
115
116
|
sql-diff-explainer examples/aggregation_before.sql examples/aggregation_after.sql
|
|
116
117
|
sql-diff-explainer examples/distinct_before.sql examples/distinct_after.sql
|
|
118
|
+
sql-diff-explainer examples/cte_lineage_before.sql examples/cte_lineage_after.sql --format json
|
|
119
|
+
sql-diff-explainer examples/output_contract_before.sql examples/output_contract_after.sql --format json
|
|
120
|
+
sql-diff-explainer --analyze-migration examples/migration.sql --format json
|
|
117
121
|
```
|
|
118
122
|
|
|
123
|
+
`--analyze-migration FILE` accepts multi-statement migration scripts and emits
|
|
124
|
+
text or JSON. It supports common `ALTER TABLE` column operations; SARIF and
|
|
125
|
+
HTML formats are not available for this separate migration mode.
|
|
126
|
+
|
|
119
127
|
### Directory comparison
|
|
120
128
|
|
|
121
129
|
Compare two directory trees recursively. SQL files are paired by their exact
|
|
@@ -212,6 +220,63 @@ and file paths. Python callers can use `to_sarif(result)` or
|
|
|
212
220
|
`render_html_report(result)` with either `SQLDiffResult` or
|
|
213
221
|
`SQLDirectoryDiffResult`.
|
|
214
222
|
|
|
223
|
+
## Additional SQL QA analyses
|
|
224
|
+
|
|
225
|
+
Query comparisons include three static QA views in `SQLDiffResult` and JSON
|
|
226
|
+
output:
|
|
227
|
+
|
|
228
|
+
- **CTE lineage** (`lineage`) traces explicit output expressions through
|
|
229
|
+
non-recursive CTE chains to their source columns. Changed sources or
|
|
230
|
+
expressions are also included in `changes` and the risk summary. Lineage
|
|
231
|
+
requires explicit projections, aliases for calculated expressions, and
|
|
232
|
+
direct table/CTE sources; recursive CTEs, wildcard projections, nested
|
|
233
|
+
subqueries, CTE column alias lists, and ambiguous unqualified columns are
|
|
234
|
+
reported as unsupported.
|
|
235
|
+
- **Output-schema contracts** (`schema_contract`) report added, removed,
|
|
236
|
+
renamed, reordered, changed, and supported type-changed output columns.
|
|
237
|
+
Types are inferred only for explicit casts and aliased literals; column types
|
|
238
|
+
that require a database catalog remain unknown. Wildcard projections, unnamed
|
|
239
|
+
expressions, and set operations are reported as unsupported.
|
|
240
|
+
- **Validation suggestions** (`validation_checks`) provide a checklist and,
|
|
241
|
+
when the before/after output columns are compatible, SQL for unmatched-row
|
|
242
|
+
and duplicate checks. The unmatched-row query uses `EXCEPT`; adapt it for
|
|
243
|
+
dialects that use different set-difference syntax. These checks are
|
|
244
|
+
suggestions only: SQL Diff Explainer never connects to a database or
|
|
245
|
+
executes generated SQL.
|
|
246
|
+
|
|
247
|
+
Use the analysis helpers directly when needed:
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
from sql_diff_explainer import (
|
|
251
|
+
analyze_migration,
|
|
252
|
+
compare_output_schema,
|
|
253
|
+
trace_cte_lineage,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
before_sql = """
|
|
257
|
+
WITH totals AS (SELECT customer_id, amount FROM sales)
|
|
258
|
+
SELECT customer_id, amount FROM totals
|
|
259
|
+
"""
|
|
260
|
+
after_sql = """
|
|
261
|
+
WITH totals AS (SELECT customer_id, net_amount AS amount FROM sales)
|
|
262
|
+
SELECT customer_id, amount FROM totals
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
lineage = trace_cte_lineage(before_sql, after_sql)
|
|
266
|
+
contract = compare_output_schema(before_sql, after_sql)
|
|
267
|
+
migration = analyze_migration(
|
|
268
|
+
"ALTER TABLE users ADD COLUMN email TEXT;"
|
|
269
|
+
"ALTER TABLE users RENAME COLUMN old_email TO email_address;"
|
|
270
|
+
)
|
|
271
|
+
print(lineage.to_dict(), contract.to_dict(), migration.to_dict())
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Migration analysis recognizes `ADD COLUMN`, `DROP COLUMN`, `RENAME COLUMN`,
|
|
275
|
+
`ALTER COLUMN ... TYPE`, and `ALTER COLUMN ... SET/DROP NOT NULL`. Other
|
|
276
|
+
statements and ALTER actions are listed as unsupported rather than being
|
|
277
|
+
silently treated as analyzed. Use `--analyze-migration` for a file or
|
|
278
|
+
`analyze_migration(sql)` from Python.
|
|
279
|
+
|
|
215
280
|
## What it compares
|
|
216
281
|
|
|
217
282
|
The analyzer compares supported `SELECT` query features and reports additions,
|
|
@@ -225,6 +290,7 @@ removals, or changes with a low, medium, or high risk classification:
|
|
|
225
290
|
| Aggregation | Aggregate functions and `GROUP BY` expressions |
|
|
226
291
|
| Ordering and pagination | `ORDER BY`, `LIMIT`, and `OFFSET` |
|
|
227
292
|
| Duplicate handling | `DISTINCT` added or removed |
|
|
293
|
+
| Output schema | Result-column additions, removals, renames, ordering, and known type changes |
|
|
228
294
|
|
|
229
295
|
Join or filter changes can alter which rows qualify; grouping or aggregation
|
|
230
296
|
changes can change a metric's meaning or result grain; and pagination changes
|
|
@@ -244,11 +310,14 @@ can affect which rows downstream consumers receive.
|
|
|
244
310
|
This is static analysis, not a SQL execution engine or correctness proof. It
|
|
245
311
|
does not connect to a database, validate schemas or permissions, estimate query
|
|
246
312
|
plans or cost, inspect table statistics or null rates, or decide whether a
|
|
247
|
-
business metric is correct.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
313
|
+
business metric is correct. Output-column types that require database metadata
|
|
314
|
+
are unknown. CTE lineage and query validation SQL support only the documented
|
|
315
|
+
static cases, and migration analysis recognizes only the listed `ALTER TABLE`
|
|
316
|
+
operations. Generated validation SQL is not executed and may need dialect
|
|
317
|
+
adaptation. The analyzer does not guarantee complete semantic equivalence
|
|
318
|
+
across SQL dialects. Risk levels are review signals, not predictions. Pair the
|
|
319
|
+
report with warehouse tests, representative row-count checks, query-plan
|
|
320
|
+
review, and domain-owner input.
|
|
252
321
|
|
|
253
322
|
## Development
|
|
254
323
|
|
|
@@ -17,6 +17,7 @@ than formatting.
|
|
|
17
17
|
- [Directory comparison](#directory-comparison)
|
|
18
18
|
- [Git-aware pull-request comparison](#git-aware-pull-request-comparison)
|
|
19
19
|
- [SARIF and HTML reports](#sarif-and-html-reports)
|
|
20
|
+
- [Additional SQL QA analyses](#additional-sql-qa-analyses)
|
|
20
21
|
- [What it compares](#what-it-compares)
|
|
21
22
|
- [Use cases](#use-cases)
|
|
22
23
|
- [Scope and limitations](#scope-and-limitations)
|
|
@@ -100,8 +101,15 @@ Try the runnable before/after pairs in [`examples/`](examples/):
|
|
|
100
101
|
sql-diff-explainer examples/filter_before.sql examples/filter_after.sql --format json
|
|
101
102
|
sql-diff-explainer examples/aggregation_before.sql examples/aggregation_after.sql
|
|
102
103
|
sql-diff-explainer examples/distinct_before.sql examples/distinct_after.sql
|
|
104
|
+
sql-diff-explainer examples/cte_lineage_before.sql examples/cte_lineage_after.sql --format json
|
|
105
|
+
sql-diff-explainer examples/output_contract_before.sql examples/output_contract_after.sql --format json
|
|
106
|
+
sql-diff-explainer --analyze-migration examples/migration.sql --format json
|
|
103
107
|
```
|
|
104
108
|
|
|
109
|
+
`--analyze-migration FILE` accepts multi-statement migration scripts and emits
|
|
110
|
+
text or JSON. It supports common `ALTER TABLE` column operations; SARIF and
|
|
111
|
+
HTML formats are not available for this separate migration mode.
|
|
112
|
+
|
|
105
113
|
### Directory comparison
|
|
106
114
|
|
|
107
115
|
Compare two directory trees recursively. SQL files are paired by their exact
|
|
@@ -198,6 +206,63 @@ and file paths. Python callers can use `to_sarif(result)` or
|
|
|
198
206
|
`render_html_report(result)` with either `SQLDiffResult` or
|
|
199
207
|
`SQLDirectoryDiffResult`.
|
|
200
208
|
|
|
209
|
+
## Additional SQL QA analyses
|
|
210
|
+
|
|
211
|
+
Query comparisons include three static QA views in `SQLDiffResult` and JSON
|
|
212
|
+
output:
|
|
213
|
+
|
|
214
|
+
- **CTE lineage** (`lineage`) traces explicit output expressions through
|
|
215
|
+
non-recursive CTE chains to their source columns. Changed sources or
|
|
216
|
+
expressions are also included in `changes` and the risk summary. Lineage
|
|
217
|
+
requires explicit projections, aliases for calculated expressions, and
|
|
218
|
+
direct table/CTE sources; recursive CTEs, wildcard projections, nested
|
|
219
|
+
subqueries, CTE column alias lists, and ambiguous unqualified columns are
|
|
220
|
+
reported as unsupported.
|
|
221
|
+
- **Output-schema contracts** (`schema_contract`) report added, removed,
|
|
222
|
+
renamed, reordered, changed, and supported type-changed output columns.
|
|
223
|
+
Types are inferred only for explicit casts and aliased literals; column types
|
|
224
|
+
that require a database catalog remain unknown. Wildcard projections, unnamed
|
|
225
|
+
expressions, and set operations are reported as unsupported.
|
|
226
|
+
- **Validation suggestions** (`validation_checks`) provide a checklist and,
|
|
227
|
+
when the before/after output columns are compatible, SQL for unmatched-row
|
|
228
|
+
and duplicate checks. The unmatched-row query uses `EXCEPT`; adapt it for
|
|
229
|
+
dialects that use different set-difference syntax. These checks are
|
|
230
|
+
suggestions only: SQL Diff Explainer never connects to a database or
|
|
231
|
+
executes generated SQL.
|
|
232
|
+
|
|
233
|
+
Use the analysis helpers directly when needed:
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
from sql_diff_explainer import (
|
|
237
|
+
analyze_migration,
|
|
238
|
+
compare_output_schema,
|
|
239
|
+
trace_cte_lineage,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
before_sql = """
|
|
243
|
+
WITH totals AS (SELECT customer_id, amount FROM sales)
|
|
244
|
+
SELECT customer_id, amount FROM totals
|
|
245
|
+
"""
|
|
246
|
+
after_sql = """
|
|
247
|
+
WITH totals AS (SELECT customer_id, net_amount AS amount FROM sales)
|
|
248
|
+
SELECT customer_id, amount FROM totals
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
lineage = trace_cte_lineage(before_sql, after_sql)
|
|
252
|
+
contract = compare_output_schema(before_sql, after_sql)
|
|
253
|
+
migration = analyze_migration(
|
|
254
|
+
"ALTER TABLE users ADD COLUMN email TEXT;"
|
|
255
|
+
"ALTER TABLE users RENAME COLUMN old_email TO email_address;"
|
|
256
|
+
)
|
|
257
|
+
print(lineage.to_dict(), contract.to_dict(), migration.to_dict())
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Migration analysis recognizes `ADD COLUMN`, `DROP COLUMN`, `RENAME COLUMN`,
|
|
261
|
+
`ALTER COLUMN ... TYPE`, and `ALTER COLUMN ... SET/DROP NOT NULL`. Other
|
|
262
|
+
statements and ALTER actions are listed as unsupported rather than being
|
|
263
|
+
silently treated as analyzed. Use `--analyze-migration` for a file or
|
|
264
|
+
`analyze_migration(sql)` from Python.
|
|
265
|
+
|
|
201
266
|
## What it compares
|
|
202
267
|
|
|
203
268
|
The analyzer compares supported `SELECT` query features and reports additions,
|
|
@@ -211,6 +276,7 @@ removals, or changes with a low, medium, or high risk classification:
|
|
|
211
276
|
| Aggregation | Aggregate functions and `GROUP BY` expressions |
|
|
212
277
|
| Ordering and pagination | `ORDER BY`, `LIMIT`, and `OFFSET` |
|
|
213
278
|
| Duplicate handling | `DISTINCT` added or removed |
|
|
279
|
+
| Output schema | Result-column additions, removals, renames, ordering, and known type changes |
|
|
214
280
|
|
|
215
281
|
Join or filter changes can alter which rows qualify; grouping or aggregation
|
|
216
282
|
changes can change a metric's meaning or result grain; and pagination changes
|
|
@@ -230,11 +296,14 @@ can affect which rows downstream consumers receive.
|
|
|
230
296
|
This is static analysis, not a SQL execution engine or correctness proof. It
|
|
231
297
|
does not connect to a database, validate schemas or permissions, estimate query
|
|
232
298
|
plans or cost, inspect table statistics or null rates, or decide whether a
|
|
233
|
-
business metric is correct.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
299
|
+
business metric is correct. Output-column types that require database metadata
|
|
300
|
+
are unknown. CTE lineage and query validation SQL support only the documented
|
|
301
|
+
static cases, and migration analysis recognizes only the listed `ALTER TABLE`
|
|
302
|
+
operations. Generated validation SQL is not executed and may need dialect
|
|
303
|
+
adaptation. The analyzer does not guarantee complete semantic equivalence
|
|
304
|
+
across SQL dialects. Risk levels are review signals, not predictions. Pair the
|
|
305
|
+
report with warehouse tests, representative row-count checks, query-plan
|
|
306
|
+
review, and domain-owner input.
|
|
238
307
|
|
|
239
308
|
## Development
|
|
240
309
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Tools for explaining SQL query differences."""
|
|
2
|
+
|
|
3
|
+
from .analysis import (
|
|
4
|
+
CTELineageDiff,
|
|
5
|
+
LineageChange,
|
|
6
|
+
MigrationAnalysisResult,
|
|
7
|
+
MigrationOperation,
|
|
8
|
+
OutputLineage,
|
|
9
|
+
SchemaChange,
|
|
10
|
+
SchemaColumn,
|
|
11
|
+
SchemaContractDiff,
|
|
12
|
+
UnsupportedMigrationStatement,
|
|
13
|
+
ValidationSuggestion,
|
|
14
|
+
analyze_migration,
|
|
15
|
+
compare_output_schema,
|
|
16
|
+
trace_cte_lineage,
|
|
17
|
+
)
|
|
18
|
+
from .core import (
|
|
19
|
+
SQLChange,
|
|
20
|
+
SQLDiffError,
|
|
21
|
+
SQLDiffResult,
|
|
22
|
+
SQLDirectoryDiffResult,
|
|
23
|
+
SQLFileDiff,
|
|
24
|
+
SqlglotParser,
|
|
25
|
+
SQLParseError,
|
|
26
|
+
SQLParser,
|
|
27
|
+
UnsupportedSQL,
|
|
28
|
+
compare_sql_directories,
|
|
29
|
+
compare_sql_git,
|
|
30
|
+
explain_diff,
|
|
31
|
+
explain_sql_change,
|
|
32
|
+
)
|
|
33
|
+
from .reports import render_html_report, to_sarif
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"CTELineageDiff",
|
|
37
|
+
"LineageChange",
|
|
38
|
+
"MigrationAnalysisResult",
|
|
39
|
+
"MigrationOperation",
|
|
40
|
+
"OutputLineage",
|
|
41
|
+
"SchemaChange",
|
|
42
|
+
"SchemaColumn",
|
|
43
|
+
"SchemaContractDiff",
|
|
44
|
+
"SQLChange",
|
|
45
|
+
"SQLDirectoryDiffResult",
|
|
46
|
+
"SQLDiffError",
|
|
47
|
+
"SQLDiffResult",
|
|
48
|
+
"SQLFileDiff",
|
|
49
|
+
"SQLParseError",
|
|
50
|
+
"SQLParser",
|
|
51
|
+
"SqlglotParser",
|
|
52
|
+
"UnsupportedSQL",
|
|
53
|
+
"UnsupportedMigrationStatement",
|
|
54
|
+
"ValidationSuggestion",
|
|
55
|
+
"analyze_migration",
|
|
56
|
+
"compare_sql_directories",
|
|
57
|
+
"compare_sql_git",
|
|
58
|
+
"compare_output_schema",
|
|
59
|
+
"explain_diff",
|
|
60
|
+
"explain_sql_change",
|
|
61
|
+
"render_html_report",
|
|
62
|
+
"trace_cte_lineage",
|
|
63
|
+
"to_sarif",
|
|
64
|
+
]
|
|
65
|
+
__version__ = "0.1.5"
|