This release is a pre-release and may not be stable for production use.
duckdb-kql
Run Kusto KQL queries on DuckDB, from Python. Pure Python, no server, no JVM.
Status: pre-alpha. The API is settling but not frozen. Coverage is real and measured — see Coverage — and anything outside it raises rather than guessing.
import duckdb_kql
con = duckdb_kql.connect()
con.sql("CREATE TABLE Logs AS SELECT * FROM 'logs.parquet'")
df = duckdb_kql.df(con, """
Logs
| where Timestamp > ago(1d) and Level == "Error"
| summarize Count = count() by bin(Timestamp, 1h), Component
| sort by Timestamp asc
""")
New here? Start with Getting started.
Install
Install only the layer you need — each adds one dependency.
pip install duckdb-kql # translate KQL to SQL (antlr4 only)
pip install 'duckdb-kql[duckdb]' # ... and run it (+ duckdb)
pip install 'duckdb-kql[kusto]' # ... via the Kusto SDK API (+ pandas)
pip install 'duckdb-kql[all]' # everything
Python 3.10 or newer. Fully typed — the package ships py.typed, so your type
checker sees real types across all three layers (details).
No-runtime-dependency option
Translate at build time and the output has no dependency on this package at all — not even Python. Only your CI machine installs it.
duckdb-kql queries/ -o build/sql/ --check # fails the build if a .sql is stale
Three layers
| Layer | Import | Needs | For |
|---|---|---|---|
| 0 | duckdb_kql |
antlr4-python3-runtime |
KQL text in, DuckDB SQL out. No database involved. |
| 1 | duckdb_kql.engine |
+ duckdb |
Running the translated SQL. |
| 2 | duckdb_kql.kusto |
+ pandas |
A drop-in for azure-kusto-data's KustoClient. |
Importing duckdb_kql never imports duckdb, so Layer 0 genuinely installs and
runs without a database.
Layer 0 — translate
>>> import duckdb_kql
>>> duckdb_kql.to_sql("print x = 1 + 1")
'SELECT (CAST(1 AS BIGINT) + CAST(1 AS BIGINT)) AS "x"'
>>> duckdb_kql.validate("Logs | where Level ==")
[Diagnostic(span=SourceSpan(line=1, column=21), message="mismatched input '<EOF>' ...")]
Layer 1 — execute
import duckdb_kql
con = duckdb_kql.connect("analytics.duckdb") # duckdb.connect + TimeZone=UTC
rel = duckdb_kql.kql(con, "StormEvents | summarize n = count() by State")
rel.fetchall()
Layer 2 — the Kusto SDK interface
For code already written against azure-kusto-data: change the import and the
connection string, leave the queries alone.
from duckdb_kql.kusto import KustoClient, ClientRequestProperties
from duckdb_kql.kusto.helpers import dataframe_from_result_table
client = KustoClient("analytics.duckdb")
props = ClientRequestProperties()
props.set_parameter("state", user_input)
response = client.execute("Storm", """
declare query_parameters(state:string);
StormEvents | where State == state | take 10
""", props)
df = dataframe_from_result_table(response.primary_results[0])
Details, including what it refuses and why:
docs/kusto-client.md.
Query parameters
Never build a query by concatenating strings. Declare parameters and pass values; they are bound as values, so the generated SQL contains no caller-controlled text at all.
duckdb_kql.kql(con, """
declare query_parameters(state:string);
StormEvents | where State == state
""", {"state": user_input}) # safe whatever user_input contains
Coverage
Measured against the real KQL engine (the Kusto Emulator), not asserted.
| Doc-corpus cases matching ground truth | 245 of 1036 (0 mismatches) |
| Azure Monitor's published KQL subset | 114 / 119 (96%) |
| Tabular operators | 15 / 41 |
| Scalar functions / aggregates / binary operators | 110 / 19 / 33 |
Supported operators: where, project, project-away, project-rename,
extend, summarize, join, mv-expand, distinct, count,
sort / order by, take / limit, render; sources print, datatable,
range, and tables; plus let and declare query_parameters.
The support matrix lists every operator, function and type — supported or not — with the known limitations and Kusto discrepancies for each. It is generated from the translator's own registries and probed at build time, so it cannot claim support that does not exist.
Why refusal matters
The failure mode this project is built to avoid is not a crash — it is a query
that runs and returns a different answer than Kusto would. KQL and SQL look
alike in places where they behave differently: % is a mathematical modulo in
KQL and takes the dividend's sign in DuckDB; extract's arguments are in the
opposite order; KQL weeks start on Sunday; make_datetime truncates where
make_timestamp rounds. Every mapping is verified against the emulator rather
than inferred from documentation, and where an honest mapping does not exist —
hash_xxhash64, datetime_part('nanosecond') — the answer is an error, not an
approximation.
Documentation
| Document | What it covers |
|---|---|
| Getting started | Install, first query, the three layers |
| KQL support matrix | Every operator and function, supported or not, each with its gotchas |
| Build-time CLI | Translating .kql to .sql in CI, to avoid a runtime dependency |
| API reference | Every public function and type |
| Kusto SDK compatibility | What Layer 2 implements, no-ops, and refuses |
| Azure Monitor profile | Coverage against a published KQL subset |
docs/TRANSLATION.md |
Normative KQL→DuckDB mapping spec (R1–R12) |
docs/implementation-plan.md |
Architecture and milestones |
docs/test-plan.md |
Corpus harvesting, oracle, divergence catalog |
docs/kql-on-duckdb-landscape.md |
Survey of existing KQL-on-DuckDB work |
docs/implementation-options.md |
Six approaches considered, with the chosen one |
docs/m0-grammar-spike.md |
Grammar viability result |
docs/frequency-scan-results.md |
What KQL constructs actually get used |
docs/licensing.md |
Third-party licensing review |
| demo/ | Notebook tour of all three layers, with outputs |
| CONTRIBUTING.md | How to add a mapping, and when not to |
| SECURITY.md | Reporting vulnerabilities; what is in scope |
| Releases | What changed, and when |
Design
The parser is generated by ANTLR from Microsoft's own Apache-2.0 KQL grammar. Translation targets DuckDB SQL as a chain of CTEs — one per KQL operator — so DuckDB does all execution and optimization, and the generated SQL stays readable.
Development
pip install -e ".[dev]"
pytest
tools/regen_parser.sh # regenerate the parser (maintainers; needs Java)
The acceptance suite compares against the Kusto Emulator, which runs in Docker;
see docs/oracle-harness.md. It is a development and
CI tool only — never a runtime dependency, and never redistributed.
License
MIT — see LICENSE. Vendors an Apache-2.0 grammar and MIT-licensed
documentation samples; see THIRD-PARTY-NOTICES.md.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file duckdb_kql-0.0.1.dev2.tar.gz.
File metadata
- Download URL: duckdb_kql-0.0.1.dev2.tar.gz
- Upload date:
- Size: 524.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55dbb87d19c106a5e96f7d92baac1123d00d2c73ccddcb41295f6d563af12ee3
|
|
| MD5 |
639a86c6c6420c2c0baac01eabb445eb
|
|
| BLAKE2b-256 |
6511623821d8dcd27261d79eb91e853ae01748c11a29ae606515b3d7e1198003
|
Provenance
The following attestation bundles were made for duckdb_kql-0.0.1.dev2.tar.gz:
Publisher:
release.yml on mmaitre314/duckdb-kql
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duckdb_kql-0.0.1.dev2.tar.gz -
Subject digest:
55dbb87d19c106a5e96f7d92baac1123d00d2c73ccddcb41295f6d563af12ee3 - Sigstore transparency entry: 2457936374
- Sigstore integration time:
-
Permalink:
mmaitre314/duckdb-kql@4130313f12929ada0fe328fb9abf322812c71958 -
Branch / Tag:
refs/tags/v0.0.1.dev2 - Owner: https://github.com/mmaitre314
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4130313f12929ada0fe328fb9abf322812c71958 -
Trigger Event:
release
-
Statement type:
File details
Details for the file duckdb_kql-0.0.1.dev2-py3-none-any.whl.
File metadata
- Download URL: duckdb_kql-0.0.1.dev2-py3-none-any.whl
- Upload date:
- Size: 318.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52fca3c0d7c6e22e8b6026a4c4a4993df1384e314c7f21888a8bac2035c347e1
|
|
| MD5 |
07f087156a98e77325f1c6a588d81dfc
|
|
| BLAKE2b-256 |
c2553d2b12cc85fc0b4aec94378e5072c9ced159d1bd35e842c314a1651a0c2f
|
Provenance
The following attestation bundles were made for duckdb_kql-0.0.1.dev2-py3-none-any.whl:
Publisher:
release.yml on mmaitre314/duckdb-kql
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duckdb_kql-0.0.1.dev2-py3-none-any.whl -
Subject digest:
52fca3c0d7c6e22e8b6026a4c4a4993df1384e314c7f21888a8bac2035c347e1 - Sigstore transparency entry: 2457936497
- Sigstore integration time:
-
Permalink:
mmaitre314/duckdb-kql@4130313f12929ada0fe328fb9abf322812c71958 -
Branch / Tag:
refs/tags/v0.0.1.dev2 - Owner: https://github.com/mmaitre314
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4130313f12929ada0fe328fb9abf322812c71958 -
Trigger Event:
release
-
Statement type: