sqloxide 0.1.47__tar.gz → 0.1.51__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.
- sqloxide-0.1.51/.bumpversion.cfg +10 -0
- sqloxide-0.1.51/.cargo/config.toml +2 -0
- sqloxide-0.1.51/.github/dependabot.yml +21 -0
- sqloxide-0.1.51/.github/workflows/build-wheels.sh +11 -0
- sqloxide-0.1.51/.github/workflows/build.yaml +107 -0
- sqloxide-0.1.51/.github/workflows/ci.yml +40 -0
- sqloxide-0.1.51/.gitignore +159 -0
- sqloxide-0.1.51/CHANGELOG.md +14 -0
- sqloxide-0.1.51/Cargo.lock +232 -0
- {sqloxide-0.1.47 → sqloxide-0.1.51}/Cargo.toml +5 -5
- sqloxide-0.1.51/PKG-INFO +8 -0
- sqloxide-0.1.51/examples/depgraph.py +98 -0
- sqloxide-0.1.51/examples/visitor.py +20 -0
- sqloxide-0.1.51/justfile +8 -0
- sqloxide-0.1.51/poetry.lock +7 -0
- sqloxide-0.1.51/pyproject.toml +24 -0
- sqloxide-0.1.47/sqloxide.egg-info/PKG-INFO → sqloxide-0.1.51/readme.md +0 -11
- sqloxide-0.1.51/rust-toolchain +1 -0
- {sqloxide-0.1.47 → sqloxide-0.1.51}/src/lib.rs +5 -6
- {sqloxide-0.1.47 → sqloxide-0.1.51}/src/visitor.rs +9 -9
- sqloxide-0.1.51/tests/benchmark.py +48 -0
- sqloxide-0.1.47/PKG-INFO +0 -210
- sqloxide-0.1.47/build.py +0 -12
- sqloxide-0.1.47/pyproject.toml +0 -35
- sqloxide-0.1.47/setup.cfg +0 -4
- sqloxide-0.1.47/setup.py +0 -27
- sqloxide-0.1.47/sqloxide.egg-info/SOURCES.txt +0 -15
- sqloxide-0.1.47/sqloxide.egg-info/dependency_links.txt +0 -1
- sqloxide-0.1.47/sqloxide.egg-info/not-zip-safe +0 -1
- sqloxide-0.1.47/sqloxide.egg-info/top_level.txt +0 -1
- {sqloxide-0.1.47 → sqloxide-0.1.51}/LICENSE +0 -0
- {sqloxide-0.1.47 → sqloxide-0.1.51}/MANIFEST.in +0 -0
- {sqloxide-0.1.47 → sqloxide-0.1.51}/sqloxide/__init__.py +0 -0
- {sqloxide-0.1.47 → sqloxide-0.1.51}/tests/test_sqloxide.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: cargo
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: daily
|
|
7
|
+
time: "10:00"
|
|
8
|
+
- package-ecosystem: pip
|
|
9
|
+
directory: "/"
|
|
10
|
+
schedule:
|
|
11
|
+
interval: daily
|
|
12
|
+
time: "10:00"
|
|
13
|
+
open-pull-requests-limit: 10
|
|
14
|
+
ignore:
|
|
15
|
+
- dependency-name: setuptools-rust
|
|
16
|
+
versions:
|
|
17
|
+
- 0.12.0
|
|
18
|
+
- dependency-name: moz-sql-parser
|
|
19
|
+
versions:
|
|
20
|
+
- 4.17.21027
|
|
21
|
+
- 4.18.21031
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
name: publish-pypi
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
linux-wheels:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
target: [x86_64, i686, aarch64]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v3
|
|
16
|
+
- uses: PyO3/maturin-action@v1
|
|
17
|
+
with:
|
|
18
|
+
target: ${{ matrix.target }}
|
|
19
|
+
maturin-version: latest
|
|
20
|
+
command: build
|
|
21
|
+
manylinux: auto
|
|
22
|
+
args: --release --sdist -i 3.8 3.9 3.10 3.11 3.12 3.13
|
|
23
|
+
- uses: actions/upload-artifact@v4
|
|
24
|
+
with:
|
|
25
|
+
name: linux-wheels-${{ matrix.target }}
|
|
26
|
+
path: target/wheels/
|
|
27
|
+
|
|
28
|
+
osx-wheels:
|
|
29
|
+
runs-on: macos-latest
|
|
30
|
+
strategy:
|
|
31
|
+
matrix:
|
|
32
|
+
python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13"]
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v1
|
|
35
|
+
- uses: actions-rs/toolchain@v1
|
|
36
|
+
with:
|
|
37
|
+
toolchain: nightly
|
|
38
|
+
default: true
|
|
39
|
+
- uses: actions/setup-python@v2
|
|
40
|
+
with:
|
|
41
|
+
python-version: ${{ matrix.python-version }}
|
|
42
|
+
- name: Build wheels
|
|
43
|
+
run: |
|
|
44
|
+
python3 -m pip install maturin
|
|
45
|
+
maturin build --release
|
|
46
|
+
- uses: actions/upload-artifact@v4
|
|
47
|
+
with:
|
|
48
|
+
name: osx-${{ matrix.python-version }}-wheel
|
|
49
|
+
path: target/wheels
|
|
50
|
+
|
|
51
|
+
windows-wheels:
|
|
52
|
+
runs-on: windows-latest
|
|
53
|
+
strategy:
|
|
54
|
+
matrix:
|
|
55
|
+
python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13"]
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v1
|
|
58
|
+
- uses: actions-rs/toolchain@v1
|
|
59
|
+
with:
|
|
60
|
+
toolchain: nightly
|
|
61
|
+
default: true
|
|
62
|
+
- uses: actions/setup-python@v2
|
|
63
|
+
with:
|
|
64
|
+
python-version: ${{ matrix.python-version }}
|
|
65
|
+
- name: Build wheels
|
|
66
|
+
run: |
|
|
67
|
+
python -m pip install maturin
|
|
68
|
+
maturin build --release
|
|
69
|
+
- uses: actions/upload-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: windows-${{ matrix.python-version }}-wheel
|
|
72
|
+
path: target/wheels
|
|
73
|
+
|
|
74
|
+
push:
|
|
75
|
+
needs: [osx-wheels, windows-wheels, linux-wheels]
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v2
|
|
79
|
+
- uses: actions/download-artifact@v4
|
|
80
|
+
|
|
81
|
+
- name: Display structure of downloaded files
|
|
82
|
+
run: ls -R
|
|
83
|
+
|
|
84
|
+
- run: mkdir wheels
|
|
85
|
+
- run: mv ./linux-wheels-x86_64/* wheels
|
|
86
|
+
- run: mv ./linux-wheels-i686/* wheels
|
|
87
|
+
- run: mv ./linux-wheels-aarch64/* wheels
|
|
88
|
+
- run: mv ./osx-3.13-wheel/* wheels
|
|
89
|
+
- run: mv ./osx-3.12-wheel/* wheels
|
|
90
|
+
- run: mv ./osx-3.11-wheel/* wheels
|
|
91
|
+
- run: mv ./osx-3.10-wheel/* wheels
|
|
92
|
+
- run: mv ./osx-3.9-wheel/* wheels
|
|
93
|
+
- run: mv ./osx-3.8-wheel/* wheels
|
|
94
|
+
- run: mv ./windows-3.13-wheel/* wheels
|
|
95
|
+
- run: mv ./windows-3.12-wheel/* wheels
|
|
96
|
+
- run: mv ./windows-3.11-wheel/* wheels
|
|
97
|
+
- run: mv ./windows-3.10-wheel/* wheels
|
|
98
|
+
- run: mv ./windows-3.9-wheel/* wheels
|
|
99
|
+
- run: mv ./windows-3.8-wheel/* wheels
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
- name: Publish a Python distribution to PyPI
|
|
103
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
104
|
+
with:
|
|
105
|
+
password: ${{ secrets.PYPI }}
|
|
106
|
+
packages_dir: wheels/
|
|
107
|
+
verify_metadata: false
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [created]
|
|
6
|
+
push:
|
|
7
|
+
schedule:
|
|
8
|
+
# Runs every Thursday at 20:23 GMT to avoid bit rot
|
|
9
|
+
- cron: "20 23 * * 4"
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
lint:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v1
|
|
17
|
+
|
|
18
|
+
- name: Install rust toolchain
|
|
19
|
+
uses: actions-rs/toolchain@v1
|
|
20
|
+
with:
|
|
21
|
+
toolchain: stable
|
|
22
|
+
override: true
|
|
23
|
+
components: rustfmt, clippy
|
|
24
|
+
|
|
25
|
+
- name: Lint with rustfmt
|
|
26
|
+
uses: actions-rs/cargo@v1
|
|
27
|
+
with:
|
|
28
|
+
command: fmt
|
|
29
|
+
|
|
30
|
+
- name: Lint with clippy
|
|
31
|
+
uses: actions-rs/cargo@v1
|
|
32
|
+
with:
|
|
33
|
+
command: clippy
|
|
34
|
+
args: --all-targets --all-features
|
|
35
|
+
|
|
36
|
+
- name: Test with cargo
|
|
37
|
+
uses: actions-rs/cargo@v1.0.1
|
|
38
|
+
with:
|
|
39
|
+
command: test
|
|
40
|
+
toolchain: stable
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
|
|
2
|
+
# Created by https://www.toptal.com/developers/gitignore/api/python,rust,vscode
|
|
3
|
+
# Edit at https://www.toptal.com/developers/gitignore?templates=python,rust,vscode
|
|
4
|
+
|
|
5
|
+
### Python ###
|
|
6
|
+
# Byte-compiled / optimized / DLL files
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*$py.class
|
|
10
|
+
|
|
11
|
+
# C extensions
|
|
12
|
+
*.so
|
|
13
|
+
|
|
14
|
+
# Distribution / packaging
|
|
15
|
+
.Python
|
|
16
|
+
build/
|
|
17
|
+
develop-eggs/
|
|
18
|
+
dist/
|
|
19
|
+
downloads/
|
|
20
|
+
eggs/
|
|
21
|
+
.eggs/
|
|
22
|
+
lib/
|
|
23
|
+
lib64/
|
|
24
|
+
parts/
|
|
25
|
+
sdist/
|
|
26
|
+
var/
|
|
27
|
+
wheels/
|
|
28
|
+
pip-wheel-metadata/
|
|
29
|
+
share/python-wheels/
|
|
30
|
+
*.egg-info/
|
|
31
|
+
.installed.cfg
|
|
32
|
+
*.egg
|
|
33
|
+
MANIFEST
|
|
34
|
+
|
|
35
|
+
# PyInstaller
|
|
36
|
+
# Usually these files are written by a python script from a template
|
|
37
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
38
|
+
*.manifest
|
|
39
|
+
*.spec
|
|
40
|
+
|
|
41
|
+
# Installer logs
|
|
42
|
+
pip-log.txt
|
|
43
|
+
pip-delete-this-directory.txt
|
|
44
|
+
|
|
45
|
+
# Unit test / coverage reports
|
|
46
|
+
htmlcov/
|
|
47
|
+
.tox/
|
|
48
|
+
.nox/
|
|
49
|
+
.coverage
|
|
50
|
+
.coverage.*
|
|
51
|
+
.cache
|
|
52
|
+
nosetests.xml
|
|
53
|
+
coverage.xml
|
|
54
|
+
*.cover
|
|
55
|
+
*.py,cover
|
|
56
|
+
.hypothesis/
|
|
57
|
+
.pytest_cache/
|
|
58
|
+
pytestdebug.log
|
|
59
|
+
|
|
60
|
+
# Translations
|
|
61
|
+
*.mo
|
|
62
|
+
*.pot
|
|
63
|
+
|
|
64
|
+
# Django stuff:
|
|
65
|
+
*.log
|
|
66
|
+
local_settings.py
|
|
67
|
+
db.sqlite3
|
|
68
|
+
db.sqlite3-journal
|
|
69
|
+
|
|
70
|
+
# Flask stuff:
|
|
71
|
+
instance/
|
|
72
|
+
.webassets-cache
|
|
73
|
+
|
|
74
|
+
# Scrapy stuff:
|
|
75
|
+
.scrapy
|
|
76
|
+
|
|
77
|
+
# Sphinx documentation
|
|
78
|
+
docs/_build/
|
|
79
|
+
doc/_build/
|
|
80
|
+
|
|
81
|
+
# PyBuilder
|
|
82
|
+
target/
|
|
83
|
+
|
|
84
|
+
# Jupyter Notebook
|
|
85
|
+
.ipynb_checkpoints
|
|
86
|
+
|
|
87
|
+
# IPython
|
|
88
|
+
profile_default/
|
|
89
|
+
ipython_config.py
|
|
90
|
+
|
|
91
|
+
# pyenv
|
|
92
|
+
.python-version
|
|
93
|
+
|
|
94
|
+
# pipenv
|
|
95
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
96
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
97
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
98
|
+
# install all needed dependencies.
|
|
99
|
+
#Pipfile.lock
|
|
100
|
+
|
|
101
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
102
|
+
__pypackages__/
|
|
103
|
+
|
|
104
|
+
# Celery stuff
|
|
105
|
+
celerybeat-schedule
|
|
106
|
+
celerybeat.pid
|
|
107
|
+
|
|
108
|
+
# SageMath parsed files
|
|
109
|
+
*.sage.py
|
|
110
|
+
|
|
111
|
+
# Environments
|
|
112
|
+
.env
|
|
113
|
+
.venv
|
|
114
|
+
env/
|
|
115
|
+
venv/
|
|
116
|
+
ENV/
|
|
117
|
+
env.bak/
|
|
118
|
+
venv.bak/
|
|
119
|
+
pythonenv*
|
|
120
|
+
|
|
121
|
+
# Spyder project settings
|
|
122
|
+
.spyderproject
|
|
123
|
+
.spyproject
|
|
124
|
+
|
|
125
|
+
# Rope project settings
|
|
126
|
+
.ropeproject
|
|
127
|
+
|
|
128
|
+
# mkdocs documentation
|
|
129
|
+
/site
|
|
130
|
+
|
|
131
|
+
# mypy
|
|
132
|
+
.mypy_cache/
|
|
133
|
+
.dmypy.json
|
|
134
|
+
dmypy.json
|
|
135
|
+
|
|
136
|
+
# Pyre type checker
|
|
137
|
+
.pyre/
|
|
138
|
+
|
|
139
|
+
# pytype static type analyzer
|
|
140
|
+
.pytype/
|
|
141
|
+
|
|
142
|
+
# profiling data
|
|
143
|
+
.prof
|
|
144
|
+
|
|
145
|
+
### Rust ###
|
|
146
|
+
# Generated by Cargo
|
|
147
|
+
# will have compiled files and executables
|
|
148
|
+
/target/
|
|
149
|
+
|
|
150
|
+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
|
151
|
+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
|
152
|
+
Cargo.lock
|
|
153
|
+
|
|
154
|
+
*.code-workspace
|
|
155
|
+
|
|
156
|
+
# End of https://www.toptal.com/developers/gitignore/api/python,rust,vscode
|
|
157
|
+
|
|
158
|
+
/examples/depgraph.gv*
|
|
159
|
+
.vscode
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
# 0.1.36
|
|
3
|
+
|
|
4
|
+
- Upgrade to sqlparser-rs 0.36.0
|
|
5
|
+
- Add more visitor functions
|
|
6
|
+
- `mutate_relations`
|
|
7
|
+
- `mutate_expressions`
|
|
8
|
+
- `extract_expressions`
|
|
9
|
+
- add `restore_ast`
|
|
10
|
+
- remove ability for library to internally panic via `.expect()`, now throws only `ValueError`
|
|
11
|
+
|
|
12
|
+
# 0.1.35
|
|
13
|
+
|
|
14
|
+
- Added `extract_relations` function to assist in extracting table references from the AST in Rust.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 3
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "autocfg"
|
|
7
|
+
version = "1.4.0"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "cfg-if"
|
|
13
|
+
version = "1.0.0"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "heck"
|
|
19
|
+
version = "0.5.0"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "indoc"
|
|
25
|
+
version = "2.0.5"
|
|
26
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
27
|
+
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
|
28
|
+
|
|
29
|
+
[[package]]
|
|
30
|
+
name = "libc"
|
|
31
|
+
version = "0.2.161"
|
|
32
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
33
|
+
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
|
34
|
+
|
|
35
|
+
[[package]]
|
|
36
|
+
name = "log"
|
|
37
|
+
version = "0.4.22"
|
|
38
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
39
|
+
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
|
40
|
+
|
|
41
|
+
[[package]]
|
|
42
|
+
name = "memoffset"
|
|
43
|
+
version = "0.9.1"
|
|
44
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
45
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
46
|
+
dependencies = [
|
|
47
|
+
"autocfg",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[[package]]
|
|
51
|
+
name = "once_cell"
|
|
52
|
+
version = "1.20.2"
|
|
53
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
54
|
+
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
|
55
|
+
|
|
56
|
+
[[package]]
|
|
57
|
+
name = "portable-atomic"
|
|
58
|
+
version = "1.9.0"
|
|
59
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
60
|
+
checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
|
|
61
|
+
|
|
62
|
+
[[package]]
|
|
63
|
+
name = "proc-macro2"
|
|
64
|
+
version = "1.0.89"
|
|
65
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
66
|
+
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
|
67
|
+
dependencies = [
|
|
68
|
+
"unicode-ident",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[[package]]
|
|
72
|
+
name = "pyo3"
|
|
73
|
+
version = "0.22.5"
|
|
74
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
75
|
+
checksum = "3d922163ba1f79c04bc49073ba7b32fd5a8d3b76a87c955921234b8e77333c51"
|
|
76
|
+
dependencies = [
|
|
77
|
+
"cfg-if",
|
|
78
|
+
"indoc",
|
|
79
|
+
"libc",
|
|
80
|
+
"memoffset",
|
|
81
|
+
"once_cell",
|
|
82
|
+
"portable-atomic",
|
|
83
|
+
"pyo3-build-config",
|
|
84
|
+
"pyo3-ffi",
|
|
85
|
+
"pyo3-macros",
|
|
86
|
+
"unindent",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[[package]]
|
|
90
|
+
name = "pyo3-build-config"
|
|
91
|
+
version = "0.22.5"
|
|
92
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
93
|
+
checksum = "bc38c5feeb496c8321091edf3d63e9a6829eab4b863b4a6a65f26f3e9cc6b179"
|
|
94
|
+
dependencies = [
|
|
95
|
+
"once_cell",
|
|
96
|
+
"target-lexicon",
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
[[package]]
|
|
100
|
+
name = "pyo3-ffi"
|
|
101
|
+
version = "0.22.5"
|
|
102
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
103
|
+
checksum = "94845622d88ae274d2729fcefc850e63d7a3ddff5e3ce11bd88486db9f1d357d"
|
|
104
|
+
dependencies = [
|
|
105
|
+
"libc",
|
|
106
|
+
"pyo3-build-config",
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
[[package]]
|
|
110
|
+
name = "pyo3-macros"
|
|
111
|
+
version = "0.22.5"
|
|
112
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
113
|
+
checksum = "e655aad15e09b94ffdb3ce3d217acf652e26bbc37697ef012f5e5e348c716e5e"
|
|
114
|
+
dependencies = [
|
|
115
|
+
"proc-macro2",
|
|
116
|
+
"pyo3-macros-backend",
|
|
117
|
+
"quote",
|
|
118
|
+
"syn",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
[[package]]
|
|
122
|
+
name = "pyo3-macros-backend"
|
|
123
|
+
version = "0.22.5"
|
|
124
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
125
|
+
checksum = "ae1e3f09eecd94618f60a455a23def79f79eba4dc561a97324bf9ac8c6df30ce"
|
|
126
|
+
dependencies = [
|
|
127
|
+
"heck",
|
|
128
|
+
"proc-macro2",
|
|
129
|
+
"pyo3-build-config",
|
|
130
|
+
"quote",
|
|
131
|
+
"syn",
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
[[package]]
|
|
135
|
+
name = "pythonize"
|
|
136
|
+
version = "0.22.0"
|
|
137
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
138
|
+
checksum = "90fcf491425978bd889015d5430f6473d91bdfa2097262f1e731aadcf6c2113e"
|
|
139
|
+
dependencies = [
|
|
140
|
+
"pyo3",
|
|
141
|
+
"serde",
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
[[package]]
|
|
145
|
+
name = "quote"
|
|
146
|
+
version = "1.0.37"
|
|
147
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
148
|
+
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
|
149
|
+
dependencies = [
|
|
150
|
+
"proc-macro2",
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
[[package]]
|
|
154
|
+
name = "serde"
|
|
155
|
+
version = "1.0.213"
|
|
156
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
157
|
+
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
|
158
|
+
dependencies = [
|
|
159
|
+
"serde_derive",
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
[[package]]
|
|
163
|
+
name = "serde_derive"
|
|
164
|
+
version = "1.0.213"
|
|
165
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
166
|
+
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
|
167
|
+
dependencies = [
|
|
168
|
+
"proc-macro2",
|
|
169
|
+
"quote",
|
|
170
|
+
"syn",
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
[[package]]
|
|
174
|
+
name = "sqloxide"
|
|
175
|
+
version = "0.1.51"
|
|
176
|
+
dependencies = [
|
|
177
|
+
"pyo3",
|
|
178
|
+
"pythonize",
|
|
179
|
+
"serde",
|
|
180
|
+
"sqlparser",
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
[[package]]
|
|
184
|
+
name = "sqlparser"
|
|
185
|
+
version = "0.51.0"
|
|
186
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
187
|
+
checksum = "5fe11944a61da0da3f592e19a45ebe5ab92dc14a779907ff1f08fbb797bfefc7"
|
|
188
|
+
dependencies = [
|
|
189
|
+
"log",
|
|
190
|
+
"serde",
|
|
191
|
+
"sqlparser_derive",
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
[[package]]
|
|
195
|
+
name = "sqlparser_derive"
|
|
196
|
+
version = "0.2.2"
|
|
197
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
198
|
+
checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554"
|
|
199
|
+
dependencies = [
|
|
200
|
+
"proc-macro2",
|
|
201
|
+
"quote",
|
|
202
|
+
"syn",
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
[[package]]
|
|
206
|
+
name = "syn"
|
|
207
|
+
version = "2.0.83"
|
|
208
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
209
|
+
checksum = "01680f5d178a369f817f43f3d399650272873a8e7588a7872f7e90edc71d60a3"
|
|
210
|
+
dependencies = [
|
|
211
|
+
"proc-macro2",
|
|
212
|
+
"quote",
|
|
213
|
+
"unicode-ident",
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
[[package]]
|
|
217
|
+
name = "target-lexicon"
|
|
218
|
+
version = "0.12.16"
|
|
219
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
220
|
+
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
221
|
+
|
|
222
|
+
[[package]]
|
|
223
|
+
name = "unicode-ident"
|
|
224
|
+
version = "1.0.13"
|
|
225
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
226
|
+
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
|
227
|
+
|
|
228
|
+
[[package]]
|
|
229
|
+
name = "unindent"
|
|
230
|
+
version = "0.2.3"
|
|
231
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
232
|
+
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "sqloxide"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.51"
|
|
4
4
|
authors = ["Will Eaton <me@wseaton.com>"]
|
|
5
|
-
edition = "
|
|
5
|
+
edition = "2021"
|
|
6
6
|
|
|
7
7
|
[lib]
|
|
8
8
|
name = "sqloxide"
|
|
9
9
|
crate-type = ["cdylib"]
|
|
10
10
|
|
|
11
11
|
[dependencies]
|
|
12
|
-
pythonize = "0.
|
|
12
|
+
pythonize = "0.22"
|
|
13
13
|
serde = "1.0.171"
|
|
14
14
|
|
|
15
15
|
[dependencies.pyo3]
|
|
16
|
-
version = "0.
|
|
16
|
+
version = "0.22"
|
|
17
17
|
features = ["extension-module"]
|
|
18
18
|
|
|
19
19
|
[dependencies.sqlparser]
|
|
20
|
-
version = "0.
|
|
20
|
+
version = "0.51.0"
|
|
21
21
|
features = ["serde", "visitor"]
|
sqloxide-0.1.51/PKG-INFO
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Builds a dependency graph by parsing all of the queries inside a folder
|
|
3
|
+
of .sql files. Renders via graphviz.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from glob import glob
|
|
10
|
+
from typing import List
|
|
11
|
+
|
|
12
|
+
import sqloxide
|
|
13
|
+
from graphviz import Digraph
|
|
14
|
+
|
|
15
|
+
parser = argparse.ArgumentParser()
|
|
16
|
+
parser.add_argument("--path", "-p", type=str, help="The path to process queries for.")
|
|
17
|
+
parser.add_argument("--dialect", "-d", type=str, help="The dialect to use.")
|
|
18
|
+
|
|
19
|
+
def get_sql_files(path: str) -> List[str]:
|
|
20
|
+
return glob(path + "/**/*.sql")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_key_recursive(search_dict, field):
|
|
24
|
+
"""
|
|
25
|
+
Takes a dict with nested lists and dicts,
|
|
26
|
+
and searches all dicts for a key of the field
|
|
27
|
+
provided.
|
|
28
|
+
|
|
29
|
+
- modified from: https://stackoverflow.com/a/20254842
|
|
30
|
+
"""
|
|
31
|
+
fields_found = []
|
|
32
|
+
|
|
33
|
+
for key, value in search_dict.items():
|
|
34
|
+
|
|
35
|
+
if key == field:
|
|
36
|
+
fields_found.append(value)
|
|
37
|
+
|
|
38
|
+
elif isinstance(value, dict):
|
|
39
|
+
results = get_key_recursive(value, field)
|
|
40
|
+
for result in results:
|
|
41
|
+
fields_found.append(result)
|
|
42
|
+
|
|
43
|
+
elif isinstance(value, list):
|
|
44
|
+
for item in value:
|
|
45
|
+
if isinstance(item, dict):
|
|
46
|
+
more_results = get_key_recursive(item, field)
|
|
47
|
+
for another_result in more_results:
|
|
48
|
+
fields_found.append(another_result)
|
|
49
|
+
|
|
50
|
+
return fields_found
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_tables_in_query(SQL: str, dialect: str) -> List[str]:
|
|
54
|
+
|
|
55
|
+
res = sqloxide.parse_sql(sql=SQL, dialect=dialect)
|
|
56
|
+
tables = get_key_recursive(res[0]["Query"], "Table")
|
|
57
|
+
|
|
58
|
+
results = list()
|
|
59
|
+
|
|
60
|
+
for table in tables:
|
|
61
|
+
results.append(table["name"][0]["value"] + "." + table["name"][1]["value"])
|
|
62
|
+
|
|
63
|
+
return results
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
|
|
68
|
+
args = parser.parse_args()
|
|
69
|
+
|
|
70
|
+
files = get_sql_files(args.path)
|
|
71
|
+
print(f'Parsing using dialect: {args.dialect}')
|
|
72
|
+
|
|
73
|
+
result_dict = dict()
|
|
74
|
+
|
|
75
|
+
for _f in files:
|
|
76
|
+
pretty_filename = ".".join(_f.split("/")[-2:])
|
|
77
|
+
|
|
78
|
+
with open(_f, "r") as f:
|
|
79
|
+
sql = f.read()
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
tables = get_tables_in_query(SQL=sql, dialect=args.dialect)
|
|
83
|
+
result_dict[pretty_filename] = list(set(tables.copy()))
|
|
84
|
+
except ValueError:
|
|
85
|
+
print(f"File: {_f} failed to parse.")
|
|
86
|
+
|
|
87
|
+
dot = Digraph(engine="dot")
|
|
88
|
+
dot.attr(rankdir="LR")
|
|
89
|
+
dot.attr(splines="ortho")
|
|
90
|
+
dot.node_attr['shape'] = 'box'
|
|
91
|
+
|
|
92
|
+
for view, tables in result_dict.items():
|
|
93
|
+
view = view[:-4]
|
|
94
|
+
dot.node(view)
|
|
95
|
+
for table in tables:
|
|
96
|
+
dot.edge(view, table)
|
|
97
|
+
|
|
98
|
+
dot.render("./examples/depgraph.gv", view=True)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from sqloxide import parse_sql, mutate_expressions, mutate_relations
|
|
2
|
+
|
|
3
|
+
if __name__ == "__main__":
|
|
4
|
+
sql = "SELECT something from somewhere where something = 1 and something_else = 2"
|
|
5
|
+
|
|
6
|
+
def func(x):
|
|
7
|
+
if "CompoundIdentifier" in x.keys():
|
|
8
|
+
for y in x["CompoundIdentifier"]:
|
|
9
|
+
y["value"] = y["value"].upper()
|
|
10
|
+
return x
|
|
11
|
+
|
|
12
|
+
ast = parse_sql(sql=sql, dialect="ansi")
|
|
13
|
+
result = mutate_expressions(parsed_query=ast, func=func)
|
|
14
|
+
print(result)
|
|
15
|
+
|
|
16
|
+
def func(x):
|
|
17
|
+
return x.replace("somewhere", "anywhere")
|
|
18
|
+
|
|
19
|
+
result = mutate_relations(parsed_query=ast, func=func)
|
|
20
|
+
print(result)
|
sqloxide-0.1.51/justfile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "sqloxide"
|
|
3
|
+
version = "0.1.51rc1"
|
|
4
|
+
repository = "https://github.com/wseaton/sqloxide"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
description = "Python bindings for sqlparser-rs"
|
|
7
|
+
authors = ["Will Eaton <me@wseaton.com>"]
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Development Status :: 3 - Alpha",
|
|
10
|
+
"Programming Language :: Rust",
|
|
11
|
+
"Topic :: Database",
|
|
12
|
+
"License :: OSI Approved :: MIT License",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["maturin>=0.13,<0.14"]
|
|
17
|
+
build-backend = "maturin"
|
|
18
|
+
|
|
19
|
+
[project]
|
|
20
|
+
name = "sqloxide"
|
|
21
|
+
requires-python = ">=3.7"
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Programming Language :: Rust",
|
|
24
|
+
]
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: sqloxide
|
|
3
|
-
Version: 0.1.47
|
|
4
|
-
Summary: Python bindings for sqlparser-rs
|
|
5
|
-
Home-page: https://github.com/wseaton/sqloxide
|
|
6
|
-
Author: Will Eaton
|
|
7
|
-
Author-email: me@wseaton.com
|
|
8
|
-
Requires-Python: >=3.8,<4.0
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
|
|
12
1
|
# sqloxide
|
|
13
2
|
|
|
14
3
|
[](https://github.com/wseaton/sqloxide/actions/workflows/ci.yml)[](https://pepy.tech/project/sqloxide)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
stable
|
|
@@ -3,7 +3,6 @@ use pythonize::pythonize;
|
|
|
3
3
|
use pyo3::exceptions::PyValueError;
|
|
4
4
|
use pyo3::prelude::*;
|
|
5
5
|
|
|
6
|
-
use pyo3::wrap_pyfunction;
|
|
7
6
|
use pythonize::PythonizeError;
|
|
8
7
|
|
|
9
8
|
use sqlparser::ast::Statement;
|
|
@@ -20,12 +19,12 @@ use visitor::{extract_expressions, extract_relations, mutate_expressions, mutate
|
|
|
20
19
|
/// Available `dialects`: https://github.com/sqlparser-rs/sqlparser-rs/blob/main/src/dialect/mod.rs#L189-L206
|
|
21
20
|
#[pyfunction]
|
|
22
21
|
#[pyo3(text_signature = "(sql, dialect)")]
|
|
23
|
-
fn parse_sql(py: Python, sql:
|
|
22
|
+
fn parse_sql(py: Python, sql: String, dialect: String) -> PyResult<PyObject> {
|
|
24
23
|
let chosen_dialect = dialect_from_str(dialect).unwrap_or_else(|| {
|
|
25
24
|
println!("The dialect you chose was not recognized, falling back to 'generic'");
|
|
26
25
|
Box::new(GenericDialect {})
|
|
27
26
|
});
|
|
28
|
-
let parse_result = Parser::parse_sql(&*chosen_dialect, sql);
|
|
27
|
+
let parse_result = Parser::parse_sql(&*chosen_dialect, &sql);
|
|
29
28
|
|
|
30
29
|
let output = match parse_result {
|
|
31
30
|
Ok(statements) => pythonize(py, &statements).map_err(|e| {
|
|
@@ -40,13 +39,13 @@ fn parse_sql(py: Python, sql: &str, dialect: &str) -> PyResult<PyObject> {
|
|
|
40
39
|
}
|
|
41
40
|
};
|
|
42
41
|
|
|
43
|
-
Ok(output)
|
|
42
|
+
Ok(output.into())
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
/// This utility function allows reconstituing a modified AST back into list of SQL queries.
|
|
47
46
|
#[pyfunction]
|
|
48
47
|
#[pyo3(text_signature = "(ast)")]
|
|
49
|
-
fn restore_ast(_py: Python, ast: &PyAny) -> PyResult<Vec<String>> {
|
|
48
|
+
fn restore_ast(_py: Python, ast: &Bound<'_, PyAny>) -> PyResult<Vec<String>> {
|
|
50
49
|
let parse_result: Result<Vec<Statement>, PythonizeError> = pythonize::depythonize(ast);
|
|
51
50
|
|
|
52
51
|
let output = match parse_result {
|
|
@@ -66,7 +65,7 @@ fn restore_ast(_py: Python, ast: &PyAny) -> PyResult<Vec<String>> {
|
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
#[pymodule]
|
|
69
|
-
fn sqloxide(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
68
|
+
fn sqloxide(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
70
69
|
m.add_function(wrap_pyfunction!(parse_sql, m)?)?;
|
|
71
70
|
m.add_function(wrap_pyfunction!(restore_ast, m)?)?;
|
|
72
71
|
// TODO: maybe refactor into seperate module
|
|
@@ -10,7 +10,7 @@ use sqlparser::ast::{
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// Refactored function for handling depythonization
|
|
13
|
-
fn depythonize_query(parsed_query: &PyAny) -> Result<Vec<Statement>, PyErr> {
|
|
13
|
+
fn depythonize_query(parsed_query: &Bound<'_, PyAny>) -> Result<Vec<Statement>, PyErr> {
|
|
14
14
|
match pythonize::depythonize(parsed_query) {
|
|
15
15
|
Ok(statements) => Ok(statements),
|
|
16
16
|
Err(e) => {
|
|
@@ -27,7 +27,7 @@ where
|
|
|
27
27
|
T: Sized + Serialize,
|
|
28
28
|
{
|
|
29
29
|
match pythonize::pythonize(py, &output) {
|
|
30
|
-
Ok(p) => Ok(p),
|
|
30
|
+
Ok(p) => Ok(p.into()),
|
|
31
31
|
Err(e) => {
|
|
32
32
|
let msg = e.to_string();
|
|
33
33
|
Err(PyValueError::new_err(format!(
|
|
@@ -39,7 +39,7 @@ where
|
|
|
39
39
|
|
|
40
40
|
#[pyfunction]
|
|
41
41
|
#[pyo3(text_signature = "(parsed_query)")]
|
|
42
|
-
pub fn extract_relations(py: Python, parsed_query: &PyAny) -> PyResult<PyObject> {
|
|
42
|
+
pub fn extract_relations(py: Python, parsed_query: &Bound<'_, PyAny>) -> PyResult<PyObject> {
|
|
43
43
|
let statements = depythonize_query(parsed_query)?;
|
|
44
44
|
|
|
45
45
|
let mut relations = Vec::new();
|
|
@@ -55,7 +55,7 @@ pub fn extract_relations(py: Python, parsed_query: &PyAny) -> PyResult<PyObject>
|
|
|
55
55
|
|
|
56
56
|
#[pyfunction]
|
|
57
57
|
#[pyo3(text_signature = "(parsed_query, func)")]
|
|
58
|
-
pub fn mutate_relations(_py: Python, parsed_query: &
|
|
58
|
+
pub fn mutate_relations(_py: Python, parsed_query: &Bound<'_, PyAny>, func: &Bound<'_, PyAny>) -> PyResult<Vec<String>> {
|
|
59
59
|
let mut statements = depythonize_query(parsed_query)?;
|
|
60
60
|
|
|
61
61
|
for statement in &mut statements {
|
|
@@ -85,8 +85,8 @@ pub fn mutate_relations(_py: Python, parsed_query: &PyAny, func: &PyAny) -> PyRe
|
|
|
85
85
|
|
|
86
86
|
#[pyfunction]
|
|
87
87
|
#[pyo3(text_signature = "(parsed_query, func)")]
|
|
88
|
-
pub fn mutate_expressions(py: Python, parsed_query: &
|
|
89
|
-
let mut statements = depythonize_query(parsed_query)?;
|
|
88
|
+
pub fn mutate_expressions(py: Python, parsed_query: &Bound<'_, PyAny>, func: &Bound<'_, PyAny>) -> PyResult<Vec<String>> {
|
|
89
|
+
let mut statements: Vec<Statement> = depythonize_query(parsed_query)?;
|
|
90
90
|
|
|
91
91
|
for statement in &mut statements {
|
|
92
92
|
visit_expressions_mut(statement, |expr| {
|
|
@@ -110,7 +110,7 @@ pub fn mutate_expressions(py: Python, parsed_query: &PyAny, func: &PyAny) -> PyR
|
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
-
*expr = match pythonize::depythonize(func_result) {
|
|
113
|
+
*expr = match pythonize::depythonize(&func_result) {
|
|
114
114
|
Ok(val) => val,
|
|
115
115
|
Err(e) => {
|
|
116
116
|
let msg = e.to_string();
|
|
@@ -132,8 +132,8 @@ pub fn mutate_expressions(py: Python, parsed_query: &PyAny, func: &PyAny) -> PyR
|
|
|
132
132
|
|
|
133
133
|
#[pyfunction]
|
|
134
134
|
#[pyo3(text_signature = "(parsed_query)")]
|
|
135
|
-
pub fn extract_expressions(py: Python, parsed_query: &PyAny) -> PyResult<PyObject> {
|
|
136
|
-
let statements = depythonize_query(parsed_query)?;
|
|
135
|
+
pub fn extract_expressions(py: Python, parsed_query: &Bound<'_, PyAny>) -> PyResult<PyObject> {
|
|
136
|
+
let statements: Vec<Statement> = depythonize_query(parsed_query)?;
|
|
137
137
|
|
|
138
138
|
let mut expressions = Vec::new();
|
|
139
139
|
for statement in statements {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from sqloxide import parse_sql
|
|
4
|
+
import sqlparse
|
|
5
|
+
import sqlglot
|
|
6
|
+
import json
|
|
7
|
+
import moz_sql_parser
|
|
8
|
+
|
|
9
|
+
TEST_SQL = """
|
|
10
|
+
SELECT employee.first_name, employee.last_name,
|
|
11
|
+
call.start_time, call.end_time, call_outcome.outcome_text
|
|
12
|
+
FROM employee
|
|
13
|
+
INNER JOIN call ON call.employee_id = employee.id
|
|
14
|
+
INNER JOIN call_outcome ON call.call_outcome_id = call_outcome.id
|
|
15
|
+
ORDER BY call.start_time ASC;
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def bench_parse_sql():
|
|
20
|
+
return parse_sql(sql=TEST_SQL, dialect="ansi")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def bench_sqlparser():
|
|
24
|
+
return sqlparse.parse(TEST_SQL)[0]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def bench_mozsqlparser():
|
|
28
|
+
return json.dumps(moz_sql_parser.parse(TEST_SQL))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def bench_sqlglot():
|
|
32
|
+
return sqlglot.parse(TEST_SQL, error_level=sqlglot.ErrorLevel.IGNORE)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_sqloxide(benchmark):
|
|
36
|
+
benchmark(bench_parse_sql)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_sqlparser(benchmark):
|
|
40
|
+
benchmark(bench_sqlparser)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_mozsqlparser(benchmark):
|
|
44
|
+
benchmark(bench_mozsqlparser)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_sqlglot(benchmark):
|
|
48
|
+
benchmark(bench_sqlglot)
|
sqloxide-0.1.47/PKG-INFO
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: sqloxide
|
|
3
|
-
Version: 0.1.47
|
|
4
|
-
Summary: Python bindings for sqlparser-rs
|
|
5
|
-
Home-page: https://github.com/wseaton/sqloxide
|
|
6
|
-
Author: Will Eaton
|
|
7
|
-
Author-email: me@wseaton.com
|
|
8
|
-
Requires-Python: >=3.8,<4.0
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
|
|
12
|
-
# sqloxide
|
|
13
|
-
|
|
14
|
-
[](https://github.com/wseaton/sqloxide/actions/workflows/ci.yml)[](https://pepy.tech/project/sqloxide)
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
- [sqloxide](#sqloxide)
|
|
19
|
-
- [Installation](#installation)
|
|
20
|
-
- [Usage](#usage)
|
|
21
|
-
- [Parsing](#parsing)
|
|
22
|
-
- [AST Rewrites](#ast-rewrites)
|
|
23
|
-
- [Benchmarks](#benchmarks)
|
|
24
|
-
- [Example](#example)
|
|
25
|
-
- [Develop](#develop)
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
`sqloxide` wraps rust bindings for [sqlparser-rs](https://github.com/ballista-compute/sqlparser-rs) into a python package using `pyO3`.
|
|
30
|
-
|
|
31
|
-
The original goal of this project was to have a very fast, efficient, and accurate SQL parser I could use for building data lineage graphs across large code bases (think hundreds of auto-generated .sql files). Most existing sql parsing approaches for python are either very slow or not accurate (especially in regards to deeply nested queries, sub-selects and/or table aliases). Looking to the rust community for support, I found the excellent `sqlparser-rs` crate which is quite easy to wrap in python code.
|
|
32
|
-
|
|
33
|
-
## Installation
|
|
34
|
-
|
|
35
|
-
The project provides `manylinux2014` wheels on pypi so it should be compatible with most linux distributions. Native wheels are also now available for OSX and Windows.
|
|
36
|
-
|
|
37
|
-
To install from pypi:
|
|
38
|
-
|
|
39
|
-
```sh
|
|
40
|
-
pip install sqloxide
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Usage
|
|
44
|
-
|
|
45
|
-
### Parsing
|
|
46
|
-
|
|
47
|
-
Parsing a SQL query is relatively straight forward:
|
|
48
|
-
|
|
49
|
-
```python
|
|
50
|
-
from sqloxide import parse_sql
|
|
51
|
-
|
|
52
|
-
sql = """
|
|
53
|
-
SELECT employee.first_name, employee.last_name,
|
|
54
|
-
call.start_time, call.end_time, call_outcome.outcome_text
|
|
55
|
-
FROM employee
|
|
56
|
-
INNER JOIN call ON call.employee_id = employee.id
|
|
57
|
-
INNER JOIN call_outcome ON call.call_outcome_id = call_outcome.id
|
|
58
|
-
ORDER BY call.start_time ASC;
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
output = parse_sql(sql=sql, dialect='ansi')
|
|
62
|
-
|
|
63
|
-
print(output)
|
|
64
|
-
|
|
65
|
-
>>> [
|
|
66
|
-
{
|
|
67
|
-
"Query": {
|
|
68
|
-
"ctes": [],
|
|
69
|
-
"body": {
|
|
70
|
-
"Select": {
|
|
71
|
-
"distinct": false,
|
|
72
|
-
"top": null,
|
|
73
|
-
"projection": [
|
|
74
|
-
{
|
|
75
|
-
"UnnamedExpr": {
|
|
76
|
-
"CompoundIdentifier": [
|
|
77
|
-
{
|
|
78
|
-
"value": "employee",
|
|
79
|
-
"quote_style": null
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
"value": "first_name",
|
|
83
|
-
"quote_style": null
|
|
84
|
-
}
|
|
85
|
-
]
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
"UnnamedExpr": {
|
|
90
|
-
"CompoundIdentifier": [
|
|
91
|
-
{
|
|
92
|
-
"value": "employee",
|
|
93
|
-
"quote_style": null
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
"value": "last_name",
|
|
97
|
-
"quote_style": null
|
|
98
|
-
}
|
|
99
|
-
]
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
"UnnamedExpr": {
|
|
104
|
-
"CompoundIdentifier": [
|
|
105
|
-
{
|
|
106
|
-
"value": "call",
|
|
107
|
-
"quote_style": null
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
"value": "start_time",
|
|
111
|
-
"quote_style": null
|
|
112
|
-
}
|
|
113
|
-
]
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
{ # OUTPUT TRUNCATED
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Note that you get back what looks like a JSON document but in actual python types, this is a typed AST that matches the sqlparser-rs AST schema.
|
|
120
|
-
|
|
121
|
-
We can convert this AST back into a SQL query by running:
|
|
122
|
-
|
|
123
|
-
```python
|
|
124
|
-
from sqloxide import restore_ast
|
|
125
|
-
|
|
126
|
-
query = restore_ast(ast=output)
|
|
127
|
-
print(query)
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
This reconstruction is helpful if you want to make manual edits to the AST in python.
|
|
131
|
-
|
|
132
|
-
### AST Rewrites
|
|
133
|
-
|
|
134
|
-
If you want a more structured approach to AST edits, we also expose APIs that allow you to use the visitor pattern to make query modifications.
|
|
135
|
-
|
|
136
|
-
Here is an example for mutating a subset of the expressions in the query to be SHOUTING UPPERCASE:
|
|
137
|
-
|
|
138
|
-
```python
|
|
139
|
-
from sqloxide import parse_sql, mutate_expressions
|
|
140
|
-
|
|
141
|
-
sql = "SELECT something from somewhere where something = 1 and something_else = 2"
|
|
142
|
-
|
|
143
|
-
def func(x):
|
|
144
|
-
if "CompoundIdentifier" in x.keys():
|
|
145
|
-
for y in x["CompoundIdentifier"]:
|
|
146
|
-
y["value"] = y["value"].upper()
|
|
147
|
-
return x
|
|
148
|
-
|
|
149
|
-
ast = parse_sql(sql=sql, dialect="ansi")
|
|
150
|
-
result = mutate_expressions(parsed_query=ast, func=func)
|
|
151
|
-
print(result)
|
|
152
|
-
---
|
|
153
|
-
>>> ['SELECT something FROM somewhere WHERE something = 1 AND something_else = 2']
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
What if you needed to make a structured edit to the table name in the above query? There is also an API for that as well:
|
|
157
|
-
|
|
158
|
-
```python
|
|
159
|
-
from sqloxide import parse_sql, mutate_relations
|
|
160
|
-
|
|
161
|
-
def func(x):
|
|
162
|
-
return x.replace("somewhere", "anywhere")
|
|
163
|
-
result = mutate_relations(parsed_query=ast, func=func)
|
|
164
|
-
print(result)
|
|
165
|
-
---
|
|
166
|
-
>>> ['SELECT something FROM anywhere WHERE something = 1 AND something_else = 2']
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
These features combined allow for powerful semantic rewrites of queries, if you have any examples you'd like to share please contribue back to the `examples/` folder!
|
|
171
|
-
|
|
172
|
-
## Benchmarks
|
|
173
|
-
|
|
174
|
-
We run 4 benchmarks, comparing to some python native sql parsing libraries:
|
|
175
|
-
|
|
176
|
-
- `test_sqloxide` - parse query and get a python object back from rust
|
|
177
|
-
- `test_sqlparser` - testing [sqlparse](https://pypi.org/project/sqlparse/), query -> AST
|
|
178
|
-
- `test_mozsqlparser` - testing [moz-sql-parser](https://pypi.org/project/moz-sql-parser/), full roundtrip as in the docs, query -> JSON
|
|
179
|
-
- `test_sqlglot` - testing [sqlglot](https://github.com/tobymao/sqlglot/), query -> AST
|
|
180
|
-
|
|
181
|
-
To run them on your machine:
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
poetry run pytest tests/benchmark.py
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
```
|
|
188
|
-
------------------------------------------------------------------------------------------- benchmark: 4 tests -------------------------------------------------------------------------------------------
|
|
189
|
-
Name (time in us) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
|
|
190
|
-
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
191
|
-
test_sqloxide 29.6800 (1.0) 50.4300 (1.0) 30.6219 (1.0) 0.7367 (1.0) 30.4900 (1.0) 0.2390 (1.0) 527;716 32,656.3811 (1.0) 9099 1
|
|
192
|
-
test_sqlglot 365.8420 (12.33) 692.8950 (13.74) 377.2422 (12.32) 11.7692 (15.98) 375.7825 (12.32) 4.3145 (18.05) 62;97 2,650.8168 (0.08) 2260 1
|
|
193
|
-
test_sqlparser 1,577.7720 (53.16) 9,751.9699 (193.38) 1,651.5547 (53.93) 355.5511 (482.64) 1,620.7315 (53.16) 30.9200 (129.37) 3;60 605.4901 (0.02) 538 1
|
|
194
|
-
test_mozsqlparser 2,793.8400 (94.13) 12,358.7790 (245.07) 3,091.8519 (100.97) 960.4173 (>1000.0) 2,937.6310 (96.35) 243.3220 (>1000.0) 4;4 323.4308 (0.01) 316 1
|
|
195
|
-
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## Example
|
|
199
|
-
|
|
200
|
-
The `depgraph` example reads a bunch of `.sql` files from disk using glob, and builds a dependency graph of all of the objects using graphviz.
|
|
201
|
-
|
|
202
|
-
```
|
|
203
|
-
poetry run python ./examples/depgraph.py --path {path/to/folder/with/queries}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
## Develop
|
|
207
|
-
|
|
208
|
-
1) Install `rustup`
|
|
209
|
-
|
|
210
|
-
2) `poetry install` will automatically create the venv, compile the package and install it into the venv via the build script.
|
sqloxide-0.1.47/build.py
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
from typing import Any, Dict
|
|
2
|
-
|
|
3
|
-
from setuptools_rust import RustExtension
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def build(setup_kwargs: Dict[str, Any]) -> None:
|
|
7
|
-
setup_kwargs.update(
|
|
8
|
-
{
|
|
9
|
-
"rust_extensions": [RustExtension("sqloxide.sqloxide", "Cargo.toml", debug=False)],
|
|
10
|
-
"zip_safe": False
|
|
11
|
-
}
|
|
12
|
-
)
|
sqloxide-0.1.47/pyproject.toml
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
[tool.poetry]
|
|
2
|
-
name = "sqloxide"
|
|
3
|
-
version = "0.1.47"
|
|
4
|
-
repository = "https://github.com/wseaton/sqloxide"
|
|
5
|
-
license = "MIT"
|
|
6
|
-
description = "Python bindings for sqlparser-rs"
|
|
7
|
-
authors = ["Will Eaton <me@wseaton.com>"]
|
|
8
|
-
build = "build.py"
|
|
9
|
-
classifiers = [
|
|
10
|
-
"Development Status :: 3 - Alpha",
|
|
11
|
-
"Programming Language :: Rust",
|
|
12
|
-
"Topic :: Database",
|
|
13
|
-
"License :: OSI Approved :: MIT License",
|
|
14
|
-
]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
[tool.poetry.dependencies]
|
|
18
|
-
python = ">=3.7,<4.0"
|
|
19
|
-
|
|
20
|
-
[tool.poetry.dev-dependencies]
|
|
21
|
-
setuptools-rust = "^1.1.2"
|
|
22
|
-
pytest = "^7.0.0"
|
|
23
|
-
black = "*"
|
|
24
|
-
isort = "^5.8.0"
|
|
25
|
-
ipython = "^7"
|
|
26
|
-
bump2version = "^1.0.1"
|
|
27
|
-
|
|
28
|
-
[build-system]
|
|
29
|
-
requires = [
|
|
30
|
-
"poetry-core>=1.0.0",
|
|
31
|
-
"setuptools>=41.0.0",
|
|
32
|
-
"wheel",
|
|
33
|
-
"setuptools_rust>=0.10.2",
|
|
34
|
-
]
|
|
35
|
-
build-backend = "poetry.core.masonry.api"
|
sqloxide-0.1.47/setup.cfg
DELETED
sqloxide-0.1.47/setup.py
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
from setuptools import setup
|
|
3
|
-
|
|
4
|
-
packages = ["sqloxide"]
|
|
5
|
-
|
|
6
|
-
package_data = {"": ["*"]}
|
|
7
|
-
|
|
8
|
-
setup_kwargs = {
|
|
9
|
-
"name": "sqloxide",
|
|
10
|
-
"version": "0.1.47",
|
|
11
|
-
"description": "Python bindings for sqlparser-rs",
|
|
12
|
-
"long_description": open("readme.md").read(),
|
|
13
|
-
"long_description_content_type": "text/markdown",
|
|
14
|
-
"author": "Will Eaton",
|
|
15
|
-
"author_email": "me@wseaton.com",
|
|
16
|
-
"maintainer": None,
|
|
17
|
-
"maintainer_email": None,
|
|
18
|
-
"url": "https://github.com/wseaton/sqloxide",
|
|
19
|
-
"packages": packages,
|
|
20
|
-
"package_data": package_data,
|
|
21
|
-
"python_requires": ">=3.8,<4.0",
|
|
22
|
-
}
|
|
23
|
-
from build import *
|
|
24
|
-
|
|
25
|
-
build(setup_kwargs)
|
|
26
|
-
|
|
27
|
-
setup(**setup_kwargs)
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
Cargo.toml
|
|
2
|
-
LICENSE
|
|
3
|
-
MANIFEST.in
|
|
4
|
-
build.py
|
|
5
|
-
pyproject.toml
|
|
6
|
-
setup.py
|
|
7
|
-
sqloxide/__init__.py
|
|
8
|
-
sqloxide.egg-info/PKG-INFO
|
|
9
|
-
sqloxide.egg-info/SOURCES.txt
|
|
10
|
-
sqloxide.egg-info/dependency_links.txt
|
|
11
|
-
sqloxide.egg-info/not-zip-safe
|
|
12
|
-
sqloxide.egg-info/top_level.txt
|
|
13
|
-
src/lib.rs
|
|
14
|
-
src/visitor.rs
|
|
15
|
-
tests/test_sqloxide.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
sqloxide
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|