trino-sql-validator 0.1.0__tar.gz → 0.2.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.
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/AGENTS.md +14 -2
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/CHANGELOG.md +14 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/Cargo.lock +13 -1
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/Cargo.toml +2 -2
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/PKG-INFO +18 -1
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/README.md +17 -0
- trino_sql_validator-0.2.0/plan/functions-validation.md +109 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/plan/plan.md +5 -1
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/plan/roadmap.md +6 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/pyproject.toml +1 -1
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/python/trino_sql_validator/__init__.py +47 -7
- trino_sql_validator-0.2.0/python/trino_sql_validator/_native.pyi +15 -0
- trino_sql_validator-0.2.0/src/functions.rs +503 -0
- trino_sql_validator-0.2.0/src/lib.rs +262 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/tests/test_validator.py +66 -1
- trino_sql_validator-0.2.0/tools/extract_functions.py +196 -0
- trino_sql_validator-0.1.0/python/trino_sql_validator/_native.pyi +0 -11
- trino_sql_validator-0.1.0/src/lib.rs +0 -159
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/.github/workflows/ci.yml +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/.github/workflows/release.yml +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/.gitignore +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/LICENSE +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/python/trino_sql_validator/py.typed +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/rust-toolchain.toml +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/src/dialects/mod.rs +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/tests/conftest.py +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/tests/fixtures/ddl_multi.sql +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/tests/fixtures/empty.sql +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/tests/fixtures/invalid_one.sql +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/tests/fixtures/trino_specific.sql +0 -0
- {trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/tests/fixtures/valid_multi.sql +0 -0
|
@@ -10,10 +10,12 @@ Built with PyO3 + maturin. Distributed as wheels + sdist on PyPI.
|
|
|
10
10
|
Layout:
|
|
11
11
|
- `src/` — Rust crate (the compiled `_native` extension). `src/lib.rs` holds the
|
|
12
12
|
`#[pymodule] fn _native`. `src/dialects/` holds the `TrinoDialect`/`SqlDialect`.
|
|
13
|
+
`src/functions.rs` is the **generated** Trino function catalog (`@generated`).
|
|
13
14
|
- `python/trino_sql_validator/` — pure-Python public API (`__init__.py`), type
|
|
14
15
|
stubs (`_native.pyi`), `py.typed`.
|
|
15
16
|
- `tests/` — pytest suite for the public API (+ `.sql` fixtures).
|
|
16
|
-
- `plan/` — planning docs (`plan.md`, `roadmap.md`).
|
|
17
|
+
- `plan/` — planning docs (`plan.md`, `roadmap.md`, `functions-validation.md`).
|
|
18
|
+
- `tools/extract_functions.py` — regenerates `src/functions.rs` from Trino docs.
|
|
17
19
|
|
|
18
20
|
## Source of truth
|
|
19
21
|
|
|
@@ -47,6 +49,10 @@ mypy python/trino_sql_validator
|
|
|
47
49
|
# Produce distributable artifacts
|
|
48
50
|
maturin build --release # wheels
|
|
49
51
|
maturin sdist # source distribution
|
|
52
|
+
|
|
53
|
+
# Regenerate the Trino function catalog from upstream docs
|
|
54
|
+
python tools/extract_functions.py # fetch from GitHub
|
|
55
|
+
python tools/extract_functions.py --docs-path /path/to/trino/docs/src/main/sphinx/functions # local checkout
|
|
50
56
|
```
|
|
51
57
|
|
|
52
58
|
## Conventions / rules
|
|
@@ -86,4 +92,10 @@ maturin sdist # source distribution
|
|
|
86
92
|
`sqlparser-rs` does syntax, not semantics. It accepts some SQL Trino rejects at
|
|
87
93
|
semantic analysis and can reject exotic Trino-specific DDL. This is a documented,
|
|
88
94
|
accepted limitation (see README + plan/roadmap.md). Do not "fix" by loosening the
|
|
89
|
-
dialect to Generic by default for `dialect="trino"`.
|
|
95
|
+
dialect to Generic by default for `dialect="trino"`.
|
|
96
|
+
|
|
97
|
+
Function validation (`ValidationResult.warnings`) checks only **name existence**
|
|
98
|
+
against the documented Trino catalog (`src/functions.rs`); it does not check
|
|
99
|
+
arity or argument types — that is semantic analysis, out of scope for a syntax
|
|
100
|
+
validator. False positives are possible if a deployed Trino has plugin functions
|
|
101
|
+
beyond the docs; warnings are non-fatal by design.
|
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.0] - 2026-09-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Trino **function-name validation**: for `dialect="trino"`, `validate()` and
|
|
13
|
+
`validate_file()` now walk the parsed AST and flag calls to functions that are
|
|
14
|
+
not in the documented Trino catalog (e.g. a misspelled `round`).
|
|
15
|
+
- New `ValidationResult.warnings` (`FunctionWarning`) and convenience property
|
|
16
|
+
`ValidationResult.unknown_functions`. Unknown functions are non-fatal warnings
|
|
17
|
+
— `valid` stays `True` because the syntax is fine.
|
|
18
|
+
- `tools/extract_functions.py` — regenerates the embedded catalog
|
|
19
|
+
(`src/functions.rs`, 459 canonical names) from the Trino docs; committed so
|
|
20
|
+
builds stay offline and deterministic.
|
|
21
|
+
|
|
8
22
|
## [0.1.0] - 2026-09-06
|
|
9
23
|
|
|
10
24
|
Initial release.
|
|
@@ -235,6 +235,18 @@ checksum = "13c6d1b651dc4edf07eead2a0c6c78016ce971bc2c10da5266861b13f25e7cec"
|
|
|
235
235
|
dependencies = [
|
|
236
236
|
"log",
|
|
237
237
|
"recursive",
|
|
238
|
+
"sqlparser_derive",
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
[[package]]
|
|
242
|
+
name = "sqlparser_derive"
|
|
243
|
+
version = "0.5.0"
|
|
244
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
245
|
+
checksum = "a6dd45d8fc1c79299bfbb7190e42ccbbdf6a5f52e4a6ad98d92357ea965bd289"
|
|
246
|
+
dependencies = [
|
|
247
|
+
"proc-macro2",
|
|
248
|
+
"quote",
|
|
249
|
+
"syn",
|
|
238
250
|
]
|
|
239
251
|
|
|
240
252
|
[[package]]
|
|
@@ -269,7 +281,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
269
281
|
|
|
270
282
|
[[package]]
|
|
271
283
|
name = "trino-sql-validator"
|
|
272
|
-
version = "0.
|
|
284
|
+
version = "0.2.0"
|
|
273
285
|
dependencies = [
|
|
274
286
|
"pyo3",
|
|
275
287
|
"regex",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "trino-sql-validator"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Fast Trino SQL syntax validator — Python library backed by Rust"
|
|
6
6
|
license = "MIT"
|
|
@@ -16,7 +16,7 @@ crate-type = ["cdylib"]
|
|
|
16
16
|
|
|
17
17
|
[dependencies]
|
|
18
18
|
pyo3 = { version = "0.29", features = ["abi3-py310"] }
|
|
19
|
-
sqlparser = { version = "0.62", default-features = true }
|
|
19
|
+
sqlparser = { version = "0.62", default-features = true, features = ["visitor"] }
|
|
20
20
|
regex = "1"
|
|
21
21
|
|
|
22
22
|
[dev-dependencies]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trino-sql-validator
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -60,6 +60,23 @@ Invalid SQL (and files containing it) is returned as a `ValidationResult`;
|
|
|
60
60
|
it is **not** raised as an exception. Only genuine misuse (unknown dialect,
|
|
61
61
|
unreadable file) raises.
|
|
62
62
|
|
|
63
|
+
### Function-name warnings
|
|
64
|
+
|
|
65
|
+
For `dialect="trino"`, `validate()` also checks that every function called in
|
|
66
|
+
the SQL exists in the documented Trino catalog. Unknown names are reported as
|
|
67
|
+
non-fatal `warnings` — `valid` stays `True` because syntax is fine:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
result = validate("SELECT marh(1.5)") # round() misspelled
|
|
71
|
+
assert result.valid
|
|
72
|
+
print(result.warnings) # (FunctionWarning(name='marh', line=1, column=8),)
|
|
73
|
+
print(result.unknown_functions) # ['marh']
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The catalog is auto-generated from the Trino docs and only checks *name
|
|
77
|
+
existence*, not argument count or types. `hive`/`generic` dialects skip this
|
|
78
|
+
check.
|
|
79
|
+
|
|
63
80
|
### Dialects
|
|
64
81
|
|
|
65
82
|
- `"trino"` (default) — Trino-flavored with a custom override tuned for
|
|
@@ -33,6 +33,23 @@ Invalid SQL (and files containing it) is returned as a `ValidationResult`;
|
|
|
33
33
|
it is **not** raised as an exception. Only genuine misuse (unknown dialect,
|
|
34
34
|
unreadable file) raises.
|
|
35
35
|
|
|
36
|
+
### Function-name warnings
|
|
37
|
+
|
|
38
|
+
For `dialect="trino"`, `validate()` also checks that every function called in
|
|
39
|
+
the SQL exists in the documented Trino catalog. Unknown names are reported as
|
|
40
|
+
non-fatal `warnings` — `valid` stays `True` because syntax is fine:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
result = validate("SELECT marh(1.5)") # round() misspelled
|
|
44
|
+
assert result.valid
|
|
45
|
+
print(result.warnings) # (FunctionWarning(name='marh', line=1, column=8),)
|
|
46
|
+
print(result.unknown_functions) # ['marh']
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The catalog is auto-generated from the Trino docs and only checks *name
|
|
50
|
+
existence*, not argument count or types. `hive`/`generic` dialects skip this
|
|
51
|
+
check.
|
|
52
|
+
|
|
36
53
|
### Dialects
|
|
37
54
|
|
|
38
55
|
- `"trino"` (default) — Trino-flavored with a custom override tuned for
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Function-name validation for Trino
|
|
2
|
+
|
|
3
|
+
Status: implemented for v0.2.0.
|
|
4
|
+
Source of truth for the catalog: Trino docs at
|
|
5
|
+
`docs/src/main/sphinx/functions/` in `trinodb/trino` (MyST, current format).
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Catch a common class of error today's validator ships past: a syntactically
|
|
10
|
+
well-formed SQL statement that calls a function Trino does not provide (typo,
|
|
11
|
+
wrong engine, e.g. `SELECT marh(x)` vs `round(x)`). `sqlparser-rs` treats any
|
|
12
|
+
`name(...)` as a function call, so it never flags unknown names.
|
|
13
|
+
|
|
14
|
+
## Where the catalog comes from
|
|
15
|
+
|
|
16
|
+
The `functions/` tree has 35 topic files (`aggregate.md`, `string.md`, ...) plus
|
|
17
|
+
`list.md`. A function name appears in two machine-greppable forms:
|
|
18
|
+
|
|
19
|
+
- `:::{function} name(args) -> type` directives (the authoritative signatures,
|
|
20
|
+
one per overload).
|
|
21
|
+
- `{func}\`name\`` cross-references in `list.md` (the alphabetical catalog of
|
|
22
|
+
every documented function).
|
|
23
|
+
|
|
24
|
+
Our catalog = **union** of directive names across all topic files AND `{func}`
|
|
25
|
+
names in `list.md`. Current Trino docs yield **459 canonical names**
|
|
26
|
+
(measured 2026-09-06). This catches all of `count/sum/avg/min/max/...` plus
|
|
27
|
+
Trino-only functions.
|
|
28
|
+
|
|
29
|
+
Cast/keywords that sqlparser models specially (`cast`, `try`, `if`, `coalesce`)
|
|
30
|
+
never reach the function walker, so they need no special-casing.
|
|
31
|
+
|
|
32
|
+
## Algorithm (time-optimal)
|
|
33
|
+
|
|
34
|
+
1. **No extra cost on invalid SQL.** Parse first. If `Parser::parse_sql` fails,
|
|
35
|
+
return the existing error today and skip function walking entirely.
|
|
36
|
+
2. **Single linear AST walk.** For a successful parse, walk every statement once
|
|
37
|
+
with `sqlparser`'s `visit_expressions` (`feature = "visitor"`). For each
|
|
38
|
+
`Expr::Function`, look up the *last* `Ident` of its `ObjectName` in a static
|
|
39
|
+
`HashSet<&'static str>` of canonical names. O(functions-in-query), O(1) lookup.
|
|
40
|
+
3. **Exact line/column for free.** `Ident.span` gives byte-exact start
|
|
41
|
+
`Location` — no regex, no string scanning (unlike the error-location path).
|
|
42
|
+
4. **Static, lock-free data.** The name list is committed to `src/functions.rs`
|
|
43
|
+
as a `LazyLock<HashSet>`. Built once by `tools/extract_functions.py` (a
|
|
44
|
+
dev-only script that pulls the docs). No runtime/build-time network.
|
|
45
|
+
|
|
46
|
+
The name list only changes when Trino adds/removes functions (a release-scale
|
|
47
|
+
event). Committing the generated list keeps the crate offline-buildable and
|
|
48
|
+
deterministic; regenerating is opt-in (`python tools/extract_functions.py`).
|
|
49
|
+
|
|
50
|
+
## Behavior / semantics
|
|
51
|
+
|
|
52
|
+
- Only `dialect == "trino"` checks functions. `hive`/`generic` stay as-is
|
|
53
|
+
(permissive escapes hatch; their function surface differs from Trino's).
|
|
54
|
+
- A call to an unknown function does **not** make the statement "invalid" in the
|
|
55
|
+
existing `valid: bool` sense — that stays strictly about syntax. It is
|
|
56
|
+
reported as a `warning` alongside the syntax result. This preserves the
|
|
57
|
+
"bad syntax is a value, not an exception" contract on parse errors and keeps
|
|
58
|
+
the truly-syntactic check peer-reviewable.
|
|
59
|
+
- Stopping early: the walker records unknown names and their locations and can
|
|
60
|
+
short-circuit (via `ControlFlow::Break`) to avoid scanning the rest of a large
|
|
61
|
+
script once already-warned — bounded memory, unchanged O(n) time.
|
|
62
|
+
- Fully-qualified calls (`schema.round(x)`): match on the bare function name;
|
|
63
|
+
the leading qualifier is a schema reference, not part of the function.
|
|
64
|
+
|
|
65
|
+
## API shape
|
|
66
|
+
|
|
67
|
+
`ValidationResult` gains an optional `warnings: tuple[FunctionWarning, ...]`.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
@dataclass(frozen=True)
|
|
71
|
+
class FunctionWarning:
|
|
72
|
+
name: str
|
|
73
|
+
line: int | None = None
|
|
74
|
+
column: int | None = None
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Rust `validate`/`validate_file` return a 6-tuple
|
|
78
|
+
`(valid, statement_count, error_message, line, column, warnings)` where
|
|
79
|
+
`warnings` is a list of `(name, line, column)` triples. pyo3 converts the
|
|
80
|
+
plain `Vec<(String, Option<usize>, Option<usize>)>` to a Python list of tuples
|
|
81
|
+
directly — no serialization dependency needed.
|
|
82
|
+
|
|
83
|
+
## Files
|
|
84
|
+
|
|
85
|
+
- `tools/extract_functions.py` — dev-only doc parser → `src/functions.rs`.
|
|
86
|
+
- `src/functions.rs` — generated static catalog + `is_known_function()`.
|
|
87
|
+
- `src/lib.rs` — enable `visitor` feature usage; walk AST after successful parse.
|
|
88
|
+
- `python/trino_sql_validator/__init__.py` — `FunctionWarning`, `warnings` field.
|
|
89
|
+
- `python/trino_sql_validator/_native.pyi` — updated tuple type.
|
|
90
|
+
- `tests/test_validator.py` — unknown/known/qualified/trino-only cases.
|
|
91
|
+
- `tests/fixtures/` — sample queries with/without unknown functions.
|
|
92
|
+
|
|
93
|
+
## Verification
|
|
94
|
+
|
|
95
|
+
- `cargo test` (Rust unit tests for the walker + catalog sanity).
|
|
96
|
+
- `pytest` — unknown function → warning with correct line/column; known &
|
|
97
|
+
multi-argument & qualified calls → no warning; `dialect="generic"` → no
|
|
98
|
+
function checks; invalid SQL → parse error, no warnings.
|
|
99
|
+
- `cargo fmt --check`, `cargo clippy --all-targets -- -D warnings`, `ruff`,
|
|
100
|
+
`mypy` all stay green.
|
|
101
|
+
|
|
102
|
+
## Trade-offs / limits (documented, not "fixed" here)
|
|
103
|
+
|
|
104
|
+
- Catalog is the *documented* function set. Trino deploys may have
|
|
105
|
+
connector/plugin-specific functions beyond docs → possible false positives for
|
|
106
|
+
exotic setups. Warnings are non-fatal and per-dialect-trino, so this is safe.
|
|
107
|
+
- We validate name existence, not arity/type (sqlparser does not model Trino's
|
|
108
|
+
function signatures beyond parse; arity/type checking is semantic and out of
|
|
109
|
+
scope for a syntax validator). See roadmap for `trino-parser` on the far end.
|
|
@@ -43,12 +43,16 @@ trino-sql-validator/
|
|
|
43
43
|
├── AGENTS.md
|
|
44
44
|
├── plan/
|
|
45
45
|
│ ├── plan.md # this file
|
|
46
|
-
│
|
|
46
|
+
│ ├── roadmap.md # future work (Trino-exact grammar, etc.)
|
|
47
|
+
│ └── functions-validation.md # function-name validation plan (v0.2.0)
|
|
47
48
|
├── src/
|
|
48
49
|
│ ├── lib.rs # #[pymodule] entry point
|
|
50
|
+
│ ├── functions.rs # GENERATED Trino function catalog (459 names)
|
|
49
51
|
│ ├── dialects/
|
|
50
52
|
│ │ └── mod.rs # TrinoDialect (impl Dialect trait)
|
|
51
53
|
│ └── error.rs # Mapping ParserError -> Python exception info
|
|
54
|
+
├── tools/
|
|
55
|
+
│ └── extract_functions.py # regenerates src/functions.rs from Trino docs
|
|
52
56
|
├── python/trino_sql_validator/
|
|
53
57
|
│ ├── __init__.py # pure-Python public API (re-exports, __all__)
|
|
54
58
|
│ ├── _validator.pyi # type stubs referencing the compiled _native module
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
- Wheels for Linux (x86_64, aarch64), macOS (arm64, x86_64), Windows (x86_64).
|
|
7
7
|
- PyPI publishing via GitHub Actions + Trusted Publishing.
|
|
8
8
|
|
|
9
|
+
## v0.2.0
|
|
10
|
+
- Trino function-name validation: AST walk via `sqlparser::visitor`; unknown
|
|
11
|
+
functions are surfaced as non-fatal `FunctionWarning` in `ValidationResult.warnings`.
|
|
12
|
+
Catalog (459 names) auto-generated from Trino docs and committed in `src/functions.rs`.
|
|
13
|
+
Only active for `dialect="trino"`.
|
|
14
|
+
|
|
9
15
|
## v0.2.x — clearer Trino cursor
|
|
10
16
|
- Enrich `TrinoDialect` overrides for commonly-mis-parsed Trino-specific syntax:
|
|
11
17
|
- `EXECUTE IMMEDIATE`, `CALL` signatures
|
{trino_sql_validator-0.1.0 → trino_sql_validator-0.2.0}/python/trino_sql_validator/__init__.py
RENAMED
|
@@ -15,15 +15,23 @@ from ._native import validate_file as _native_validate_file
|
|
|
15
15
|
|
|
16
16
|
__all__ = [
|
|
17
17
|
"Error",
|
|
18
|
+
"FunctionWarning",
|
|
18
19
|
"ValidationResult",
|
|
19
20
|
"__version__",
|
|
20
21
|
"validate",
|
|
21
22
|
"validate_file",
|
|
22
23
|
]
|
|
23
24
|
|
|
24
|
-
_NativeResult = tuple[
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
_NativeResult = tuple[
|
|
26
|
+
bool,
|
|
27
|
+
int,
|
|
28
|
+
str | None,
|
|
29
|
+
int | None,
|
|
30
|
+
int | None,
|
|
31
|
+
tuple[tuple[str, int | None, int | None], ...],
|
|
32
|
+
]
|
|
33
|
+
"""Tuple shape returned by the Rust core: (valid, statement_count, error
|
|
34
|
+
message, line, column, warnings)."""
|
|
27
35
|
|
|
28
36
|
__version__ = _native.__version__
|
|
29
37
|
|
|
@@ -44,6 +52,25 @@ class Error:
|
|
|
44
52
|
return self.message
|
|
45
53
|
|
|
46
54
|
|
|
55
|
+
@dataclass(frozen=True)
|
|
56
|
+
class FunctionWarning:
|
|
57
|
+
"""A call to a function that is not in the documented Trino catalog.
|
|
58
|
+
|
|
59
|
+
``valid`` stays ``True`` for such statements — name checks are advisory,
|
|
60
|
+
not syntax errors (a deployed Trino may still offer plugin functions that
|
|
61
|
+
the docs do not list).
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
name: str
|
|
65
|
+
line: int | None = None
|
|
66
|
+
column: int | None = None
|
|
67
|
+
|
|
68
|
+
def __str__(self) -> str:
|
|
69
|
+
if self.line is not None and self.column is not None:
|
|
70
|
+
return f"unknown function '{self.name}' at line {self.line}, column {self.column}"
|
|
71
|
+
return f"unknown function '{self.name}'"
|
|
72
|
+
|
|
73
|
+
|
|
47
74
|
@dataclass(frozen=True)
|
|
48
75
|
class ValidationResult:
|
|
49
76
|
"""Structured outcome of validating one or more SQL statements."""
|
|
@@ -51,14 +78,26 @@ class ValidationResult:
|
|
|
51
78
|
valid: bool
|
|
52
79
|
statement_count: int
|
|
53
80
|
error: Error | None = None
|
|
81
|
+
warnings: tuple[FunctionWarning, ...] = ()
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def unknown_functions(self) -> list[str]:
|
|
85
|
+
"""Function names used in the SQL that have no Trino documentation
|
|
86
|
+
entry, in order of appearance."""
|
|
87
|
+
return [warning.name for warning in self.warnings]
|
|
54
88
|
|
|
55
89
|
def __bool__(self) -> bool:
|
|
56
90
|
return self.valid
|
|
57
91
|
|
|
58
92
|
def __repr__(self) -> str:
|
|
59
|
-
if self.valid:
|
|
60
|
-
return f"<ValidationResult valid=
|
|
61
|
-
|
|
93
|
+
if not self.valid:
|
|
94
|
+
return f"<ValidationResult valid=False error={self.error!r}>"
|
|
95
|
+
if self.warnings:
|
|
96
|
+
return (
|
|
97
|
+
f"<ValidationResult valid=True statements={self.statement_count} "
|
|
98
|
+
f"warnings={len(self.warnings)}>"
|
|
99
|
+
)
|
|
100
|
+
return f"<ValidationResult valid=True statements={self.statement_count}>"
|
|
62
101
|
|
|
63
102
|
|
|
64
103
|
_SUPPORTED_DIALECTS = ("trino", "hive", "generic")
|
|
@@ -69,11 +108,12 @@ def _validate(dialect: Dialect, call: _NativeResult) -> ValidationResult:
|
|
|
69
108
|
raise ValueError(
|
|
70
109
|
f"unknown dialect {dialect!r}; expected one of {_SUPPORTED_DIALECTS}"
|
|
71
110
|
)
|
|
72
|
-
valid, statement_count, message, line, column = call
|
|
111
|
+
valid, statement_count, message, line, column, warnings = call
|
|
73
112
|
return ValidationResult(
|
|
74
113
|
valid=bool(valid),
|
|
75
114
|
statement_count=int(statement_count),
|
|
76
115
|
error=Error(message=message, line=line, column=column) if message else None,
|
|
116
|
+
warnings=tuple(FunctionWarning(name=name, line=wl, column=wc) for name, wl, wc in warnings),
|
|
77
117
|
)
|
|
78
118
|
|
|
79
119
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import TypeAlias
|
|
2
|
+
|
|
3
|
+
__version__: str
|
|
4
|
+
|
|
5
|
+
_Warning: TypeAlias = tuple[str, int | None, int | None]
|
|
6
|
+
|
|
7
|
+
def validate(
|
|
8
|
+
sql: str,
|
|
9
|
+
dialect: str = "trino",
|
|
10
|
+
) -> tuple[bool, int, str | None, int | None, int | None, tuple[_Warning, ...]]: ...
|
|
11
|
+
|
|
12
|
+
def validate_file(
|
|
13
|
+
path: str,
|
|
14
|
+
dialect: str = "trino",
|
|
15
|
+
) -> tuple[bool, int, str | None, int | None, int | None, tuple[_Warning, ...]]: ...
|