sql2iceberg 0.1.0__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.
- sql2iceberg-0.1.0/.gitignore +31 -0
- sql2iceberg-0.1.0/CHANGELOG.md +12 -0
- sql2iceberg-0.1.0/CONTRIBUTING.md +20 -0
- sql2iceberg-0.1.0/LICENSE +21 -0
- sql2iceberg-0.1.0/PKG-INFO +190 -0
- sql2iceberg-0.1.0/README.md +132 -0
- sql2iceberg-0.1.0/docs/environments.md +65 -0
- sql2iceberg-0.1.0/docs/examples.md +191 -0
- sql2iceberg-0.1.0/docs/iceberg-setup.md +36 -0
- sql2iceberg-0.1.0/docs/known-issues.md +20 -0
- sql2iceberg-0.1.0/docs/packages.md +71 -0
- sql2iceberg-0.1.0/docs/roadmap.md +8 -0
- sql2iceberg-0.1.0/docs/snowflake-setup.sql +45 -0
- sql2iceberg-0.1.0/docs/troubleshooting.md +188 -0
- sql2iceberg-0.1.0/examples/app.py +4 -0
- sql2iceberg-0.1.0/examples/cases/dateadd_timestamp/case.json +8 -0
- sql2iceberg-0.1.0/examples/cases/dateadd_timestamp/expected.csv +4 -0
- sql2iceberg-0.1.0/examples/cases/dateadd_timestamp/orders.csv +4 -0
- sql2iceberg-0.1.0/examples/cases/qualify/case.json +8 -0
- sql2iceberg-0.1.0/examples/cases/qualify/expected.csv +3 -0
- sql2iceberg-0.1.0/examples/cases/qualify/orders.csv +4 -0
- sql2iceberg-0.1.0/examples/corpus/app.py +8 -0
- sql2iceberg-0.1.0/examples/corpus/ddl/schema.sql +5 -0
- sql2iceberg-0.1.0/examples/corpus/pipelines/01_basic.sql +10 -0
- sql2iceberg-0.1.0/examples/corpus/pipelines/02_dates.sql +8 -0
- sql2iceberg-0.1.0/examples/corpus/pipelines/03_windows.sql +5 -0
- sql2iceberg-0.1.0/examples/corpus/pipelines/04_semistructured.sql +8 -0
- sql2iceberg-0.1.0/examples/corpus/pipelines/05_strings_regex.sql +6 -0
- sql2iceberg-0.1.0/examples/corpus/pipelines/06_dml_pivot_generator.sql +7 -0
- sql2iceberg-0.1.0/examples/corpus/pipelines/07_curated_orders.sql +2 -0
- sql2iceberg-0.1.0/examples/sample.sql +5 -0
- sql2iceberg-0.1.0/pyproject.toml +70 -0
- sql2iceberg-0.1.0/src/sql2iceberg/__init__.py +15 -0
- sql2iceberg-0.1.0/src/sql2iceberg/cli.py +258 -0
- sql2iceberg-0.1.0/src/sql2iceberg/compare.py +68 -0
- sql2iceberg-0.1.0/src/sql2iceberg/connectors/__init__.py +22 -0
- sql2iceberg-0.1.0/src/sql2iceberg/connectors/base.py +126 -0
- sql2iceberg-0.1.0/src/sql2iceberg/connectors/others.py +133 -0
- sql2iceberg-0.1.0/src/sql2iceberg/connectors/snowflake.py +47 -0
- sql2iceberg-0.1.0/src/sql2iceberg/convert.py +170 -0
- sql2iceberg-0.1.0/src/sql2iceberg/detect.py +95 -0
- sql2iceberg-0.1.0/src/sql2iceberg/diagnostics.py +124 -0
- sql2iceberg-0.1.0/src/sql2iceberg/generate.py +112 -0
- sql2iceberg-0.1.0/src/sql2iceberg/lineage.py +58 -0
- sql2iceberg-0.1.0/src/sql2iceberg/platforms.py +63 -0
- sql2iceberg-0.1.0/src/sql2iceberg/publish/__init__.py +18 -0
- sql2iceberg-0.1.0/src/sql2iceberg/publish/aws.py +108 -0
- sql2iceberg-0.1.0/src/sql2iceberg/publish/base.py +120 -0
- sql2iceberg-0.1.0/src/sql2iceberg/publish/gcp.py +72 -0
- sql2iceberg-0.1.0/src/sql2iceberg/publish/metadata.py +63 -0
- sql2iceberg-0.1.0/src/sql2iceberg/publish/snowflake.py +143 -0
- sql2iceberg-0.1.0/src/sql2iceberg/pyspark_gen.py +251 -0
- sql2iceberg-0.1.0/src/sql2iceberg/pytest_plugin.py +74 -0
- sql2iceberg-0.1.0/src/sql2iceberg/report.py +49 -0
- sql2iceberg-0.1.0/src/sql2iceberg/tracing.py +134 -0
- sql2iceberg-0.1.0/src/sql2iceberg/validate.py +186 -0
- sql2iceberg-0.1.0/src/sql2iceberg/verify.py +138 -0
- sql2iceberg-0.1.0/tests/test_connectors.py +90 -0
- sql2iceberg-0.1.0/tests/test_convert.py +65 -0
- sql2iceberg-0.1.0/tests/test_detect.py +26 -0
- sql2iceberg-0.1.0/tests/test_features.py +69 -0
- sql2iceberg-0.1.0/tests/test_publish.py +172 -0
- sql2iceberg-0.1.0/tests/test_pyspark_gen.py +111 -0
- sql2iceberg-0.1.0/tests/test_spark.py +61 -0
- sql2iceberg-0.1.0/tests/test_trace_validate.py +103 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.egg-info/
|
|
3
|
+
dist/
|
|
4
|
+
build/
|
|
5
|
+
.venv/
|
|
6
|
+
.pytest_cache/
|
|
7
|
+
out/
|
|
8
|
+
.env
|
|
9
|
+
jars/
|
|
10
|
+
warehouse/
|
|
11
|
+
seed/
|
|
12
|
+
|
|
13
|
+
# personal / generated: kept local
|
|
14
|
+
CLAUDE.md
|
|
15
|
+
PUBLISHING-RUNBOOK.md
|
|
16
|
+
medium-article-draft.md
|
|
17
|
+
curated/
|
|
18
|
+
converted/
|
|
19
|
+
spark_scripts/
|
|
20
|
+
scripts/cleanup_live_test.md
|
|
21
|
+
catalog.db
|
|
22
|
+
*.jsonl
|
|
23
|
+
.venv/
|
|
24
|
+
.ruff_cache/
|
|
25
|
+
.env.*
|
|
26
|
+
!.env.example
|
|
27
|
+
scripts/curated_e2e.py
|
|
28
|
+
scripts/export_spark.py
|
|
29
|
+
scripts/pyspark_vs_sql.py
|
|
30
|
+
scripts/pyspark_to_s3.py
|
|
31
|
+
scripts/cost_compare.py
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (first release, alpha)
|
|
4
|
+
|
|
5
|
+
- Renamed from the working name `sf2iceberg` to `sql2iceberg`.
|
|
6
|
+
- Detect SQL platform and SQL in `.sql`, `.py`, `.ipynb`; dialect-aware conversion to Spark SQL with safety findings.
|
|
7
|
+
- Connectors: Snowflake (tested live); Postgres, MySQL, SQL Server, Oracle (experimental, mock-tested).
|
|
8
|
+
- PySpark job generator: JDBC ingest to Iceberg, curation SQL, hadoop/jdbc/glue/rest/hive catalogs, S3 and GCS warehouses.
|
|
9
|
+
- Publishers: Snowflake external Iceberg tables, AWS Glue/Athena, BigQuery BigLake. Plan first, `--apply` to change accounts.
|
|
10
|
+
- `validate`: counts, non-null counts and sums across Snowflake, Athena and BigQuery.
|
|
11
|
+
- `--trace` JSONL tracer with secret redaction; `doctor` and `issue` commands.
|
|
12
|
+
- Live-tested on Snowflake, AWS and GCP with Iceberg format-version 2 (see docs/examples.md).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
```bash
|
|
5
|
+
pip install -e ".[pytest,dev]"
|
|
6
|
+
ruff check src tests
|
|
7
|
+
pytest -q -m "not spark" # no cloud accounts needed
|
|
8
|
+
```
|
|
9
|
+
Spark tests: `SQL2ICE_ICEBERG=1 pytest -m spark` with the Iceberg jars in `./jars` (see docs/environments.md).
|
|
10
|
+
|
|
11
|
+
## Rules
|
|
12
|
+
- Never commit credentials, account IDs, bucket names, or real company SQL or data. Examples use made-up data.
|
|
13
|
+
- Untested platforms must stay labelled experimental in code and docs.
|
|
14
|
+
- Anything that changes a cloud account must be gated behind an explicit `--apply` / `confirm=True`.
|
|
15
|
+
|
|
16
|
+
## Reporting problems
|
|
17
|
+
Open an issue with `sql2iceberg issue --open` (pre-fills your environment: names and versions only).
|
|
18
|
+
|
|
19
|
+
## Security
|
|
20
|
+
Do not open a public issue for a vulnerability. Use GitHub's "Report a vulnerability" (private advisory) on the repository.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jeyaraman Ramalingam
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sql2iceberg
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Convert SQL from Snowflake and other databases to PySpark, persist it as Apache Iceberg tables, expose them to Snowflake, AWS and GCP, and validate the result.
|
|
5
|
+
Project-URL: Homepage, https://github.com/jey1987-cmd/sql2iceberg
|
|
6
|
+
Project-URL: Documentation, https://github.com/jey1987-cmd/sql2iceberg/tree/main/docs
|
|
7
|
+
Project-URL: Issues, https://github.com/jey1987-cmd/sql2iceberg/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/jey1987-cmd/sql2iceberg/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Jeyaraman Ramalingam
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: bigquery,data-engineering,glue,iceberg,migration,pyspark,snowflake,sql,sqlglot
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Framework :: Pytest
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Information Technology
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Database
|
|
24
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: sqlglot>=30
|
|
27
|
+
Provides-Extra: all
|
|
28
|
+
Requires-Dist: boto3>=1.34; extra == 'all'
|
|
29
|
+
Requires-Dist: google-cloud-bigquery>=3.20; extra == 'all'
|
|
30
|
+
Requires-Dist: pyspark>=3.5; extra == 'all'
|
|
31
|
+
Requires-Dist: snowflake-connector-python>=3.12; extra == 'all'
|
|
32
|
+
Provides-Extra: aws
|
|
33
|
+
Requires-Dist: boto3>=1.34; extra == 'aws'
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: build; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
38
|
+
Requires-Dist: twine; extra == 'dev'
|
|
39
|
+
Provides-Extra: gcp
|
|
40
|
+
Requires-Dist: google-cloud-bigquery>=3.20; extra == 'gcp'
|
|
41
|
+
Provides-Extra: mssql
|
|
42
|
+
Requires-Dist: pyodbc>=5; extra == 'mssql'
|
|
43
|
+
Provides-Extra: mysql
|
|
44
|
+
Requires-Dist: pymysql>=1.1; extra == 'mysql'
|
|
45
|
+
Provides-Extra: oracle
|
|
46
|
+
Requires-Dist: oracledb>=2; extra == 'oracle'
|
|
47
|
+
Provides-Extra: postgres
|
|
48
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == 'postgres'
|
|
49
|
+
Provides-Extra: pytest
|
|
50
|
+
Requires-Dist: chispa>=0.10; extra == 'pytest'
|
|
51
|
+
Requires-Dist: pyspark>=3.5; extra == 'pytest'
|
|
52
|
+
Requires-Dist: pytest>=8; extra == 'pytest'
|
|
53
|
+
Provides-Extra: snowflake
|
|
54
|
+
Requires-Dist: snowflake-connector-python>=3.12; extra == 'snowflake'
|
|
55
|
+
Provides-Extra: spark
|
|
56
|
+
Requires-Dist: pyspark>=3.5; extra == 'spark'
|
|
57
|
+
Description-Content-Type: text/markdown
|
|
58
|
+
|
|
59
|
+
# sql2iceberg
|
|
60
|
+
|
|
61
|
+
Take the SQL in your code base, convert it to **PySpark**, persist the result as **Apache Iceberg** tables, expose those
|
|
62
|
+
tables to **Snowflake, AWS (Glue/Athena) and GCP (BigQuery)**, and **validate** the migration.
|
|
63
|
+
|
|
64
|
+
**Status: alpha (0.1.0).** One source platform (Snowflake) and three target platforms were exercised against real
|
|
65
|
+
accounts; everything else is written but untested and labelled so. See [what was tested](docs/environments.md#what-was-tested)
|
|
66
|
+
and [what is not supported yet](#limitations).
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
your code ──detect──▶ SQL ──convert──▶ Spark SQL ──generate──▶ PySpark job ──run──▶ Iceberg on S3 / GCS
|
|
70
|
+
│
|
|
71
|
+
validate ◀──── counts, nulls, sums ────────────────────────┤
|
|
72
|
+
├─▶ Snowflake (external Iceberg table)
|
|
73
|
+
├─▶ AWS Glue / Athena
|
|
74
|
+
└─▶ BigQuery (BigLake)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Core capabilities
|
|
78
|
+
|
|
79
|
+
| # | Capability | Command / API | Status |
|
|
80
|
+
|---|---|---|---|
|
|
81
|
+
| 1 | **Detect** the SQL platform and find SQL in `.sql`, `.py`, `.ipynb` | `detect`, `detect_platform()` | Snowflake tested; other dialects parse via SQLGlot |
|
|
82
|
+
| 2 | **Connect** to the source (you set up authentication first) | `connectors.get_connector()` | Snowflake tested live; Postgres, MySQL, SQL Server, Oracle **experimental** (mock-tested only) |
|
|
83
|
+
| 3 | **Convert** to Spark SQL and flag what cannot be trusted | `convert`, `convert_statement()` | tested |
|
|
84
|
+
| 4 | **Generate a PySpark job**: JDBC read, write Iceberg, run the curation SQL | `pyspark`, `pyspark_pipeline()` | tested end to end (Snowflake to Iceberg on S3 and on GCS) |
|
|
85
|
+
| 5 | **Expose** Iceberg tables to other platforms | `publish`, `publish.*Publisher` | Snowflake, AWS Glue/Athena, BigQuery all tested live |
|
|
86
|
+
| 6 | **Validate** counts, non-null counts and column sums across platforms | `validate`, `validate.validate()` | tested live across four platforms |
|
|
87
|
+
| 7 | **Report**: converted PySpark, plan of changes, validation results (Markdown and JSON) | all commands | tested |
|
|
88
|
+
| 8 | **Trace** every step with timings, secrets redacted | `--trace trace.jsonl`, `tracing.span()` | tested |
|
|
89
|
+
| 9 | **Diagnose** your environment; open a pre-filled GitHub issue | `doctor`, `issue` | tested |
|
|
90
|
+
|
|
91
|
+
Safety defaults: `publish` prints a plan and changes nothing unless you pass `--apply`; credentials are only read from
|
|
92
|
+
environment variables or your cloud profile; generated scripts never contain secrets.
|
|
93
|
+
|
|
94
|
+
## Install
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install sql2iceberg # core: detect + convert (needs only sqlglot)
|
|
98
|
+
pip install "sql2iceberg[spark]" # run the generated PySpark jobs locally
|
|
99
|
+
pip install "sql2iceberg[snowflake,aws,gcp]" # connectors and publishers you need
|
|
100
|
+
pip install "sql2iceberg[all]" # spark + snowflake + aws + gcp
|
|
101
|
+
pip install "sql2iceberg[pytest]" # pytest plugin and fixtures
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Extras: `spark`, `snowflake`, `postgres`, `mysql`, `mssql`, `oracle`, `aws`, `gcp`, `pytest`, `all`, `dev`.
|
|
105
|
+
|
|
106
|
+
## Environment dependencies (summary)
|
|
107
|
+
|
|
108
|
+
| Needed for | Requirement |
|
|
109
|
+
|---|---|
|
|
110
|
+
| everything | Python 3.10 or newer |
|
|
111
|
+
| running generated Spark jobs | **Java 17** (Java 21 should work), `pyspark` 3.5 or 4.x. Do not set `SPARK_HOME` to another Spark. |
|
|
112
|
+
| Iceberg tables | Iceberg Spark runtime jar matching your Spark (we used `iceberg-spark-runtime-4.1_2.13-1.12.0`) |
|
|
113
|
+
| Iceberg on S3 / GCS | `iceberg-aws-bundle` / `iceberg-gcp-bundle` jars |
|
|
114
|
+
| reading the source over JDBC | the JDBC driver jar (we used `snowflake-jdbc-3.19.0`) |
|
|
115
|
+
| Snowflake | account, role that can create external volumes, catalog integrations and Iceberg tables; a warehouse |
|
|
116
|
+
| AWS | an AWS profile or environment credentials with S3, Glue and (to count) Athena permissions |
|
|
117
|
+
| GCP | `gcloud auth login`, a project **with billing enabled**, a BigQuery connection to the bucket |
|
|
118
|
+
| behind a TLS-inspecting proxy | a CA bundle for Python and a Java truststore: see [troubleshooting](docs/troubleshooting.md) |
|
|
119
|
+
|
|
120
|
+
Full details, versions we tested and the list of every package and jar: [docs/environments.md](docs/environments.md) and
|
|
121
|
+
[docs/packages.md](docs/packages.md). Run `sql2iceberg doctor` to check your machine.
|
|
122
|
+
|
|
123
|
+
## Quick start
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# 1. which platform is this code base for, and what SQL is in it?
|
|
127
|
+
sql2iceberg detect path/to/code
|
|
128
|
+
# 2. convert, and see what needs hand work
|
|
129
|
+
sql2iceberg convert path/to/code --out out
|
|
130
|
+
# 3. generate the PySpark job (reads the source over JDBC, writes Iceberg, runs the curation SQL)
|
|
131
|
+
sql2iceberg pyspark path/to/code --out out --platform snowflake \
|
|
132
|
+
--catalog-type jdbc --catalog-uri jdbc:sqlite:catalog.db \
|
|
133
|
+
--warehouse s3://my-bucket/warehouse --namespace live --iceberg-version 2
|
|
134
|
+
# 4. run it (set SNOWFLAKE_USER and SNOWFLAKE_PRIVATE_KEY_PATH or SNOWFLAKE_PASSWORD first)
|
|
135
|
+
spark-submit out/pipeline_pyspark.py
|
|
136
|
+
# 5. expose the tables: read the plan first, then --apply
|
|
137
|
+
sql2iceberg publish publish.json
|
|
138
|
+
sql2iceberg publish publish.json --apply
|
|
139
|
+
# 6. validate across platforms
|
|
140
|
+
sql2iceberg validate validate.json --out out
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Add `--trace trace.jsonl` to any command to record a timed trace of the run.
|
|
144
|
+
Worked examples from real runs (configs, plans, results): [docs/examples.md](docs/examples.md).
|
|
145
|
+
|
|
146
|
+
## Python API
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from sql2iceberg import detect_path, convert_statement
|
|
150
|
+
from sql2iceberg.pyspark_gen import MigrationConfig, pyspark_pipeline
|
|
151
|
+
from sql2iceberg.publish import SnowflakePublisher, SnowflakeTarget, tables_from_jdbc_catalog
|
|
152
|
+
from sql2iceberg.validate import validate, Target, ColumnSpec
|
|
153
|
+
|
|
154
|
+
stmts = detect_path("pipelines/", dialect="snowflake")
|
|
155
|
+
script, report = pyspark_pipeline(stmts, MigrationConfig(platform="snowflake", warehouse="s3://b/wh",
|
|
156
|
+
catalog_type="jdbc", catalog_uri="jdbc:sqlite:catalog.db"))
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Documentation
|
|
160
|
+
|
|
161
|
+
- [Environments and what was tested](docs/environments.md)
|
|
162
|
+
- [Every package, jar and service used in this build](docs/packages.md)
|
|
163
|
+
- [Examples from the live tests](docs/examples.md)
|
|
164
|
+
- [Troubleshooting frequent problems](docs/troubleshooting.md)
|
|
165
|
+
- [Known conversion differences between Snowflake and Spark](docs/known-issues.md)
|
|
166
|
+
- [Roadmap](docs/roadmap.md)
|
|
167
|
+
|
|
168
|
+
## Getting help and reporting problems
|
|
169
|
+
|
|
170
|
+
- Run `sql2iceberg doctor`, then `sql2iceberg issue --title "short summary" --open` to open a GitHub issue pre-filled with
|
|
171
|
+
your environment (names and versions only, never values or credentials).
|
|
172
|
+
- Attach a trace (`--trace trace.jsonl`) after reading it. Secrets are redacted but check anyway.
|
|
173
|
+
- Issues: <https://github.com/jey1987-cmd/sql2iceberg/issues>. Please read [troubleshooting](docs/troubleshooting.md) first.
|
|
174
|
+
- Security problems: do not open a public issue; see [CONTRIBUTING.md](CONTRIBUTING.md#security).
|
|
175
|
+
|
|
176
|
+
## Limitations
|
|
177
|
+
|
|
178
|
+
- Only **Snowflake as a source** and **Snowflake, AWS Glue/Athena, BigQuery as targets** were run against real systems.
|
|
179
|
+
Postgres, MySQL, SQL Server and Oracle connectors, REST/Hive catalogs, Spark 3.5, Azure and Databricks are untested.
|
|
180
|
+
- Tested with Iceberg **format-version 2**. Whether Snowflake and BigQuery read v3 tables is unverified here.
|
|
181
|
+
- BigQuery cannot read an S3 warehouse. The GCP path writes a second Iceberg copy to GCS.
|
|
182
|
+
- Snowflake object-store tables and BigQuery tables point at one metadata file (a snapshot): refresh after new writes.
|
|
183
|
+
- Validation compares counts, non-null counts and sums. It does not compare every row.
|
|
184
|
+
- Tested on Windows 10 with Python 3.13. Linux and macOS should work; CI covers Linux but live tests were not run there.
|
|
185
|
+
- Conversion uses [SQLGlot](https://github.com/tobymao/sqlglot); the converter flags constructs that behave differently in
|
|
186
|
+
Spark (see [known issues](docs/known-issues.md)) but a clean result still needs your own verification.
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT. Not affiliated with Snowflake, the Apache Software Foundation, Amazon or Google. "Iceberg" is a trademark of the ASF.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# sql2iceberg
|
|
2
|
+
|
|
3
|
+
Take the SQL in your code base, convert it to **PySpark**, persist the result as **Apache Iceberg** tables, expose those
|
|
4
|
+
tables to **Snowflake, AWS (Glue/Athena) and GCP (BigQuery)**, and **validate** the migration.
|
|
5
|
+
|
|
6
|
+
**Status: alpha (0.1.0).** One source platform (Snowflake) and three target platforms were exercised against real
|
|
7
|
+
accounts; everything else is written but untested and labelled so. See [what was tested](docs/environments.md#what-was-tested)
|
|
8
|
+
and [what is not supported yet](#limitations).
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
your code ──detect──▶ SQL ──convert──▶ Spark SQL ──generate──▶ PySpark job ──run──▶ Iceberg on S3 / GCS
|
|
12
|
+
│
|
|
13
|
+
validate ◀──── counts, nulls, sums ────────────────────────┤
|
|
14
|
+
├─▶ Snowflake (external Iceberg table)
|
|
15
|
+
├─▶ AWS Glue / Athena
|
|
16
|
+
└─▶ BigQuery (BigLake)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Core capabilities
|
|
20
|
+
|
|
21
|
+
| # | Capability | Command / API | Status |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| 1 | **Detect** the SQL platform and find SQL in `.sql`, `.py`, `.ipynb` | `detect`, `detect_platform()` | Snowflake tested; other dialects parse via SQLGlot |
|
|
24
|
+
| 2 | **Connect** to the source (you set up authentication first) | `connectors.get_connector()` | Snowflake tested live; Postgres, MySQL, SQL Server, Oracle **experimental** (mock-tested only) |
|
|
25
|
+
| 3 | **Convert** to Spark SQL and flag what cannot be trusted | `convert`, `convert_statement()` | tested |
|
|
26
|
+
| 4 | **Generate a PySpark job**: JDBC read, write Iceberg, run the curation SQL | `pyspark`, `pyspark_pipeline()` | tested end to end (Snowflake to Iceberg on S3 and on GCS) |
|
|
27
|
+
| 5 | **Expose** Iceberg tables to other platforms | `publish`, `publish.*Publisher` | Snowflake, AWS Glue/Athena, BigQuery all tested live |
|
|
28
|
+
| 6 | **Validate** counts, non-null counts and column sums across platforms | `validate`, `validate.validate()` | tested live across four platforms |
|
|
29
|
+
| 7 | **Report**: converted PySpark, plan of changes, validation results (Markdown and JSON) | all commands | tested |
|
|
30
|
+
| 8 | **Trace** every step with timings, secrets redacted | `--trace trace.jsonl`, `tracing.span()` | tested |
|
|
31
|
+
| 9 | **Diagnose** your environment; open a pre-filled GitHub issue | `doctor`, `issue` | tested |
|
|
32
|
+
|
|
33
|
+
Safety defaults: `publish` prints a plan and changes nothing unless you pass `--apply`; credentials are only read from
|
|
34
|
+
environment variables or your cloud profile; generated scripts never contain secrets.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install sql2iceberg # core: detect + convert (needs only sqlglot)
|
|
40
|
+
pip install "sql2iceberg[spark]" # run the generated PySpark jobs locally
|
|
41
|
+
pip install "sql2iceberg[snowflake,aws,gcp]" # connectors and publishers you need
|
|
42
|
+
pip install "sql2iceberg[all]" # spark + snowflake + aws + gcp
|
|
43
|
+
pip install "sql2iceberg[pytest]" # pytest plugin and fixtures
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Extras: `spark`, `snowflake`, `postgres`, `mysql`, `mssql`, `oracle`, `aws`, `gcp`, `pytest`, `all`, `dev`.
|
|
47
|
+
|
|
48
|
+
## Environment dependencies (summary)
|
|
49
|
+
|
|
50
|
+
| Needed for | Requirement |
|
|
51
|
+
|---|---|
|
|
52
|
+
| everything | Python 3.10 or newer |
|
|
53
|
+
| running generated Spark jobs | **Java 17** (Java 21 should work), `pyspark` 3.5 or 4.x. Do not set `SPARK_HOME` to another Spark. |
|
|
54
|
+
| Iceberg tables | Iceberg Spark runtime jar matching your Spark (we used `iceberg-spark-runtime-4.1_2.13-1.12.0`) |
|
|
55
|
+
| Iceberg on S3 / GCS | `iceberg-aws-bundle` / `iceberg-gcp-bundle` jars |
|
|
56
|
+
| reading the source over JDBC | the JDBC driver jar (we used `snowflake-jdbc-3.19.0`) |
|
|
57
|
+
| Snowflake | account, role that can create external volumes, catalog integrations and Iceberg tables; a warehouse |
|
|
58
|
+
| AWS | an AWS profile or environment credentials with S3, Glue and (to count) Athena permissions |
|
|
59
|
+
| GCP | `gcloud auth login`, a project **with billing enabled**, a BigQuery connection to the bucket |
|
|
60
|
+
| behind a TLS-inspecting proxy | a CA bundle for Python and a Java truststore: see [troubleshooting](docs/troubleshooting.md) |
|
|
61
|
+
|
|
62
|
+
Full details, versions we tested and the list of every package and jar: [docs/environments.md](docs/environments.md) and
|
|
63
|
+
[docs/packages.md](docs/packages.md). Run `sql2iceberg doctor` to check your machine.
|
|
64
|
+
|
|
65
|
+
## Quick start
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# 1. which platform is this code base for, and what SQL is in it?
|
|
69
|
+
sql2iceberg detect path/to/code
|
|
70
|
+
# 2. convert, and see what needs hand work
|
|
71
|
+
sql2iceberg convert path/to/code --out out
|
|
72
|
+
# 3. generate the PySpark job (reads the source over JDBC, writes Iceberg, runs the curation SQL)
|
|
73
|
+
sql2iceberg pyspark path/to/code --out out --platform snowflake \
|
|
74
|
+
--catalog-type jdbc --catalog-uri jdbc:sqlite:catalog.db \
|
|
75
|
+
--warehouse s3://my-bucket/warehouse --namespace live --iceberg-version 2
|
|
76
|
+
# 4. run it (set SNOWFLAKE_USER and SNOWFLAKE_PRIVATE_KEY_PATH or SNOWFLAKE_PASSWORD first)
|
|
77
|
+
spark-submit out/pipeline_pyspark.py
|
|
78
|
+
# 5. expose the tables: read the plan first, then --apply
|
|
79
|
+
sql2iceberg publish publish.json
|
|
80
|
+
sql2iceberg publish publish.json --apply
|
|
81
|
+
# 6. validate across platforms
|
|
82
|
+
sql2iceberg validate validate.json --out out
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Add `--trace trace.jsonl` to any command to record a timed trace of the run.
|
|
86
|
+
Worked examples from real runs (configs, plans, results): [docs/examples.md](docs/examples.md).
|
|
87
|
+
|
|
88
|
+
## Python API
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from sql2iceberg import detect_path, convert_statement
|
|
92
|
+
from sql2iceberg.pyspark_gen import MigrationConfig, pyspark_pipeline
|
|
93
|
+
from sql2iceberg.publish import SnowflakePublisher, SnowflakeTarget, tables_from_jdbc_catalog
|
|
94
|
+
from sql2iceberg.validate import validate, Target, ColumnSpec
|
|
95
|
+
|
|
96
|
+
stmts = detect_path("pipelines/", dialect="snowflake")
|
|
97
|
+
script, report = pyspark_pipeline(stmts, MigrationConfig(platform="snowflake", warehouse="s3://b/wh",
|
|
98
|
+
catalog_type="jdbc", catalog_uri="jdbc:sqlite:catalog.db"))
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Documentation
|
|
102
|
+
|
|
103
|
+
- [Environments and what was tested](docs/environments.md)
|
|
104
|
+
- [Every package, jar and service used in this build](docs/packages.md)
|
|
105
|
+
- [Examples from the live tests](docs/examples.md)
|
|
106
|
+
- [Troubleshooting frequent problems](docs/troubleshooting.md)
|
|
107
|
+
- [Known conversion differences between Snowflake and Spark](docs/known-issues.md)
|
|
108
|
+
- [Roadmap](docs/roadmap.md)
|
|
109
|
+
|
|
110
|
+
## Getting help and reporting problems
|
|
111
|
+
|
|
112
|
+
- Run `sql2iceberg doctor`, then `sql2iceberg issue --title "short summary" --open` to open a GitHub issue pre-filled with
|
|
113
|
+
your environment (names and versions only, never values or credentials).
|
|
114
|
+
- Attach a trace (`--trace trace.jsonl`) after reading it. Secrets are redacted but check anyway.
|
|
115
|
+
- Issues: <https://github.com/jey1987-cmd/sql2iceberg/issues>. Please read [troubleshooting](docs/troubleshooting.md) first.
|
|
116
|
+
- Security problems: do not open a public issue; see [CONTRIBUTING.md](CONTRIBUTING.md#security).
|
|
117
|
+
|
|
118
|
+
## Limitations
|
|
119
|
+
|
|
120
|
+
- Only **Snowflake as a source** and **Snowflake, AWS Glue/Athena, BigQuery as targets** were run against real systems.
|
|
121
|
+
Postgres, MySQL, SQL Server and Oracle connectors, REST/Hive catalogs, Spark 3.5, Azure and Databricks are untested.
|
|
122
|
+
- Tested with Iceberg **format-version 2**. Whether Snowflake and BigQuery read v3 tables is unverified here.
|
|
123
|
+
- BigQuery cannot read an S3 warehouse. The GCP path writes a second Iceberg copy to GCS.
|
|
124
|
+
- Snowflake object-store tables and BigQuery tables point at one metadata file (a snapshot): refresh after new writes.
|
|
125
|
+
- Validation compares counts, non-null counts and sums. It does not compare every row.
|
|
126
|
+
- Tested on Windows 10 with Python 3.13. Linux and macOS should work; CI covers Linux but live tests were not run there.
|
|
127
|
+
- Conversion uses [SQLGlot](https://github.com/tobymao/sqlglot); the converter flags constructs that behave differently in
|
|
128
|
+
Spark (see [known issues](docs/known-issues.md)) but a clean result still needs your own verification.
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT. Not affiliated with Snowflake, the Apache Software Foundation, Amazon or Google. "Iceberg" is a trademark of the ASF.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Environments
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
| Component | Required | Notes |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| Python | 3.10 or newer | declared in `pyproject.toml`; live-tested on 3.13 |
|
|
8
|
+
| Java | 17 (21 should work) for PySpark 4; 8, 11 or 17 for PySpark 3.5 | `JAVA_HOME` must point at it. `sql2iceberg doctor` warns if `JAVA_HOME` and `java` on `PATH` differ |
|
|
9
|
+
| PySpark | 3.5 or 4.x | live-tested on 4.1.4. Do not set `SPARK_HOME` to a different Spark |
|
|
10
|
+
| Iceberg Spark runtime jar | must match your Spark and Scala version | tested: `iceberg-spark-runtime-4.1_2.13-1.12.0.jar` |
|
|
11
|
+
| Iceberg cloud bundles | `iceberg-aws-bundle` for S3 and Glue; `iceberg-gcp-bundle` for GCS | tested: 1.12.0 both |
|
|
12
|
+
| JDBC driver for the source | one jar per source platform | tested: `snowflake-jdbc-3.19.0.jar`; `sqlite-jdbc-3.46.1.3.jar` for the local catalog and unit tests |
|
|
13
|
+
| Network | Maven Central (or local jars), your cloud endpoints | behind a proxy that re-signs TLS see [troubleshooting](troubleshooting.md#certificate_unknown--ssl-errors) |
|
|
14
|
+
|
|
15
|
+
The jars can come from Maven (`--packages`, `spark.jars.packages`) or from local files (`MigrationConfig.jars`).
|
|
16
|
+
On locked-down networks download them once and use local files.
|
|
17
|
+
|
|
18
|
+
## Authentication prerequisites (you do these before running)
|
|
19
|
+
|
|
20
|
+
| Platform | What to set up | Variables / tools read |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| Snowflake | a user with key-pair (recommended), password or SSO; a role and warehouse | `SNOWFLAKE_ACCOUNT`, `SNOWFLAKE_USER`, `SNOWFLAKE_PRIVATE_KEY_PATH` or `SNOWFLAKE_PASSWORD` or `SNOWFLAKE_AUTHENTICATOR`, `SNOWFLAKE_ROLE`, `SNOWFLAKE_WAREHOUSE` |
|
|
23
|
+
| Snowflake to S3 | an external volume with a storage integration and IAM role that Snowflake can assume | created once in your account; the library can create it if you pass `aws_role_arn` |
|
|
24
|
+
| AWS | profile or environment credentials | `AWS_PROFILE`, `AWS_REGION`, optionally `AWS_CA_BUNDLE` |
|
|
25
|
+
| GCP | `gcloud auth login`; a project with **billing enabled**; BigQuery Connection API enabled | `gcloud` credentials; a short-lived token is passed to Spark through `GCS_OAUTH_TOKEN` |
|
|
26
|
+
| Postgres, MySQL, SQL Server, Oracle (experimental) | native client environment variables | `PGHOST` ..., `MYSQL_HOST` ..., `MSSQL_SERVER` ..., `ORACLE_DSN` ... |
|
|
27
|
+
|
|
28
|
+
## Supported environments
|
|
29
|
+
|
|
30
|
+
| Area | Verified live | Written, mock-tested only | Not supported yet |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| Source SQL platform | Snowflake | Postgres, MySQL, SQL Server (T-SQL), Oracle | others |
|
|
33
|
+
| Spark | PySpark 4.1.4 (local mode) | PySpark 3.5 | Databricks, EMR, Dataproc submit helpers |
|
|
34
|
+
| Iceberg catalog | JDBC catalog (SQLite file), Hadoop (local), Glue (for `register_table`) | REST, Hive | Nessie, Polaris |
|
|
35
|
+
| Object storage | S3 (us-east-2), GCS (US) | | Azure Blob / ADLS |
|
|
36
|
+
| Target: Snowflake | external Iceberg tables via object-store catalog integration, existing external volume | Glue-integration mode, creating volumes and integrations | Snowflake-managed Iceberg writes from Spark |
|
|
37
|
+
| Target: AWS | Glue Data Catalog registration and Athena queries | | Redshift Spectrum, Lake Formation |
|
|
38
|
+
| Target: GCP | BigQuery BigLake Iceberg tables over GCS | | BigQuery Omni on S3 |
|
|
39
|
+
| Iceberg format version | 2 | 3 (generated by default, unverified on Snowflake and BigQuery) | |
|
|
40
|
+
| Operating system | Windows 10 | CI on Ubuntu (unit tests) | macOS |
|
|
41
|
+
|
|
42
|
+
## What was tested
|
|
43
|
+
|
|
44
|
+
The live run on 2026-11-09 used a synthetic data set ("corpus": customers, orders, order items) and these exact versions.
|
|
45
|
+
|
|
46
|
+
| Item | Version / value |
|
|
47
|
+
|---|---|
|
|
48
|
+
| OS | Windows 10 (10.0.19045), Git Bash and PowerShell |
|
|
49
|
+
| Python | 3.13.17 |
|
|
50
|
+
| Java | Eclipse Temurin 17.0.21 |
|
|
51
|
+
| PySpark | 4.1.4 (Spark local mode) |
|
|
52
|
+
| Iceberg | 1.12.0 (`spark-runtime-4.1_2.13`, `aws-bundle`, `gcp-bundle`) |
|
|
53
|
+
| SQLGlot | 30.30.0 |
|
|
54
|
+
| snowflake-connector-python | 5.2.0 |
|
|
55
|
+
| Snowflake JDBC | 3.19.0 |
|
|
56
|
+
| Snowflake | one account on AWS us-east-2, role SYSADMIN, key-pair login, XSMALL warehouse |
|
|
57
|
+
| AWS | S3, Glue Data Catalog, Athena workgroup `primary`, region us-east-2, boto3 1.43.127, AWS CLI 2.30.6 |
|
|
58
|
+
| GCP | one project, BigQuery location `US`, GCS location `US`, Cloud SDK 556.0.0 |
|
|
59
|
+
|
|
60
|
+
Results (all matched the Snowflake source): 20 customers, 100 orders, 100 order items, 75 curated orders on
|
|
61
|
+
Snowflake (Iceberg on S3), AWS Athena and BigQuery (Iceberg on GCS). Step by step with commands:
|
|
62
|
+
[examples.md](examples.md).
|
|
63
|
+
|
|
64
|
+
The unit test suite (`pytest -m "not spark"`, 75 tests) needs no cloud accounts. One test runs the generated PySpark
|
|
65
|
+
job against SQLite with a local Iceberg catalog (`SQL2ICE_ICEBERG=1` and the jars in `./jars`).
|