sql-glider 0.1.2__py3-none-any.whl
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_glider-0.1.2.dist-info/METADATA +721 -0
- sql_glider-0.1.2.dist-info/RECORD +26 -0
- sql_glider-0.1.2.dist-info/WHEEL +4 -0
- sql_glider-0.1.2.dist-info/entry_points.txt +6 -0
- sql_glider-0.1.2.dist-info/licenses/LICENSE +201 -0
- sqlglider/__init__.py +3 -0
- sqlglider/_version.py +34 -0
- sqlglider/cli.py +1137 -0
- sqlglider/global_models.py +17 -0
- sqlglider/graph/__init__.py +42 -0
- sqlglider/graph/builder.py +310 -0
- sqlglider/graph/merge.py +136 -0
- sqlglider/graph/models.py +289 -0
- sqlglider/graph/query.py +287 -0
- sqlglider/graph/serialization.py +107 -0
- sqlglider/lineage/__init__.py +10 -0
- sqlglider/lineage/analyzer.py +1183 -0
- sqlglider/lineage/formatters.py +335 -0
- sqlglider/templating/__init__.py +51 -0
- sqlglider/templating/base.py +103 -0
- sqlglider/templating/jinja.py +163 -0
- sqlglider/templating/registry.py +124 -0
- sqlglider/templating/variables.py +295 -0
- sqlglider/utils/__init__.py +11 -0
- sqlglider/utils/config.py +130 -0
- sqlglider/utils/file_utils.py +38 -0
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sql-glider
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: SQL Utility Toolkit for better understanding, use, and governance of your queries in a native environment.
|
|
5
|
+
Project-URL: Homepage, https://github.com/rycowhi/sql-glider/
|
|
6
|
+
Project-URL: Repository, https://github.com/rycowhi/sql-glider/
|
|
7
|
+
Project-URL: Documentation, https://github.com/rycowhi/sql-glider/
|
|
8
|
+
Project-URL: Issues, https://github.com/rycowhi/sql-glider/issues
|
|
9
|
+
Author-email: Ryan Whitcomb <ryankwhitcomb@gmail.com>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: data-governance,data-lineage,lineage,sql,sqlglot
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: SQL
|
|
19
|
+
Classifier: Topic :: Database
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: jinja2>=3.0.0
|
|
24
|
+
Requires-Dist: pydantic>=2.0.0
|
|
25
|
+
Requires-Dist: rich>=13.0.0
|
|
26
|
+
Requires-Dist: rustworkx>=0.15.0
|
|
27
|
+
Requires-Dist: sqlglot[rs]>=25.0.0
|
|
28
|
+
Requires-Dist: typer>=0.9.0
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# SQL Glider
|
|
32
|
+
|
|
33
|
+
SQL Utility Toolkit for better understanding, use, and governance of your queries in a native environment.
|
|
34
|
+
|
|
35
|
+
## Overview
|
|
36
|
+
|
|
37
|
+
SQL Glider provides powerful column-level and table-level lineage analysis for SQL queries using SQLGlot. It operates on standalone SQL files without requiring a full project setup, making it perfect for ad-hoc analysis, data governance, and understanding query dependencies.
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- **Forward Lineage:** Trace output columns back to their source tables and columns
|
|
42
|
+
- **Reverse Lineage:** Impact analysis - find which output columns are affected by a source column
|
|
43
|
+
- **Table Extraction:** List all tables in SQL files with usage type (INPUT/OUTPUT) and object type (TABLE/VIEW/CTE)
|
|
44
|
+
- **Multi-level Tracing:** Automatically handles CTEs, subqueries, and complex expressions
|
|
45
|
+
- **Graph-Based Lineage:** Build and query lineage graphs across thousands of SQL files
|
|
46
|
+
- **Multiple Output Formats:** Text (human-readable), JSON (machine-readable), CSV (spreadsheet-ready)
|
|
47
|
+
- **Dialect Support:** Works with Spark, PostgreSQL, Snowflake, BigQuery, MySQL, and many more SQL dialects
|
|
48
|
+
- **File Export:** Save lineage results to files for documentation or further processing
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
SQL Glider is available on PyPI and can be installed with pip or uv. Python 3.11+ is required.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Install with pip
|
|
56
|
+
pip install sql-glider
|
|
57
|
+
|
|
58
|
+
# Or install with uv
|
|
59
|
+
uv pip install sql-glider
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
After installation, the `sqlglider` command is available:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
sqlglider lineage query.sql
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Development Setup
|
|
69
|
+
|
|
70
|
+
If you want to contribute or run from source:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Clone the repository
|
|
74
|
+
git clone https://github.com/ryanholmdahl/sql-glider.git
|
|
75
|
+
cd sql-glider
|
|
76
|
+
|
|
77
|
+
# Install dependencies with uv
|
|
78
|
+
uv sync
|
|
79
|
+
|
|
80
|
+
# Run from source
|
|
81
|
+
uv run sqlglider lineage <sql_file>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
### Forward Lineage (Source Tracing)
|
|
87
|
+
|
|
88
|
+
Find out where your output columns come from:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Analyze all output columns
|
|
92
|
+
uv run sqlglider lineage query.sql
|
|
93
|
+
|
|
94
|
+
# Analyze a specific output column
|
|
95
|
+
uv run sqlglider lineage query.sql --column customer_name
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Example Output:**
|
|
99
|
+
```
|
|
100
|
+
Query 0: SELECT customer_name, o.order_total FROM customers c JOIN orders o ...
|
|
101
|
+
+-----------------------------------------------------------------------------+
|
|
102
|
+
| Output Column | Source Column |
|
|
103
|
+
|-----------------+------------------------------------------------------------|
|
|
104
|
+
| customer_name | c.customer_name |
|
|
105
|
+
+-----------------------------------------------------------------------------+
|
|
106
|
+
Total: 1 row(s)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This shows that the output column `customer_name` in Query 0 comes from `c.customer_name` (the `customer_name` column in table `c`).
|
|
110
|
+
|
|
111
|
+
### Reverse Lineage (Impact Analysis)
|
|
112
|
+
|
|
113
|
+
Find out which output columns are affected by a source column:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Find outputs affected by a source column
|
|
117
|
+
uv run sqlglider lineage query.sql --source-column orders.customer_id
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Example Output:**
|
|
121
|
+
```
|
|
122
|
+
Query 0: SELECT customer_id, segment FROM ...
|
|
123
|
+
+---------------------------------------------------------+
|
|
124
|
+
| Output Column | Source Column |
|
|
125
|
+
|--------------------+------------------------------------|
|
|
126
|
+
| orders.customer_id | orders.customer_id |
|
|
127
|
+
+---------------------------------------------------------+
|
|
128
|
+
Total: 1 row(s)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
This shows that if `orders.customer_id` changes, it will impact the output column `customer_id` in Query 0.
|
|
132
|
+
|
|
133
|
+
## Usage Examples
|
|
134
|
+
|
|
135
|
+
### Basic Column Lineage
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Forward lineage for all columns
|
|
139
|
+
uv run sqlglider lineage query.sql
|
|
140
|
+
|
|
141
|
+
# Forward lineage for specific column
|
|
142
|
+
uv run sqlglider lineage query.sql --column order_total
|
|
143
|
+
|
|
144
|
+
# Reverse lineage (impact analysis)
|
|
145
|
+
uv run sqlglider lineage query.sql --source-column orders.customer_id
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Different Output Formats
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# JSON output
|
|
152
|
+
uv run sqlglider lineage query.sql --output-format json
|
|
153
|
+
|
|
154
|
+
# CSV output
|
|
155
|
+
uv run sqlglider lineage query.sql --output-format csv
|
|
156
|
+
|
|
157
|
+
# Export to file
|
|
158
|
+
uv run sqlglider lineage query.sql --output-format json --output-file lineage.json
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Table-Level Lineage
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# Show which tables are used
|
|
165
|
+
uv run sqlglider lineage query.sql --level table
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Table Extraction
|
|
169
|
+
|
|
170
|
+
List all tables involved in SQL files with usage and type information:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# List all tables in a SQL file
|
|
174
|
+
uv run sqlglider tables query.sql
|
|
175
|
+
|
|
176
|
+
# JSON output with detailed table info
|
|
177
|
+
uv run sqlglider tables query.sql --output-format json
|
|
178
|
+
|
|
179
|
+
# Export to CSV
|
|
180
|
+
uv run sqlglider tables query.sql --output-format csv --output-file tables.csv
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Example Output (JSON):**
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"queries": [{
|
|
187
|
+
"query_index": 0,
|
|
188
|
+
"tables": [
|
|
189
|
+
{"name": "customers", "usage": "INPUT", "object_type": "UNKNOWN"},
|
|
190
|
+
{"name": "orders", "usage": "INPUT", "object_type": "UNKNOWN"}
|
|
191
|
+
]
|
|
192
|
+
}]
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Table Usage Types:**
|
|
197
|
+
- `INPUT`: Table is read from (SELECT, JOIN, subqueries)
|
|
198
|
+
- `OUTPUT`: Table is written to (INSERT, CREATE TABLE/VIEW, UPDATE)
|
|
199
|
+
- `BOTH`: Table is both read from and written to
|
|
200
|
+
|
|
201
|
+
**Object Types:**
|
|
202
|
+
- `TABLE`: CREATE TABLE or DROP TABLE statement
|
|
203
|
+
- `VIEW`: CREATE VIEW or DROP VIEW statement
|
|
204
|
+
- `CTE`: Common Table Expression (WITH clause)
|
|
205
|
+
- `UNKNOWN`: Cannot determine type from SQL alone
|
|
206
|
+
|
|
207
|
+
### Different SQL Dialects
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# PostgreSQL
|
|
211
|
+
uv run sqlglider lineage query.sql --dialect postgres
|
|
212
|
+
|
|
213
|
+
# Snowflake
|
|
214
|
+
uv run sqlglider lineage query.sql --dialect snowflake
|
|
215
|
+
|
|
216
|
+
# BigQuery
|
|
217
|
+
uv run sqlglider lineage query.sql --dialect bigquery
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Multi-Query Files
|
|
221
|
+
|
|
222
|
+
SQL Glider automatically detects and analyzes multiple SQL statements in a single file:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# Analyze all queries in a file
|
|
226
|
+
uv run sqlglider lineage multi_query.sql
|
|
227
|
+
|
|
228
|
+
# Filter to only queries that reference a specific table
|
|
229
|
+
uv run sqlglider lineage multi_query.sql --table customers
|
|
230
|
+
|
|
231
|
+
# Analyze specific column across all queries
|
|
232
|
+
uv run sqlglider lineage multi_query.sql --column customer_id
|
|
233
|
+
|
|
234
|
+
# Reverse lineage across all queries (impact analysis)
|
|
235
|
+
uv run sqlglider lineage multi_query.sql --source-column orders.customer_id
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Example multi-query file:**
|
|
239
|
+
```sql
|
|
240
|
+
-- multi_query.sql
|
|
241
|
+
SELECT customer_id, customer_name FROM customers;
|
|
242
|
+
|
|
243
|
+
SELECT order_id, customer_id, order_total FROM orders;
|
|
244
|
+
|
|
245
|
+
INSERT INTO customer_orders
|
|
246
|
+
SELECT c.customer_id, c.customer_name, o.order_id
|
|
247
|
+
FROM customers c
|
|
248
|
+
JOIN orders o ON c.customer_id = o.customer_id;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Output includes query index for each statement:**
|
|
252
|
+
```
|
|
253
|
+
Query 0: SELECT customer_id, customer_name FROM customers
|
|
254
|
+
+---------------------------------------------------+
|
|
255
|
+
| Output Column | Source Column |
|
|
256
|
+
|-------------------------+-------------------------|
|
|
257
|
+
| customers.customer_id | customers.customer_id |
|
|
258
|
+
| customers.customer_name | customers.customer_name |
|
|
259
|
+
+---------------------------------------------------+
|
|
260
|
+
Total: 2 row(s)
|
|
261
|
+
|
|
262
|
+
Query 1: SELECT order_id, customer_id, order_total FROM orders
|
|
263
|
+
+---------------------------------------------+
|
|
264
|
+
| Output Column | Source Column |
|
|
265
|
+
|--------------------+------------------------|
|
|
266
|
+
| orders.customer_id | orders.customer_id |
|
|
267
|
+
| orders.order_id | orders.order_id |
|
|
268
|
+
| orders.order_total | orders.order_total |
|
|
269
|
+
+---------------------------------------------+
|
|
270
|
+
Total: 3 row(s)
|
|
271
|
+
|
|
272
|
+
Query 2: INSERT INTO customer_orders ...
|
|
273
|
+
+---------------------------------------------+
|
|
274
|
+
| Output Column | Source Column |
|
|
275
|
+
|--------------------+------------------------|
|
|
276
|
+
...
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Graph-Based Lineage (Cross-File Analysis)
|
|
280
|
+
|
|
281
|
+
For analyzing lineage across multiple SQL files, SQL Glider provides graph commands:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Build a lineage graph from a single file
|
|
285
|
+
uv run sqlglider graph build query.sql -o graph.json
|
|
286
|
+
|
|
287
|
+
# Build from multiple files
|
|
288
|
+
uv run sqlglider graph build query1.sql query2.sql query3.sql -o graph.json
|
|
289
|
+
|
|
290
|
+
# Build from a directory (recursively finds all .sql files)
|
|
291
|
+
uv run sqlglider graph build ./queries/ -r -o graph.json
|
|
292
|
+
|
|
293
|
+
# Build from a manifest CSV file
|
|
294
|
+
uv run sqlglider graph build --manifest manifest.csv -o graph.json
|
|
295
|
+
|
|
296
|
+
# Merge multiple graphs into one
|
|
297
|
+
uv run sqlglider graph merge graph1.json graph2.json -o merged.json
|
|
298
|
+
|
|
299
|
+
# Query upstream dependencies (find all sources for a column)
|
|
300
|
+
uv run sqlglider graph query graph.json --upstream orders.customer_id
|
|
301
|
+
|
|
302
|
+
# Query downstream dependencies (find all columns affected by a source)
|
|
303
|
+
uv run sqlglider graph query graph.json --downstream customers.id
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Example Upstream Query Output:**
|
|
307
|
+
```
|
|
308
|
+
Sources for 'order_totals.total'
|
|
309
|
+
+--------------------------------------------------------------------------------------------+
|
|
310
|
+
| Column | Table | Hops | Root | Leaf | Paths | File |
|
|
311
|
+
|--------+--------+------+------+------+------------------------------------+----------------|
|
|
312
|
+
| amount | orders | 1 | Y | N | orders.amount -> order_totals.total| test_graph.sql |
|
|
313
|
+
+--------------------------------------------------------------------------------------------+
|
|
314
|
+
|
|
315
|
+
Total: 1 column(s)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Example Downstream Query Output:**
|
|
319
|
+
```
|
|
320
|
+
Affected Columns for 'orders.amount'
|
|
321
|
+
+--------------------------------------------------------------------------------------------+
|
|
322
|
+
| Column | Table | Hops | Root | Leaf | Paths | File |
|
|
323
|
+
|--------+--------------+------+------+------+------------------------------------+----------------|
|
|
324
|
+
| total | order_totals | 1 | N | Y | orders.amount -> order_totals.total| test_graph.sql |
|
|
325
|
+
+--------------------------------------------------------------------------------------------+
|
|
326
|
+
|
|
327
|
+
Total: 1 column(s)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Output Fields:**
|
|
331
|
+
- **Root**: `Y` if the column has no upstream dependencies (source column)
|
|
332
|
+
- **Leaf**: `Y` if the column has no downstream dependencies (final output)
|
|
333
|
+
- **Paths**: All paths from the dependency to the queried column
|
|
334
|
+
|
|
335
|
+
**Manifest File Format:**
|
|
336
|
+
```csv
|
|
337
|
+
file_path,dialect
|
|
338
|
+
queries/orders.sql,spark
|
|
339
|
+
queries/customers.sql,postgres
|
|
340
|
+
queries/legacy.sql,
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
The graph feature is designed for scale - it can handle thousands of SQL files and provides efficient upstream/downstream queries using rustworkx.
|
|
344
|
+
|
|
345
|
+
## Use Cases
|
|
346
|
+
|
|
347
|
+
### Data Governance
|
|
348
|
+
|
|
349
|
+
**Impact Assessment:**
|
|
350
|
+
```bash
|
|
351
|
+
# Before modifying a source column, check its impact
|
|
352
|
+
uv run sqlglider lineage analytics_dashboard.sql --source-column orders.revenue
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
This helps you understand which downstream outputs will be affected by schema changes.
|
|
356
|
+
|
|
357
|
+
### Query Understanding
|
|
358
|
+
|
|
359
|
+
**Source Tracing:**
|
|
360
|
+
```bash
|
|
361
|
+
# Understand where a metric comes from
|
|
362
|
+
uv run sqlglider lineage metrics.sql --column total_revenue
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Quickly trace complex calculations back to their source tables.
|
|
366
|
+
|
|
367
|
+
### Documentation
|
|
368
|
+
|
|
369
|
+
**Export Lineage:**
|
|
370
|
+
```bash
|
|
371
|
+
# Generate documentation for your queries
|
|
372
|
+
uv run sqlglider lineage query.sql --output-format csv --output-file docs/lineage.csv
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Create machine-readable lineage documentation for data catalogs.
|
|
376
|
+
|
|
377
|
+
### Literal Value Handling
|
|
378
|
+
|
|
379
|
+
When analyzing UNION queries, SQL Glider identifies literal values (constants) as sources and displays them clearly:
|
|
380
|
+
|
|
381
|
+
```sql
|
|
382
|
+
-- query.sql
|
|
383
|
+
SELECT customer_id, last_order_date FROM active_customers
|
|
384
|
+
UNION ALL
|
|
385
|
+
SELECT customer_id, NULL AS last_order_date FROM prospects
|
|
386
|
+
UNION ALL
|
|
387
|
+
SELECT customer_id, 'unknown' AS status FROM legacy_data
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
uv run sqlglider lineage query.sql
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**Example Output:**
|
|
395
|
+
```
|
|
396
|
+
Query 0: SELECT customer_id, last_order_date FROM active_customers ...
|
|
397
|
+
+---------------------------------------------------------------------+
|
|
398
|
+
| Output Column | Source Column |
|
|
399
|
+
|----------------------------------+----------------------------------|
|
|
400
|
+
| active_customers.customer_id | active_customers.customer_id |
|
|
401
|
+
| | prospects.customer_id |
|
|
402
|
+
| active_customers.last_order_date | <literal: NULL> |
|
|
403
|
+
| | active_customers.last_order_date |
|
|
404
|
+
+---------------------------------------------------------------------+
|
|
405
|
+
Total: 4 row(s)
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Literal values are displayed as `<literal: VALUE>` to clearly distinguish them from actual column sources:
|
|
409
|
+
- `<literal: NULL>` - NULL values
|
|
410
|
+
- `<literal: 0>` - Numeric literals
|
|
411
|
+
- `<literal: 'string'>` - String literals
|
|
412
|
+
- `<literal: CURRENT_TIMESTAMP()>` - Function calls
|
|
413
|
+
|
|
414
|
+
This helps identify which branches of a UNION contribute actual data lineage versus hardcoded values.
|
|
415
|
+
|
|
416
|
+
### Multi-Level Analysis
|
|
417
|
+
|
|
418
|
+
SQL Glider automatically traces through CTEs and subqueries:
|
|
419
|
+
|
|
420
|
+
```sql
|
|
421
|
+
-- query.sql
|
|
422
|
+
WITH order_totals AS (
|
|
423
|
+
SELECT customer_id, SUM(order_amount) as total_amount
|
|
424
|
+
FROM orders
|
|
425
|
+
GROUP BY customer_id
|
|
426
|
+
),
|
|
427
|
+
customer_segments AS (
|
|
428
|
+
SELECT
|
|
429
|
+
ot.customer_id,
|
|
430
|
+
c.customer_name,
|
|
431
|
+
CASE
|
|
432
|
+
WHEN ot.total_amount > 10000 THEN 'Premium'
|
|
433
|
+
ELSE 'Standard'
|
|
434
|
+
END as segment
|
|
435
|
+
FROM order_totals ot
|
|
436
|
+
JOIN customers c ON ot.customer_id = c.customer_id
|
|
437
|
+
)
|
|
438
|
+
SELECT customer_name, segment, total_amount
|
|
439
|
+
FROM customer_segments
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
# Trace segment back to its ultimate sources
|
|
444
|
+
uv run sqlglider lineage query.sql --column segment
|
|
445
|
+
# Output: orders.order_amount (through the CASE statement and SUM)
|
|
446
|
+
|
|
447
|
+
# Find what's affected by order_amount
|
|
448
|
+
uv run sqlglider lineage query.sql --source-column orders.order_amount
|
|
449
|
+
# Output: segment, total_amount
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## CLI Reference
|
|
453
|
+
|
|
454
|
+
```
|
|
455
|
+
sqlglider lineage <sql_file> [OPTIONS]
|
|
456
|
+
|
|
457
|
+
Arguments:
|
|
458
|
+
sql_file Path to SQL file to analyze [required]
|
|
459
|
+
|
|
460
|
+
Options:
|
|
461
|
+
--level, -l Analysis level: 'column' or 'table' [default: column]
|
|
462
|
+
--dialect, -d SQL dialect (spark, postgres, snowflake, etc.) [default: spark]
|
|
463
|
+
--column, -c Specific output column for forward lineage [optional]
|
|
464
|
+
--source-column, -s Source column for reverse lineage (impact analysis) [optional]
|
|
465
|
+
--table, -t Filter to only queries that reference this table (multi-query files) [optional]
|
|
466
|
+
--output-format, -f Output format: 'text', 'json', or 'csv' [default: text]
|
|
467
|
+
--output-file, -o Write output to file instead of stdout [optional]
|
|
468
|
+
--help Show help message and exit
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**Notes:**
|
|
472
|
+
- `--column` and `--source-column` are mutually exclusive. Use one or the other.
|
|
473
|
+
- `--table` filter is useful for multi-query files to analyze only queries that reference a specific table.
|
|
474
|
+
|
|
475
|
+
### Tables Command
|
|
476
|
+
|
|
477
|
+
```
|
|
478
|
+
sqlglider tables <sql_file> [OPTIONS]
|
|
479
|
+
|
|
480
|
+
Arguments:
|
|
481
|
+
sql_file Path to SQL file to analyze [required]
|
|
482
|
+
|
|
483
|
+
Options:
|
|
484
|
+
--dialect, -d SQL dialect (spark, postgres, snowflake, etc.) [default: spark]
|
|
485
|
+
--table Filter to only queries that reference this table [optional]
|
|
486
|
+
--output-format, -f Output format: 'text', 'json', or 'csv' [default: text]
|
|
487
|
+
--output-file, -o Write output to file instead of stdout [optional]
|
|
488
|
+
--templater, -t Templater for SQL preprocessing (e.g., 'jinja', 'none') [optional]
|
|
489
|
+
--var, -v Template variable in key=value format (repeatable) [optional]
|
|
490
|
+
--vars-file Path to variables file (JSON or YAML) [optional]
|
|
491
|
+
--help Show help message and exit
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Graph Commands
|
|
495
|
+
|
|
496
|
+
```
|
|
497
|
+
sqlglider graph build <paths> [OPTIONS]
|
|
498
|
+
|
|
499
|
+
Arguments:
|
|
500
|
+
paths SQL file(s) or directory to process [optional]
|
|
501
|
+
|
|
502
|
+
Options:
|
|
503
|
+
--output, -o Output JSON file path [required]
|
|
504
|
+
--manifest, -m Path to manifest CSV file [optional]
|
|
505
|
+
--recursive, -r Recursively search directories [default: True]
|
|
506
|
+
--glob, -g Glob pattern for SQL files [default: *.sql]
|
|
507
|
+
--dialect, -d SQL dialect [default: spark]
|
|
508
|
+
--node-format, -n Node format: 'qualified' or 'structured' [default: qualified]
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
```
|
|
512
|
+
sqlglider graph merge <inputs> [OPTIONS]
|
|
513
|
+
|
|
514
|
+
Arguments:
|
|
515
|
+
inputs JSON graph files to merge [optional]
|
|
516
|
+
|
|
517
|
+
Options:
|
|
518
|
+
--output, -o Output file path [required]
|
|
519
|
+
--glob, -g Glob pattern for graph files [optional]
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
sqlglider graph query <graph_file> [OPTIONS]
|
|
524
|
+
|
|
525
|
+
Arguments:
|
|
526
|
+
graph_file Path to graph JSON file [required]
|
|
527
|
+
|
|
528
|
+
Options:
|
|
529
|
+
--upstream, -u Find source columns for this column [optional]
|
|
530
|
+
--downstream, -d Find affected columns for this source [optional]
|
|
531
|
+
--output-format, -f Output format: 'text', 'json', or 'csv' [default: text]
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
**Notes:**
|
|
535
|
+
- `--upstream` and `--downstream` are mutually exclusive. Use one or the other.
|
|
536
|
+
- Graph queries are case-insensitive for column matching.
|
|
537
|
+
|
|
538
|
+
## Output Formats
|
|
539
|
+
|
|
540
|
+
### Text Format (Default)
|
|
541
|
+
|
|
542
|
+
Human-readable Rich table format showing query index and preview:
|
|
543
|
+
|
|
544
|
+
```
|
|
545
|
+
Query 0: SELECT customer_name FROM customers c ...
|
|
546
|
+
+---------------------------------------------------+
|
|
547
|
+
| Output Column | Source Column |
|
|
548
|
+
|-----------------+---------------------------------|
|
|
549
|
+
| customer_name | c.customer_name |
|
|
550
|
+
+---------------------------------------------------+
|
|
551
|
+
Total: 1 row(s)
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### JSON Format
|
|
555
|
+
|
|
556
|
+
Machine-readable structured format with query metadata:
|
|
557
|
+
|
|
558
|
+
```json
|
|
559
|
+
{
|
|
560
|
+
"queries": [
|
|
561
|
+
{
|
|
562
|
+
"query_index": 0,
|
|
563
|
+
"query_preview": "SELECT customer_name FROM customers c ...",
|
|
564
|
+
"level": "column",
|
|
565
|
+
"lineage": [
|
|
566
|
+
{
|
|
567
|
+
"output_name": "customer_name",
|
|
568
|
+
"source_name": "c.customer_name"
|
|
569
|
+
}
|
|
570
|
+
]
|
|
571
|
+
}
|
|
572
|
+
]
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### CSV Format
|
|
577
|
+
|
|
578
|
+
Spreadsheet-ready tabular format with query index:
|
|
579
|
+
|
|
580
|
+
```csv
|
|
581
|
+
query_index,output_column,source_column
|
|
582
|
+
0,customer_name,c.customer_name
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**Note:** Each source column gets its own row. If an output column has multiple sources, there will be multiple rows with the same `query_index` and `output_column`.
|
|
586
|
+
|
|
587
|
+
## Development
|
|
588
|
+
|
|
589
|
+
### Setup
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
# Install dependencies
|
|
593
|
+
uv sync
|
|
594
|
+
|
|
595
|
+
# Run linter
|
|
596
|
+
uv run ruff check
|
|
597
|
+
|
|
598
|
+
# Auto-fix issues
|
|
599
|
+
uv run ruff check --fix
|
|
600
|
+
|
|
601
|
+
# Format code
|
|
602
|
+
uv run ruff format
|
|
603
|
+
|
|
604
|
+
# Type checking
|
|
605
|
+
uv run basedpyright
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### Project Structure
|
|
609
|
+
|
|
610
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed technical documentation.
|
|
611
|
+
|
|
612
|
+
```
|
|
613
|
+
src/sqlglider/
|
|
614
|
+
├── cli.py # Typer CLI entry point
|
|
615
|
+
├── graph/
|
|
616
|
+
│ ├── builder.py # Build graphs from SQL files
|
|
617
|
+
│ ├── merge.py # Merge multiple graphs
|
|
618
|
+
│ ├── query.py # Query upstream/downstream lineage
|
|
619
|
+
│ └── models.py # Graph data models
|
|
620
|
+
├── lineage/
|
|
621
|
+
│ ├── analyzer.py # Core lineage analysis using SQLGlot
|
|
622
|
+
│ └── formatters.py # Output formatters (text, JSON, CSV)
|
|
623
|
+
└── utils/
|
|
624
|
+
└── file_utils.py # File I/O utilities
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
## Publishing
|
|
628
|
+
|
|
629
|
+
SQL Glider is configured for publishing to both TestPyPI and PyPI using `uv`.
|
|
630
|
+
|
|
631
|
+
### Versioning
|
|
632
|
+
|
|
633
|
+
SQL Glider uses Git tags for version management via [hatch-vcs](https://github.com/ofek/hatch-vcs). The version is automatically derived from Git:
|
|
634
|
+
|
|
635
|
+
- **Tagged commits:** Version matches the tag (e.g., `git tag v0.2.0` produces version `0.2.0`)
|
|
636
|
+
- **Untagged commits:** Version includes development info (e.g., `0.1.dev18+g7216a59`)
|
|
637
|
+
|
|
638
|
+
**Creating a new release:**
|
|
639
|
+
|
|
640
|
+
```bash
|
|
641
|
+
# Create and push a version tag
|
|
642
|
+
git tag v0.2.0
|
|
643
|
+
git push origin v0.2.0
|
|
644
|
+
|
|
645
|
+
# Build will now produce version 0.2.0
|
|
646
|
+
uv build
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
**Tag format:** Use `v` prefix (e.g., `v1.0.0`, `v0.2.1`). The `v` is stripped from the final version number.
|
|
650
|
+
|
|
651
|
+
### Building the Package
|
|
652
|
+
|
|
653
|
+
```bash
|
|
654
|
+
# Build the distribution files (wheel and sdist)
|
|
655
|
+
uv build
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
This creates distribution files in the `dist/` directory.
|
|
659
|
+
|
|
660
|
+
### Publishing to TestPyPI
|
|
661
|
+
|
|
662
|
+
Always test your release on TestPyPI first:
|
|
663
|
+
|
|
664
|
+
```bash
|
|
665
|
+
# Publish to TestPyPI
|
|
666
|
+
uv publish --index testpypi --token <YOUR_TESTPYPI_TOKEN>
|
|
667
|
+
|
|
668
|
+
# Test installation from TestPyPI
|
|
669
|
+
uv pip install --index-url https://test.pypi.org/simple/ sql-glider
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### Publishing to PyPI
|
|
673
|
+
|
|
674
|
+
Once verified on TestPyPI, publish to production:
|
|
675
|
+
|
|
676
|
+
```bash
|
|
677
|
+
# Publish to PyPI
|
|
678
|
+
uv publish --index pypi --token <YOUR_PYPI_TOKEN>
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Token Setup
|
|
682
|
+
|
|
683
|
+
You'll need API tokens from both registries:
|
|
684
|
+
|
|
685
|
+
1. **TestPyPI Token:** Create at https://test.pypi.org/manage/account/token/
|
|
686
|
+
2. **PyPI Token:** Create at https://pypi.org/manage/account/token/
|
|
687
|
+
|
|
688
|
+
**Option 1: Pass token directly (shown above)**
|
|
689
|
+
|
|
690
|
+
**Option 2: Environment variable**
|
|
691
|
+
```bash
|
|
692
|
+
export UV_PUBLISH_TOKEN=pypi-...
|
|
693
|
+
uv publish --index pypi
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
**Option 3: Store in `.env` file (not committed to git)**
|
|
697
|
+
```bash
|
|
698
|
+
# .env
|
|
699
|
+
UV_PUBLISH_TOKEN=pypi-...
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
**Security Note:** Never commit API tokens to version control. The `.gitignore` file should include `.env`.
|
|
703
|
+
|
|
704
|
+
## Dependencies
|
|
705
|
+
|
|
706
|
+
- **sqlglot[rs]:** SQL parser and lineage analysis library with Rust extensions
|
|
707
|
+
- **typer:** CLI framework with type hints
|
|
708
|
+
- **rich:** Terminal formatting and colored output
|
|
709
|
+
- **pydantic:** Data validation and serialization
|
|
710
|
+
- **rustworkx:** High-performance graph library for cross-file lineage analysis
|
|
711
|
+
|
|
712
|
+
## References
|
|
713
|
+
|
|
714
|
+
- [SQLGlot Documentation](https://sqlglot.com/)
|
|
715
|
+
- [UV Documentation](https://docs.astral.sh/uv/)
|
|
716
|
+
- [Typer Documentation](https://typer.tiangolo.com/)
|
|
717
|
+
- [Ruff Documentation](https://docs.astral.sh/ruff/configuration/)
|
|
718
|
+
|
|
719
|
+
## License
|
|
720
|
+
|
|
721
|
+
See LICENSE file for details.
|