bills-sql-lineage 0.0.1__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.
@@ -0,0 +1,80 @@
1
+ Metadata-Version: 2.1
2
+ Name: bills-sql-lineage
3
+ Version: 0.0.1
4
+ Summary: Personal SQL lineage generator.
5
+ Author: Bilbottom
6
+ Requires-Python: >=3.11,<4.0
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Requires-Dist: arguably (>=1.2.5,<2.0.0)
11
+ Requires-Dist: sqlglot[rs] (>=23.0.5,<24.0.0)
12
+ Description-Content-Type: text/markdown
13
+
14
+ <div align="center">
15
+
16
+ [![Python](https://img.shields.io/badge/Python-3.11+-blue.svg)](https://www.python.org/downloads/release/python-3110/)
17
+ [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
18
+ [![tests](https://github.com/Bilbottom/sql-lineage/actions/workflows/tests.yaml/badge.svg)](https://github.com/Bilbottom/sql-lineage/actions/workflows/tests.yaml)
19
+ [![coverage](coverage.svg)](https://github.com/dbrgn/coverage-badge)
20
+ ![GitHub last commit](https://img.shields.io/github/last-commit/Bilbottom/sql-lineage)
21
+
22
+ [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
23
+ [![code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
24
+ [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
25
+ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/Bilbottom/sql-lineage/main.svg)](https://results.pre-commit.ci/latest/github/Bilbottom/sql-lineage/main)
26
+ [![Sourcery](https://img.shields.io/badge/Sourcery-enabled-brightgreen)](https://sourcery.ai)
27
+
28
+ </div>
29
+
30
+ ---
31
+
32
+ # SQL Lineage 🔀
33
+
34
+ Personal SQL lineage generator.
35
+
36
+ Built on the following awesome libraries:
37
+
38
+ - [SQLGlot](https://github.com/tobymao/sqlglot) for SQL parsing
39
+ - [arguably](https://github.com/treykeown/arguably) for the CLI
40
+
41
+ ## So... what is this? 🤔
42
+
43
+ > [!NOTE]
44
+ >
45
+ > This project is still in development and currently only parses the CTEs of an SQL query since that's all I need for now.
46
+
47
+ I write _a lot_ of SQL, and I often need to understand "lineage" in lots of different ways.
48
+
49
+ Things like [dbt](https://www.getdbt.com/) and [SQLMesh](https://sqlmesh.com/) are great for object lineage (tables, columns, etc.), but I often need to understand "lineage" like the CTE lineage in a query, among other things.
50
+
51
+ This project is just a personal tool to help me generate lineage diagrams (in [Mermaid](https://mermaid.js.org/) syntax) for SQL queries to fit that requirement.
52
+
53
+ Upcoming improvements will (hopefully) include:
54
+
55
+ - Column lineage to Mermaid
56
+ - Semantic edges (e.g. distinguish between `JOIN`, `WHERE`, `UNION`, etc.)
57
+ - Lineage for multiple files in a single diagram
58
+
59
+ ## Installation ⬇️
60
+
61
+ Grab a copy from PyPI like usual (note the `bills-` prefix):
62
+
63
+ ```
64
+ pip install bills-sql-lineage
65
+ ```
66
+
67
+ ## Usage 📖
68
+
69
+ Pass the path to a SQL file to the `lineage` command to generate the lineage as a [Mermaid](https://mermaid.js.org/) diagram:
70
+
71
+ ```
72
+ lineage path/to/file.sql
73
+ ```
74
+
75
+ This will write a Mermaid diagram to `path/to/file.mermaid`. You can control the target with the second argument:
76
+
77
+ ```
78
+ lineage path/to/file.sql path/to/output.mermaid
79
+ ```
80
+
@@ -0,0 +1,66 @@
1
+ <div align="center">
2
+
3
+ [![Python](https://img.shields.io/badge/Python-3.11+-blue.svg)](https://www.python.org/downloads/release/python-3110/)
4
+ [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
5
+ [![tests](https://github.com/Bilbottom/sql-lineage/actions/workflows/tests.yaml/badge.svg)](https://github.com/Bilbottom/sql-lineage/actions/workflows/tests.yaml)
6
+ [![coverage](coverage.svg)](https://github.com/dbrgn/coverage-badge)
7
+ ![GitHub last commit](https://img.shields.io/github/last-commit/Bilbottom/sql-lineage)
8
+
9
+ [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
10
+ [![code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
11
+ [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
12
+ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/Bilbottom/sql-lineage/main.svg)](https://results.pre-commit.ci/latest/github/Bilbottom/sql-lineage/main)
13
+ [![Sourcery](https://img.shields.io/badge/Sourcery-enabled-brightgreen)](https://sourcery.ai)
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ # SQL Lineage 🔀
20
+
21
+ Personal SQL lineage generator.
22
+
23
+ Built on the following awesome libraries:
24
+
25
+ - [SQLGlot](https://github.com/tobymao/sqlglot) for SQL parsing
26
+ - [arguably](https://github.com/treykeown/arguably) for the CLI
27
+
28
+ ## So... what is this? 🤔
29
+
30
+ > [!NOTE]
31
+ >
32
+ > This project is still in development and currently only parses the CTEs of an SQL query since that's all I need for now.
33
+
34
+ I write _a lot_ of SQL, and I often need to understand "lineage" in lots of different ways.
35
+
36
+ Things like [dbt](https://www.getdbt.com/) and [SQLMesh](https://sqlmesh.com/) are great for object lineage (tables, columns, etc.), but I often need to understand "lineage" like the CTE lineage in a query, among other things.
37
+
38
+ This project is just a personal tool to help me generate lineage diagrams (in [Mermaid](https://mermaid.js.org/) syntax) for SQL queries to fit that requirement.
39
+
40
+ Upcoming improvements will (hopefully) include:
41
+
42
+ - Column lineage to Mermaid
43
+ - Semantic edges (e.g. distinguish between `JOIN`, `WHERE`, `UNION`, etc.)
44
+ - Lineage for multiple files in a single diagram
45
+
46
+ ## Installation ⬇️
47
+
48
+ Grab a copy from PyPI like usual (note the `bills-` prefix):
49
+
50
+ ```
51
+ pip install bills-sql-lineage
52
+ ```
53
+
54
+ ## Usage 📖
55
+
56
+ Pass the path to a SQL file to the `lineage` command to generate the lineage as a [Mermaid](https://mermaid.js.org/) diagram:
57
+
58
+ ```
59
+ lineage path/to/file.sql
60
+ ```
61
+
62
+ This will write a Mermaid diagram to `path/to/file.mermaid`. You can control the target with the second argument:
63
+
64
+ ```
65
+ lineage path/to/file.sql path/to/output.mermaid
66
+ ```
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["poetry-core"]
3
+ build-backend = "poetry.core.masonry.api"
4
+
5
+
6
+ [tool.poetry]
7
+ name = "bills-sql-lineage" # For PyPI
8
+ version = "0.0.1"
9
+ description = "Personal SQL lineage generator."
10
+ authors = ["Bilbottom"]
11
+ readme = "README.md"
12
+ packages = [{include = "sql_lineage"}]
13
+
14
+ [tool.poetry.scripts]
15
+ lineage = 'sql_lineage.__main__:app'
16
+
17
+ [tool.poetry.dependencies]
18
+ python = "^3.11"
19
+ sqlglot = {extras = ["rs"], version = "^23.0.5"}
20
+ arguably = "^1.2.5"
21
+
22
+ [tool.poetry.group]
23
+ dev.optional = true
24
+ test.optional = true
25
+ ide.optional = true
26
+
27
+ [tool.poetry.group.dev.dependencies]
28
+ coverage-badge = "^1.1.0"
29
+ pre-commit = "^3.6.2"
30
+ pylint = "3.1.0"
31
+
32
+ [tool.poetry.group.test.dependencies]
33
+ pytest = "^8.0.2"
34
+ pytest-cov = "^4.1.0"
35
+
36
+ # Packages just for IDE integration
37
+ [tool.poetry.group.ide.dependencies]
38
+ black = "*"
39
+
40
+
41
+ [tool.pytest.ini_options]
42
+ addopts = "--cov=sql_lineage --cov-fail-under=80"
43
+ testpaths = ["tests"]
44
+
45
+
46
+ [tool.coverage.run]
47
+ omit = ["__main__.py"]
48
+
49
+
50
+ [tool.isort]
51
+ profile = "black"
52
+
53
+
54
+ [tool.pylint.format]
55
+ max-line-length = 120
56
+
57
+ [tool.pylint.MASTER]
58
+ ignore-paths = "^tests/.*$"
File without changes
@@ -0,0 +1,12 @@
1
+ """
2
+ CLI entry point for the ``sql_lineage`` package.
3
+ """
4
+
5
+ import arguably
6
+
7
+ # This import is necessary for the CLI to work
8
+ import sql_lineage.main # pylint: disable=unused-import
9
+
10
+ app = arguably.run # sourcery skip: avoid-global-variables
11
+ if __name__ == "__main__":
12
+ app()
@@ -0,0 +1,55 @@
1
+ """
2
+ Parsers for SQL queries and Mermaid graphs.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import sqlglot
8
+ from sqlglot import exp
9
+ from sqlglot.optimizer import qualify
10
+
11
+ Nodes = list[str]
12
+ Edges = list[tuple[str, str]] # Directed edges, source --> target.
13
+
14
+
15
+ def _parse_ctes(sql: str) -> [Nodes, Edges]:
16
+ """
17
+ Parse an SQL query into nodes and edges based on CTEs.
18
+ """
19
+ parsed = qualify.qualify(sqlglot.parse_one(sql))
20
+ nodes, edges = [], []
21
+
22
+ # Lazy and error-prone CTE sourcing
23
+ for cte in parsed.find_all(exp.CTE):
24
+ nodes.append(cte.alias)
25
+ edges.extend((tbl.name, cte.alias) for tbl in cte.find_all(exp.Table))
26
+
27
+ parsed.args["with"] = None # Dirty hack to remove the CTEs from the query
28
+ nodes.append("final")
29
+ edges.extend((tbl.name, "final") for tbl in parsed.find_all(exp.Table))
30
+
31
+ return nodes, edges
32
+
33
+
34
+ def _to_mermaid(nodes: Nodes, edges: Edges) -> str:
35
+ """
36
+ Convert nodes and edges into a Mermaid graph.
37
+ """
38
+ mermaid = "graph TD\n"
39
+
40
+ for node in nodes:
41
+ mermaid += f" {node}\n" # pylint: disable=consider-using-join
42
+
43
+ for edge in edges:
44
+ mermaid += f" {edge[0]} --> {edge[1]}\n"
45
+
46
+ return mermaid
47
+
48
+
49
+ def sql_to_mermaid(sql: str) -> str:
50
+ """
51
+ Convert an SQL query into a Mermaid graph.
52
+ """
53
+ nodes, edges = _parse_ctes(sql)
54
+
55
+ return _to_mermaid(nodes, edges)
@@ -0,0 +1,41 @@
1
+ """
2
+ An SQL lineage tool, built on the awesome SQLGlot library.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import pathlib
8
+
9
+ import arguably
10
+
11
+ from sql_lineage import lineage
12
+
13
+
14
+ def _to_path(file_path: str, validate: bool = False) -> pathlib.Path:
15
+ """
16
+ Convert a file path to a pathlib.Path object.
17
+ """
18
+ path = pathlib.Path(file_path)
19
+ if validate:
20
+ if not path.exists():
21
+ raise FileNotFoundError(f"File not found: {file_path}")
22
+ if not path.is_file():
23
+ raise ValueError(f"Not a file: {file_path}")
24
+
25
+ return path
26
+
27
+
28
+ @arguably.command
29
+ def main(file: str, target: str = None) -> None:
30
+ """
31
+ Parse an SQL file into a Mermaid graph by tracing CTEs.
32
+ """
33
+ file_path = _to_path(file, validate=True)
34
+ target_path = _to_path(target) if target else file_path.with_suffix(".mermaid")
35
+
36
+ sql = file_path.read_text(encoding="utf-8")
37
+ mermaid = lineage.sql_to_mermaid(sql)
38
+
39
+ target_path.write_text(mermaid, encoding="utf-8")
40
+ # Switch to logging at some point
41
+ print(f"Mermaid graph written to: {target_path.absolute()}")