diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0cad7f4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +## 0.3.0 (unreleased) + +### Changed + +- Upgraded to ggsql Rust crate v0.3.0. See the [upstream changelog](https://github.com/posit-dev/ggsql/blob/main/CHANGELOG.md) for details on new features, bug fixes, and breaking changes. +- Replaced polars with Arrow (via pyarrow) for the Rust↔Python data bridge. `DuckDBReader.execute_sql()`, `Spec.data()`, `Spec.layer_data()`, and `Spec.stat_data()` now return `pyarrow.Table` instead of `polars.DataFrame`. `DuckDBReader.register()` accepts `pyarrow.Table` (polars DataFrames are still accepted via automatic conversion). Custom readers returning polars DataFrames from `execute_sql()` continue to work without changes. +- Runtime dependency changed from `polars` to `pyarrow`. + +## 0.2.7 + +Synced with ggsql Rust crate v0.2.7. + +## 0.1.4 + +Initial release. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7485e21 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,43 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What This Is + +Python bindings for the [ggsql](https://github.com/posit-dev/ggsql) Rust crate — a SQL extension for declarative data visualization. The Rust crate handles parsing, validation, and Vega-Lite generation; this repo wraps it via PyO3/maturin and adds a Python-native API layer (`render_altair()`, `VegaLiteWriter.render_chart()`). + +## Build & Development + +Requires Rust toolchain and Python 3.10+. Uses `uv` for Python dependency management. + +```bash +# Install dev dependencies +uv sync + +# Build the Rust extension in-place (required after any Rust changes) +uv run maturin develop + +# Run all tests +uv run pytest tests/ -v + +# Run a single test +uv run pytest tests/test_ggsql.py::TestValidate::test_valid_query_with_visualise -v + +# Rust checks (CI runs these) +cargo fmt -- --check +cargo clippy -- -D warnings +``` + +To pick up a new version of the upstream `ggsql` Rust crate, bump its version in `Cargo.toml` and re-run `maturin develop`. + +## Architecture + +**Rust layer** (`src/lib.rs`): Single-file PyO3 module exposing `DuckDBReader`, `VegaLiteWriter`, `Validated`, `Spec`, `validate()`, and `execute()` to Python. Data crosses the Rust↔Python boundary via Arrow IPC serialization (`arrow::ipc::StreamWriter`/`StreamReader` on the Rust side, `pyarrow.ipc` on the Python side). The `py_to_df` helper accepts any object that `pyarrow.table()` can convert (pyarrow Tables, polars DataFrames, pandas DataFrames, etc.). Custom Python readers are bridged to the Rust `Reader` trait via `PyReaderBridge`; native readers (currently just `DuckDBReader`) use a fast path that skips the bridge (see `try_native_readers!` macro). + +**Python layer** (`python/ggsql/__init__.py`): Re-exports Rust bindings and adds `render_altair()` (convenience function that registers a DataFrame, executes, and returns an Altair chart) and a Python `VegaLiteWriter` wrapper that adds `render_chart()`. The `_json_to_altair_chart()` helper dispatches to the correct Altair chart class based on the Vega-Lite spec structure (layer, facet, concat, etc.). + +**Key design pattern**: Two-stage API — `reader.execute(query) -> Spec`, then `writer.render(spec) -> str` or `writer.render_chart(spec) -> AltairChart`. The `render_altair()` shortcut collapses both stages. + +## `.cargo/config.toml` + +Sets `GGSQL_SKIP_GENERATE=1` so tree-sitter uses its pre-generated parser rather than regenerating from `grammar.js`. Don't remove this. diff --git a/Cargo.lock b/Cargo.lock index b00f3db..7ef994a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,12 +42,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -57,56 +51,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - [[package]] name = "anyhow" version = "1.0.102" @@ -122,15 +66,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "ar_archive_writer" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" -dependencies = [ - "object", -] - [[package]] name = "arbitrary" version = "1.4.2" @@ -140,27 +75,6 @@ dependencies = [ "derive_arbitrary", ] -[[package]] -name = "argminmax" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f13d10a41ac8d2ec79ee34178d61e6f47a29c2edfe7ef1721c7383b0359e65" -dependencies = [ - "num-traits", -] - -[[package]] -name = "array-init-cursor" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed51fe0f224d1d4ea768be38c51f9f831dee9d05c163c11fba0b8c44387b1fc3" - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - [[package]] name = "arrayvec" version = "0.7.6" @@ -340,51 +254,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "atoi" version = "2.0.0" @@ -394,15 +263,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "atoi_simd" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a49e05797ca52e312a0c658938b7d00693ef037799ef7187678f212d7684cf" -dependencies = [ - "debug_unsafe", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -421,34 +281,11 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bincode" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "bincode_derive", - "serde", - "unty", -] - -[[package]] -name = "bincode_derive" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" -dependencies = [ - "virtue", -] - [[package]] name = "bitflags" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" -dependencies = [ - "serde_core", -] [[package]] name = "bitvec" @@ -462,29 +299,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "blake3" -version = "1.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "cpufeatures 0.3.0", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "borsh" version = "1.6.1" @@ -509,12 +323,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "boxcar" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e" - [[package]] name = "bumpalo" version = "3.20.2" @@ -549,34 +357,11 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "bytemuck" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -dependencies = [ - "serde", -] [[package]] name = "cast" @@ -584,15 +369,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - [[package]] name = "cc" version = "1.2.60" @@ -626,67 +402,10 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", - "serde", "wasm-bindgen", "windows-link", ] -[[package]] -name = "chrono-tz" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" -dependencies = [ - "chrono", - "phf 0.12.1", -] - -[[package]] -name = "clap" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "clap_lex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - [[package]] name = "comfy-table" version = "7.1.2" @@ -698,30 +417,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "compact_str" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "serde", - "static_assertions", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "const-random" version = "0.1.18" @@ -763,46 +458,12 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "constant_time_eq" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -812,65 +473,12 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - [[package]] name = "crunchy" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "csscolorparser" version = "0.8.3" @@ -883,12 +491,6 @@ dependencies = [ "uncased", ] -[[package]] -name = "debug_unsafe" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eed2c4702fa172d1ce21078faa7c5203e69f5394d48cc436d25928394a867a2" - [[package]] name = "derive_arbitrary" version = "1.4.2" @@ -900,16 +502,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -939,18 +531,6 @@ dependencies = [ "strum 0.27.2", ] -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - [[package]] name = "equivalent" version = "1.0.2" @@ -967,33 +547,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "ethnum" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - [[package]] name = "fallible-iterator" version = "0.3.0" @@ -1006,12 +559,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "fast-float2" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55" - [[package]] name = "fast-srgb8" version = "1.0.0" @@ -1062,33 +609,12 @@ dependencies = [ "zlib-rs", ] -[[package]] -name = "float-cmp" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1098,37 +624,12 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs4" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" -dependencies = [ - "rustix", - "windows-sys 0.59.0", -] - [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" version = "0.3.32" @@ -1145,34 +646,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - [[package]] name = "futures-io" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "futures-sink" version = "0.3.32" @@ -1191,10 +670,8 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ - "futures-channel", "futures-core", "futures-io", - "futures-macro", "futures-sink", "futures-task", "memchr", @@ -1202,16 +679,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -1254,20 +721,17 @@ dependencies = [ [[package]] name = "ggsql" -version = "0.2.7" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96d026414adf7123525777df11b498aea6ad4da1905607a0f435e186e363724" +checksum = "ceed7543608a557d898d7589d29433784d2b189e1752ec62535529dd682f2b1c" dependencies = [ - "anyhow", "arrow", + "bytes", "chrono", - "clap", "const_format", "csscolorparser", "duckdb", "palette", - "polars", - "polars-ops", "rand 0.8.6", "regex", "serde", @@ -1283,36 +747,11 @@ dependencies = [ name = "ggsql-python" version = "0.2.7" dependencies = [ + "arrow", "ggsql", - "polars", "pyo3", ] -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "half" version = "2.7.1" @@ -1325,16 +764,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "halfbrown" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ed2f2edad8a14c8186b847909a41fbb9c3eafa44f88bd891114ed5019da09" -dependencies = [ - "hashbrown 0.16.1", - "serde", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -1350,9 +779,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.1.5", + "foldhash", ] [[package]] @@ -1360,14 +787,6 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", - "rayon", - "serde", - "serde_core", -] [[package]] name = "hashbrown" @@ -1390,21 +809,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "http" version = "1.4.0" @@ -1444,12 +848,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" -[[package]] -name = "humantime" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" - [[package]] name = "hyper" version = "1.9.0" @@ -1460,7 +858,6 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -1481,7 +878,6 @@ dependencies = [ "hyper", "hyper-util", "rustls", - "rustls-native-certs", "tokio", "tokio-rustls", "tower-service", @@ -1681,21 +1077,6 @@ dependencies = [ "serde", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.18" @@ -1840,7 +1221,7 @@ dependencies = [ "bitflags", "libc", "plain", - "redox_syscall 0.7.4", + "redox_syscall", ] [[package]] @@ -1855,15 +1236,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" @@ -1876,40 +1248,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "lz4" -version = "1.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20b523e860d03443e98350ceaac5e71c6ba89aea7d960769ec3ce37f4de5af4" -dependencies = [ - "lz4-sys", -] - -[[package]] -name = "lz4-sys" -version = "1.11.1+lz4-1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "memchr" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "memmap2" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -1940,15 +1284,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "now" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89e9874397a1f0a52fc1f197a8effd9735223cb2390e9dcc83ac6cd02923d0" -dependencies = [ - "chrono", -] - [[package]] name = "num" version = "0.4.3" @@ -2023,68 +1358,12 @@ dependencies = [ "libm", ] -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "object_store" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbfbfff40aeccab00ec8a910b57ca8ecf4319b335c542f2edcd19dd25a1e2a00" -dependencies = [ - "async-trait", - "base64", - "bytes", - "chrono", - "form_urlencoded", - "futures", - "http", - "http-body-util", - "humantime", - "hyper", - "itertools", - "parking_lot", - "percent-encoding", - "quick-xml", - "rand 0.9.4", - "reqwest", - "ring", - "serde", - "serde_json", - "serde_urlencoded", - "thiserror 2.0.18", - "tokio", - "tracing", - "url", - "walkdir", - "wasm-bindgen-futures", - "web-time", -] - [[package]] name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - [[package]] name = "palette" version = "0.7.6" @@ -2109,35 +1388,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.18", - "smallvec", - "windows-link", -] - [[package]] name = "percent-encoding" version = "2.3.2" @@ -2154,15 +1404,6 @@ dependencies = [ "phf_shared 0.11.3", ] -[[package]] -name = "phf" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" -dependencies = [ - "phf_shared 0.12.1", -] - [[package]] name = "phf" version = "0.13.1" @@ -2230,15 +1471,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "phf_shared" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" -dependencies = [ - "siphasher", -] - [[package]] name = "phf_shared" version = "0.13.1" @@ -2268,558 +1500,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] -name = "planus" -version = "1.1.1" +name = "portable-atomic" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3daf8e3d4b712abe1d690838f6e29fb76b76ea19589c4afa39ec30e12f62af71" -dependencies = [ - "array-init-cursor", - "hashbrown 0.15.5", -] +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] -name = "polars" -version = "0.52.0" +name = "potential_utf" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bc9ea901050c1bb8747ee411bc7fbb390f3b399931e7484719512965132a248" -dependencies = [ - "getrandom 0.2.17", - "getrandom 0.3.4", - "polars-arrow", - "polars-compute", - "polars-core", - "polars-error", - "polars-io", - "polars-lazy", - "polars-ops", - "polars-plan", - "polars-sql", - "polars-time", - "polars-utils", - "version_check", -] - -[[package]] -name = "polars-arrow" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d3fe43f8702cf7899ff3d516c2e5f7dc84ee6f6a3007e1a831a0ff87940704" -dependencies = [ - "atoi_simd", - "bitflags", - "bytemuck", - "chrono", - "chrono-tz", - "dyn-clone", - "either", - "ethnum", - "getrandom 0.2.17", - "getrandom 0.3.4", - "hashbrown 0.16.1", - "itoa", - "lz4", - "num-traits", - "polars-arrow-format", - "polars-error", - "polars-schema", - "polars-utils", - "serde", - "simdutf8", - "streaming-iterator", - "strum_macros 0.27.2", - "version_check", - "zstd", -] - -[[package]] -name = "polars-arrow-format" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a556ac0ee744e61e167f34c1eb0013ce740e0ee6cd8c158b2ec0b518f10e6675" -dependencies = [ - "planus", - "serde", -] - -[[package]] -name = "polars-compute" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29cc7497378dee3a002f117e0b4e16b7cbe6c8ed3da16a0229c89294af7c3bf" -dependencies = [ - "atoi_simd", - "bytemuck", - "chrono", - "either", - "fast-float2", - "hashbrown 0.16.1", - "itoa", - "num-traits", - "polars-arrow", - "polars-error", - "polars-utils", - "rand 0.9.4", - "ryu", - "serde", - "strength_reduce", - "strum_macros 0.27.2", - "version_check", -] - -[[package]] -name = "polars-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48409b7440cb1a4aa84953fe3a4189dfbfb300a3298266a92a37363476641e40" -dependencies = [ - "bitflags", - "boxcar", - "bytemuck", - "chrono", - "chrono-tz", - "either", - "hashbrown 0.16.1", - "indexmap", - "itoa", - "num-traits", - "polars-arrow", - "polars-compute", - "polars-dtype", - "polars-error", - "polars-row", - "polars-schema", - "polars-utils", - "rand 0.9.4", - "rand_distr", - "rayon", - "regex", - "serde", - "serde_json", - "strum_macros 0.27.2", - "uuid", - "version_check", - "xxhash-rust", -] - -[[package]] -name = "polars-dtype" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7007e9e8b7b657cbd339b65246af7e87f5756ee9a860119b9424ddffd2aaf133" -dependencies = [ - "boxcar", - "hashbrown 0.16.1", - "polars-arrow", - "polars-error", - "polars-utils", - "serde", - "uuid", -] - -[[package]] -name = "polars-error" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a6be22566c89f6405f553bfdb7c8a6cb20ec51b35f3172de9a25fa3e252d85" -dependencies = [ - "object_store", - "parking_lot", - "polars-arrow-format", - "regex", - "signal-hook", - "simdutf8", -] - -[[package]] -name = "polars-expr" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6199a50d3e1afd0674fb009e340cbfb0010682b2387187a36328c00f3f2ca87b" -dependencies = [ - "bitflags", - "hashbrown 0.16.1", - "num-traits", - "polars-arrow", - "polars-compute", - "polars-core", - "polars-io", - "polars-ops", - "polars-plan", - "polars-row", - "polars-time", - "polars-utils", - "rand 0.9.4", - "rayon", - "recursive", - "regex", - "version_check", -] - -[[package]] -name = "polars-io" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be3714acdff87170141880a07f5d9233490d3bd5531c41898f6969d440feee11" -dependencies = [ - "async-trait", - "atoi_simd", - "blake3", - "bytes", - "chrono", - "chrono-tz", - "fast-float2", - "fs4", - "futures", - "glob", - "hashbrown 0.16.1", - "home", - "itoa", - "memchr", - "memmap2", - "num-traits", - "object_store", - "percent-encoding", - "polars-arrow", - "polars-compute", - "polars-core", - "polars-error", - "polars-json", - "polars-parquet", - "polars-schema", - "polars-time", - "polars-utils", - "rayon", - "regex", - "reqwest", - "ryu", - "serde", - "serde_json", - "simdutf8", - "tokio", -] - -[[package]] -name = "polars-json" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dd2126daebf58da564fc5840cd55eb8eb2479d24dfced0a1aea2178a9b33b12" -dependencies = [ - "chrono", - "chrono-tz", - "fallible-streaming-iterator", - "hashbrown 0.16.1", - "indexmap", - "itoa", - "num-traits", - "polars-arrow", - "polars-compute", - "polars-error", - "polars-utils", - "ryu", - "simd-json", - "streaming-iterator", -] - -[[package]] -name = "polars-lazy" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea136c360d03aafe56e0233495e30044ce43639b8b0360a4a38e840233f048a1" -dependencies = [ - "bitflags", - "chrono", - "either", - "memchr", - "polars-arrow", - "polars-compute", - "polars-core", - "polars-expr", - "polars-io", - "polars-mem-engine", - "polars-ops", - "polars-plan", - "polars-stream", - "polars-time", - "polars-utils", - "rayon", - "version_check", -] - -[[package]] -name = "polars-mem-engine" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6e455ceb6e5aee7ed7d5c8944104e66992173e03a9c42f9670226318672249" -dependencies = [ - "memmap2", - "polars-arrow", - "polars-core", - "polars-error", - "polars-expr", - "polars-io", - "polars-ops", - "polars-plan", - "polars-time", - "polars-utils", - "rayon", - "recursive", -] - -[[package]] -name = "polars-ops" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b59c80a019ef0e6f09b4416d2647076a52839305c9eb11919e8298ec667f853" -dependencies = [ - "argminmax", - "base64", - "bytemuck", - "chrono", - "chrono-tz", - "either", - "hashbrown 0.16.1", - "hex", - "indexmap", - "libm", - "memchr", - "num-traits", - "polars-arrow", - "polars-compute", - "polars-core", - "polars-error", - "polars-schema", - "polars-utils", - "rayon", - "regex", - "regex-syntax", - "strum_macros 0.27.2", - "unicode-normalization", - "unicode-reverse", - "version_check", -] - -[[package]] -name = "polars-parquet" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c2439d127c59e6bfc9d698419bdb45210068a6f501d44e6096429ad72c2eaa" -dependencies = [ - "async-stream", - "base64", - "bytemuck", - "ethnum", - "futures", - "hashbrown 0.16.1", - "num-traits", - "polars-arrow", - "polars-compute", - "polars-error", - "polars-parquet-format", - "polars-utils", - "serde", - "simdutf8", - "streaming-decompression", -] - -[[package]] -name = "polars-parquet-format" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c025243dcfe8dbc57e94d9f82eb3bef10b565ab180d5b99bed87fd8aea319ce1" -dependencies = [ - "async-trait", - "futures", -] - -[[package]] -name = "polars-plan" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b4619f5c7e9b91f18611c9ed82ebeee4b10052160825c1316ecf4dbd4d97e6" -dependencies = [ - "bitflags", - "bytemuck", - "bytes", - "chrono", - "chrono-tz", - "either", - "hashbrown 0.16.1", - "memmap2", - "num-traits", - "percent-encoding", - "polars-arrow", - "polars-compute", - "polars-core", - "polars-error", - "polars-io", - "polars-ops", - "polars-time", - "polars-utils", - "rayon", - "recursive", - "regex", - "sha2", - "slotmap", - "strum_macros 0.27.2", - "version_check", -] - -[[package]] -name = "polars-row" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18d232f25b83032e280a279a1f40beb8a6f8fc43907b13dc07b1c56f3b11eea" -dependencies = [ - "bitflags", - "bytemuck", - "polars-arrow", - "polars-compute", - "polars-dtype", - "polars-error", - "polars-utils", -] - -[[package]] -name = "polars-schema" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73e21d429ae1c23f442b0220ccfe773a9734a44e997b5062a741842909d9441" -dependencies = [ - "indexmap", - "polars-error", - "polars-utils", - "serde", - "version_check", -] - -[[package]] -name = "polars-sql" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e67ac1cbb0c972a57af3be12f19aa9803898863fe95c33cdd39df05f5738a75" -dependencies = [ - "bitflags", - "hex", - "polars-core", - "polars-error", - "polars-lazy", - "polars-ops", - "polars-plan", - "polars-time", - "polars-utils", - "rand 0.9.4", - "regex", - "serde", - "sqlparser", -] - -[[package]] -name = "polars-stream" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ff19612074640a9d65e5928b7223db76ffee63e55b276f1e466d06719eb7362" -dependencies = [ - "async-channel", - "async-trait", - "atomic-waker", - "bitflags", - "chrono-tz", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils", - "futures", - "memmap2", - "parking_lot", - "percent-encoding", - "pin-project-lite", - "polars-arrow", - "polars-compute", - "polars-core", - "polars-error", - "polars-expr", - "polars-io", - "polars-mem-engine", - "polars-ops", - "polars-parquet", - "polars-plan", - "polars-time", - "polars-utils", - "rand 0.9.4", - "rayon", - "recursive", - "slotmap", - "tokio", - "version_check", -] - -[[package]] -name = "polars-time" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddce7a9f81d5f47d981bcee4a8db004f9596bb51f0f4d9d93667a1a00d88166c" -dependencies = [ - "atoi_simd", - "bytemuck", - "chrono", - "chrono-tz", - "now", - "num-traits", - "polars-arrow", - "polars-compute", - "polars-core", - "polars-error", - "polars-ops", - "polars-utils", - "rayon", - "regex", - "strum_macros 0.27.2", -] - -[[package]] -name = "polars-utils" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "667c1bc2d2313f934d711f6e3b58d8d9f80351d14ea60af936a26b7dfb06e309" -dependencies = [ - "bincode", - "bytemuck", - "bytes", - "compact_str", - "either", - "flate2", - "foldhash 0.2.0", - "hashbrown 0.16.1", - "indexmap", - "libc", - "memmap2", - "num-traits", - "polars-error", - "rand 0.9.4", - "raw-cpuid", - "rayon", - "regex", - "rmp-serde", - "serde", - "serde_json", - "serde_stacker", - "slotmap", - "stacker", - "uuid", - "version_check", -] - -[[package]] -name = "portable-atomic" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -2861,16 +1551,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "psm" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" -dependencies = [ - "ar_archive_writer", - "cc", -] - [[package]] name = "ptr_meta" version = "0.1.4" @@ -2952,16 +1632,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "quick-xml" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "quinn" version = "0.11.9" @@ -3085,119 +1755,31 @@ dependencies = [ "rand_core 0.9.5", ] -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_distr" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" -dependencies = [ - "num-traits", - "rand 0.9.4", -] - -[[package]] -name = "raw-cpuid" -version = "11.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" -dependencies = [ - "bitflags", -] - -[[package]] -name = "rayon" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "recursive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" -dependencies = [ - "recursive-proc-macro-impl", - "stacker", -] - -[[package]] -name = "recursive-proc-macro-impl" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" -dependencies = [ - "quote", - "syn 2.0.117", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_syscall" -version = "0.7.4" +[[package]] +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "bitflags", + "getrandom 0.2.17", ] [[package]] -name = "ref-cast" -version = "1.0.25" +name = "rand_core" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "ref-cast-impl", + "getrandom 0.3.4", ] [[package]] -name = "ref-cast-impl" -version = "1.0.25" +name = "redox_syscall" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "bitflags", ] [[package]] @@ -3249,7 +1831,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", "http", "http-body", "http-body-util", @@ -3262,7 +1843,6 @@ dependencies = [ "pin-project-lite", "quinn", "rustls", - "rustls-native-certs", "rustls-pki-types", "serde", "serde_json", @@ -3270,14 +1850,12 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-rustls", - "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", "web-sys", "webpki-roots", ] @@ -3325,25 +1903,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "rmp" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "rmp-serde" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" -dependencies = [ - "rmp", - "serde", -] - [[package]] name = "rust_decimal" version = "1.41.0" @@ -3403,18 +1962,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - [[package]] name = "rustls-pki-types" version = "1.14.0" @@ -3448,59 +1995,12 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "seahash" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "1.0.28" @@ -3551,17 +2051,6 @@ dependencies = [ "zmij", ] -[[package]] -name = "serde_stacker" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4936375d50c4be7eff22293a9344f8e46f323ed2b3c243e52f89138d9bb0f4a" -dependencies = [ - "serde", - "serde_core", - "stacker", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3574,65 +2063,18 @@ dependencies = [ "serde", ] -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - [[package]] name = "simd-adler32" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" -[[package]] -name = "simd-json" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4255126f310d2ba20048db6321c81ab376f6a6735608bf11f0785c41f01f64e3" -dependencies = [ - "ahash 0.8.12", - "halfbrown", - "once_cell", - "ref-cast", - "serde", - "serde_json", - "simdutf8", - "value-trait", -] - [[package]] name = "simdutf8" version = "0.1.5" @@ -3651,15 +2093,6 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" -[[package]] -name = "slotmap" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" -dependencies = [ - "version_check", -] - [[package]] name = "smallvec" version = "1.15.1" @@ -3685,67 +2118,18 @@ dependencies = [ "thiserror 2.0.18", ] -[[package]] -name = "sqlparser" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05a528114c392209b3264855ad491fcce534b94a38771b0a0b97a79379275ce8" -dependencies = [ - "log", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "stacker" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.59.0", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "streaming-decompression" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6cc3b19bfb128a8ad11026086e31d3ce9ad23f8ea37354b31383a187c44cf3" -dependencies = [ - "fallible-streaming-iterator", -] - [[package]] name = "streaming-iterator" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" -[[package]] -name = "strength_reduce" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "strum" version = "0.26.3" @@ -3942,21 +2326,9 @@ dependencies = [ "mio", "pin-project-lite", "socket2", - "tokio-macros", "windows-sys 0.61.2", ] -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "tokio-rustls" version = "0.26.4" @@ -3967,19 +2339,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - [[package]] name = "toml_datetime" version = "1.1.1+spec-1.1.0" @@ -4062,21 +2421,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "tracing-core" version = "0.1.36" @@ -4102,9 +2449,9 @@ dependencies = [ [[package]] name = "tree-sitter-ggsql" -version = "0.2.7" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d03a49b4deae040a0dd8d012866d703c628046de3f75d5c60a0dc7f996f1940" +checksum = "3e945641c95bf67b620d111212f574d75f6fa1c5f2c8820c73663ca306cda9b8" dependencies = [ "cc", "tree-sitter", @@ -4122,12 +2469,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typenum" -version = "1.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" - [[package]] name = "uncased" version = "0.9.10" @@ -4143,30 +2484,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-reverse" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6f4888ebc23094adfb574fdca9fdc891826287a6397d2cd28802ffd6f20c76" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "unicode-segmentation" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" - [[package]] name = "unicode-width" version = "0.2.2" @@ -4191,12 +2508,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "url" version = "2.5.8" @@ -4215,12 +2526,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "uuid" version = "1.23.1" @@ -4229,22 +2534,9 @@ checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "getrandom 0.4.2", "js-sys", - "serde_core", "wasm-bindgen", ] -[[package]] -name = "value-trait" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e80f0c733af0720a501b3905d22e2f97662d8eacfe082a75ed7ffb5ab08cb59" -dependencies = [ - "float-cmp", - "halfbrown", - "itoa", - "ryu", -] - [[package]] name = "vcpkg" version = "0.2.15" @@ -4257,22 +2549,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "virtue" -version = "0.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.1" @@ -4384,19 +2660,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasmparser" version = "0.244.0" @@ -4438,15 +2701,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "windows-core" version = "0.62.2" @@ -4515,15 +2769,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.60.2" @@ -4799,12 +3044,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "xxhash-rust" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" - [[package]] name = "yoke" version = "0.8.2" @@ -4945,31 +3184,3 @@ dependencies = [ "log", "simd-adler32", ] - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index 7ed0de0..7b1a0eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ggsql-python" -version = "0.2.7" +version = "0.3.0" edition = "2021" authors = ["ggsql Team"] license = "MIT" @@ -14,8 +14,8 @@ crate-type = ["cdylib"] [dependencies] pyo3 = { version = "0.26", features = ["extension-module", "abi3-py310"] } -polars = { version = "0.52", default-features = false, features = ["ipc"] } -ggsql = { version = "0.2.7", default-features = false, features = ["duckdb", "vegalite"] } +arrow = { version = "56", default-features = false, features = ["ipc"] } +ggsql = { version = "0.3.0", default-features = false, features = ["duckdb", "vegalite"] } [features] default = [] diff --git a/README.md b/README.md index 46ea575..81782a5 100644 --- a/README.md +++ b/README.md @@ -44,21 +44,21 @@ pip install target/wheels/ggsql-*.whl ### Simple Usage with `render_altair` -For quick visualizations, use the `render_altair` convenience function: +For quick visualizations, use the `render_altair` convenience function. It accepts any narwhals-compatible DataFrame (polars, pandas, pyarrow, etc.): ```python import ggsql -import polars as pl +import pyarrow as pa -# Create a DataFrame -df = pl.DataFrame({ +# Create a table +table = pa.table({ "x": [1, 2, 3, 4, 5], "y": [10, 20, 15, 30, 25], "category": ["A", "B", "A", "B", "A"] }) # Render to Altair chart -chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point") +chart = ggsql.render_altair(table, "VISUALISE x, y DRAW point") # Display or save chart.display() # In Jupyter @@ -71,18 +71,18 @@ For more control, use the two-stage API with explicit reader and writer: ```python import ggsql -import polars as pl +import pyarrow as pa # 1. Create a DuckDB reader reader = ggsql.DuckDBReader("duckdb://memory") -# 2. Register your DataFrame as a table -df = pl.DataFrame({ +# 2. Register your data as a table (accepts pyarrow, polars, pandas, etc.) +table = pa.table({ "date": ["2024-01-01", "2024-01-02", "2024-01-03"], "revenue": [100, 150, 120], "region": ["North", "South", "North"] }) -reader.register("sales", df) +reader.register("sales", table) # 3. Execute the ggsql query spec = reader.execute( @@ -102,7 +102,7 @@ print(f"Layers: {spec.layer_count()}") # 5. Inspect SQL/VISUALISE portions and data print(f"SQL: {spec.sql()}") print(f"Visual: {spec.visual()}") -print(spec.layer_data(0)) # Returns polars DataFrame +print(spec.layer_data(0)) # Returns pyarrow.Table # 6. Render to Vega-Lite JSON writer = ggsql.VegaLiteWriter() @@ -125,9 +125,9 @@ reader = ggsql.DuckDBReader("duckdb:///path/to/file.db") # File database **Methods:** -- `register(name: str, df: polars.DataFrame, replace: bool = False)` - Register a DataFrame as a queryable table +- `register(name: str, table, replace: bool = False)` - Register data as a queryable table (accepts `pyarrow.Table`, `polars.DataFrame`, `pandas.DataFrame`, etc.) - `unregister(name: str)` - Unregister a previously registered table -- `execute_sql(sql: str) -> polars.DataFrame` - Execute SQL and return results +- `execute_sql(sql: str) -> pyarrow.Table` - Execute SQL and return results #### `VegaLiteWriter()` @@ -161,9 +161,9 @@ Result of `reader.execute()`, containing resolved visualization ready for render - `sql() -> str` - The executed SQL query - `visual() -> str` - The VISUALISE clause - `layer_count() -> int` - Number of DRAW layers -- `data() -> polars.DataFrame | None` - Main query result DataFrame -- `layer_data(index: int) -> polars.DataFrame | None` - Layer-specific data (if filtered) -- `stat_data(index: int) -> polars.DataFrame | None` - Statistical transform data +- `data() -> pyarrow.Table | None` - Main query result data +- `layer_data(index: int) -> pyarrow.Table | None` - Layer-specific data (if filtered) +- `stat_data(index: int) -> pyarrow.Table | None` - Statistical transform data - `layer_sql(index: int) -> str | None` - Layer filter SQL - `stat_sql(index: int) -> str | None` - Stat transform SQL - `warnings() -> list[dict]` - Validation warnings from execution @@ -205,11 +205,11 @@ Convenience function to render a DataFrame with a VISUALISE spec to an Altair ch **Returns:** An Altair chart object (Chart, LayerChart, FacetChart, etc.) ```python -import polars as pl +import pyarrow as pa import ggsql -df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) -chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point") +table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) +chart = ggsql.render_altair(table, "VISUALISE x, y DRAW point") ``` ## Examples @@ -217,28 +217,31 @@ chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point") ### Mapping Styles ```python -df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30], "category": ["A", "B", "A"]}) +import pyarrow as pa + +table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30], "category": ["A", "B", "A"]}) # Explicit mapping -ggsql.render_altair(df, "VISUALISE x AS x, y AS y DRAW point") +ggsql.render_altair(table, "VISUALISE x AS x, y AS y DRAW point") # Implicit mapping (column name = aesthetic name) -ggsql.render_altair(df, "VISUALISE x, y DRAW point") +ggsql.render_altair(table, "VISUALISE x, y DRAW point") # Wildcard mapping (map all matching columns) -ggsql.render_altair(df, "VISUALISE * DRAW point") +ggsql.render_altair(table, "VISUALISE * DRAW point") # With color encoding -ggsql.render_altair(df, "VISUALISE x, y, category AS color DRAW point") +ggsql.render_altair(table, "VISUALISE x, y, category AS color DRAW point") ``` ### Custom Readers -You can use any Python object with an `execute_sql(sql: str) -> polars.DataFrame` method as a reader. This enables integration with any data source. +You can use any Python object with an `execute_sql(sql: str)` method as a reader. The method should return a `pyarrow.Table` (or any type that `pyarrow.table()` can convert, such as a `polars.DataFrame`). ```python import ggsql -import polars as pl +import pyarrow as pa +import pyarrow.csv class CSVReader: """Custom reader that loads data from CSV files.""" @@ -246,10 +249,10 @@ class CSVReader: def __init__(self, data_dir: str): self.data_dir = data_dir - def execute_sql(self, sql: str) -> pl.DataFrame: + def execute_sql(self, sql: str) -> pa.Table: # Simple implementation: ignore SQL and return fixed data # A real implementation would parse SQL to determine which file to load - return pl.read_csv(f"{self.data_dir}/data.csv") + return pyarrow.csv.read_csv(f"{self.data_dir}/data.csv") # Use custom reader with ggsql.execute() reader = CSVReader("/path/to/data") @@ -263,7 +266,7 @@ json_output = writer.render(spec) **Additional methods** for custom readers: -- `register(name: str, df: polars.DataFrame, replace: bool = False) -> None` - Register a DataFrame as a queryable table (required) +- `register(name: str, table, replace: bool = False) -> None` - Register data as a queryable table (required). Receives a `pyarrow.Table`. - `unregister(name: str) -> None` - Unregister a previously registered table (optional) ```python @@ -273,12 +276,12 @@ class AdvancedReader: def __init__(self): self.tables = {} - def execute_sql(self, sql: str) -> pl.DataFrame: + def execute_sql(self, sql: str) -> pa.Table: # Your SQL execution logic here ... - def register(self, name: str, df: pl.DataFrame, replace: bool = False) -> None: - self.tables[name] = df + def register(self, name: str, table: pa.Table, replace: bool = False) -> None: + self.tables[name] = table def unregister(self, name: str) -> None: del self.tables[name] @@ -292,7 +295,7 @@ Native readers like `DuckDBReader` use an optimized fast path, while custom Pyth ```python import ggsql -import polars as pl +import pyarrow as pa import ibis class IbisReader: @@ -305,22 +308,22 @@ class IbisReader: self.con = ibis.sqlite.connect() # Add other backends as needed - def execute_sql(self, sql: str) -> pl.DataFrame: - return self.con.con.execute(sql).pl() + def execute_sql(self, sql: str) -> pa.Table: + return self.con.con.execute(sql).arrow() - def register(self, name: str, df: pl.DataFrame, replace: bool = False) -> None: - self.con.create_table(name, df.to_arrow(), overwrite=replace) + def register(self, name: str, table: pa.Table, replace: bool = False) -> None: + self.con.create_table(name, table, overwrite=replace) def unregister(self, name: str) -> None: self.con.drop_table(name) # Usage reader = IbisReader() -df = pl.DataFrame({ +table = pa.table({ "date": ["2024-01-01", "2024-01-02", "2024-01-03"], "revenue": [100, 150, 120], }) -reader.register("sales", df) +reader.register("sales", table) spec = ggsql.execute( "SELECT * FROM sales VISUALISE date AS x, revenue AS y DRAW line", @@ -356,7 +359,7 @@ pytest tests/ -v - Python >= 3.10 - altair >= 5.0 - narwhals >= 2.15 -- polars >= 1.0 +- pyarrow >= 14.0 ## License diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..24e18c5 --- /dev/null +++ b/build.rs @@ -0,0 +1,7 @@ +fn main() { + // DuckDB 1.4+ uses the Windows Restart Manager API, which lives in rstrtmgr.lib. + // Without this, linking fails with unresolved symbols for RmStartSession, etc. + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") { + println!("cargo:rustc-link-lib=rstrtmgr"); + } +} diff --git a/pyproject.toml b/pyproject.toml index 806d993..959323f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "ggsql" -version = "0.2.7" +version = "0.3.0" description = "SQL extension for declarative data visualization" readme = "README.md" requires-python = ">=3.10" @@ -17,7 +17,7 @@ classifiers = [ dependencies = [ "altair>=5.0", "narwhals>=2.15.0", - "polars>=1.0", + "pyarrow>=14.0", ] [project.urls] diff --git a/python/ggsql/__init__.py b/python/ggsql/__init__.py index 359e43c..dc34eda 100644 --- a/python/ggsql/__init__.py +++ b/python/ggsql/__init__.py @@ -33,7 +33,7 @@ "execute", "render_altair", ] -__version__ = "0.2.7" +__version__ = "0.3.0" # Type alias for any Altair chart type AltairChart = Union[ @@ -136,11 +136,11 @@ def render_altair( if not isinstance(df, nw.DataFrame): raise TypeError("df must be a narwhals DataFrame or compatible type") - pl_df = df.to_polars() + arrow_table = df.to_arrow() # Create temporary reader and register data reader = DuckDBReader("duckdb://memory") - reader.register("__data__", pl_df) + reader.register("__data__", arrow_table) # Build full query: SELECT * FROM __data__ + VISUALISE clause query = f"SELECT * FROM __data__ {viz}" diff --git a/src/lib.rs b/src/lib.rs index 6e5c543..822c2d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,77 +6,113 @@ use pyo3::prelude::*; use pyo3::types::{PyBytes, PyDict, PyList}; use std::io::Cursor; +use arrow::ipc::reader::StreamReader; +use arrow::ipc::writer::StreamWriter; + +use ggsql::dataframe::DataFrame; use ggsql::reader::Spec; use ggsql::reader::{DuckDBReader as RustDuckDBReader, Reader}; use ggsql::validate::{validate as rust_validate, ValidationWarning}; use ggsql::writer::{VegaLiteWriter as RustVegaLiteWriter, Writer as RustWriter}; use ggsql::GgsqlError; -use polars::prelude::{DataFrame, IpcReader, IpcWriter, SerReader, SerWriter}; - // ============================================================================ // Helper Functions for DataFrame Conversion // ============================================================================ -/// Convert a Polars DataFrame to a Python polars DataFrame via IPC serialization -fn polars_to_py(py: Python<'_>, df: &DataFrame) -> PyResult> { +/// Convert a ggsql DataFrame to a Python pyarrow Table via Arrow IPC serialization +fn df_to_py(py: Python<'_>, df: &DataFrame) -> PyResult> { + let rb = df.inner(); let mut buffer = Vec::new(); - IpcWriter::new(&mut buffer) - .finish(&mut df.clone()) - .map_err(|e| { + { + let mut writer = StreamWriter::try_new(&mut buffer, &rb.schema()).map_err(|e| { + PyErr::new::(format!( + "Failed to create IPC writer: {}", + e + )) + })?; + writer.write(rb).map_err(|e| { + PyErr::new::(format!( + "Failed to write RecordBatch: {}", + e + )) + })?; + writer.finish().map_err(|e| { PyErr::new::(format!( - "Failed to serialize DataFrame: {}", + "Failed to finish IPC stream: {}", e )) })?; + } + let pa = py.import("pyarrow")?; + let ipc_mod = pa.getattr("ipc")?; let io = py.import("io")?; let bytes_io = io.call_method1("BytesIO", (PyBytes::new(py, &buffer),))?; - - let polars = py.import("polars")?; - polars - .call_method1("read_ipc", (bytes_io,)) - .map(|obj| obj.into()) + let reader = ipc_mod.call_method1("open_stream", (&bytes_io,))?; + let table = reader.call_method0("read_all")?; + Ok(table.into()) } -/// Convert a Python polars DataFrame to a Rust Polars DataFrame via IPC serialization -fn py_to_polars(py: Python<'_>, df: &Bound<'_, PyAny>) -> PyResult { +/// Convert a Python pyarrow Table to a ggsql DataFrame via Arrow IPC serialization +fn py_to_df(py: Python<'_>, obj: &Bound<'_, PyAny>) -> PyResult { + let pa = py.import("pyarrow")?; + let table = if obj.is_instance(&pa.getattr("Table")?)? { + obj.clone() + } else { + pa.call_method1("table", (obj,)).map_err(|e| { + let type_name = obj + .get_type() + .name() + .map(|n| n.to_string()) + .unwrap_or_else(|_| "".to_string()); + PyErr::new::(format!( + "Expected a pyarrow.Table or compatible type, got {}: {}", + type_name, e + )) + })? + }; + + let ipc_mod = pa.getattr("ipc")?; let io = py.import("io")?; - let bytes_io = io.call_method0("BytesIO")?; - df.call_method1("write_ipc", (&bytes_io,))?; - bytes_io.call_method1("seek", (0i64,))?; + let sink = io.call_method0("BytesIO")?; + let writer = ipc_mod.call_method1("new_stream", (&sink, table.getattr("schema")?))?; + writer.call_method1("write_table", (&table,))?; + writer.call_method0("close")?; + sink.call_method1("seek", (0i64,))?; + let ipc_bytes: Vec = sink.call_method0("read")?.extract()?; - let ipc_bytes: Vec = bytes_io.call_method0("read")?.extract()?; let cursor = Cursor::new(ipc_bytes); + let reader = StreamReader::try_new(cursor, None).map_err(|e| { + PyErr::new::(format!("Failed to read IPC stream: {}", e)) + })?; - IpcReader::new(cursor).finish().map_err(|e| { - PyErr::new::(format!("Failed to read DataFrame: {}", e)) - }) -} + let batches: Vec<_> = reader + .into_iter() + .collect::, _>>() + .map_err(|e| { + PyErr::new::(format!( + "Failed to read RecordBatch: {}", + e + )) + })?; -/// Convert a Python polars DataFrame to Rust DataFrame - for use inside Python::attach -/// This variant is used by PyReaderBridge where we already hold the GIL. -fn py_to_polars_inner(df: &Bound<'_, PyAny>) -> PyResult { - let py = df.py(); - let io = py.import("io")?; - let bytes_io = io.call_method0("BytesIO")?; + if batches.is_empty() { + return Ok(DataFrame::empty()); + } - df.call_method1("write_ipc", (&bytes_io,)).map_err(|_| { - PyErr::new::( - "Reader.execute_sql() must return a polars.DataFrame", - ) + let combined = arrow::compute::concat_batches(&batches[0].schema(), &batches).map_err(|e| { + PyErr::new::(format!("Failed to concat batches: {}", e)) })?; - bytes_io.call_method1("seek", (0i64,))?; - let ipc_bytes: Vec = bytes_io.call_method0("read")?.extract()?; - let cursor = Cursor::new(ipc_bytes); + Ok(DataFrame::from_record_batch(combined)) +} - IpcReader::new(cursor).finish().map_err(|e| { - PyErr::new::(format!( - "Failed to deserialize DataFrame: {}", - e - )) - }) +/// Convert a Python object to a ggsql DataFrame - for use inside Python::attach +/// This variant is used by PyReaderBridge where we already hold the GIL. +fn py_to_df_inner(obj: &Bound<'_, PyAny>) -> PyResult { + let py = obj.py(); + py_to_df(py, obj) } /// Convert validation errors/warnings to a Python list of dicts @@ -121,7 +157,7 @@ fn warnings_to_pylist(py: Python<'_>, warnings: &[ValidationWarning]) -> PyResul /// Bridges a Python reader object to the Rust Reader trait. /// -/// This allows any Python object with an `execute_sql(sql: str) -> polars.DataFrame` +/// This allows any Python object with an `execute_sql(sql: str) -> pyarrow.Table` /// method to be used as a ggsql reader. struct PyReaderBridge { obj: Py, @@ -136,17 +172,16 @@ impl Reader for PyReaderBridge { let result = bound.call_method1("execute_sql", (sql,)).map_err(|e| { GgsqlError::ReaderError(format!("Reader.execute_sql() failed: {}", e)) })?; - py_to_polars_inner(&result).map_err(|e| GgsqlError::ReaderError(e.to_string())) + py_to_df_inner(&result).map_err(|e| GgsqlError::ReaderError(e.to_string())) }) } fn register(&self, name: &str, df: DataFrame, replace: bool) -> ggsql::Result<()> { Python::attach(|py| { - let py_df = - polars_to_py(py, &df).map_err(|e| GgsqlError::ReaderError(e.to_string()))?; + let py_table = df_to_py(py, &df).map_err(|e| GgsqlError::ReaderError(e.to_string()))?; self.obj .bind(py) - .call_method1("register", (name, py_df, replace)) + .call_method1("register", (name, py_table, replace)) .map_err(|e| GgsqlError::ReaderError(format!("Reader.register() failed: {}", e)))?; Ok(()) }) @@ -203,11 +238,11 @@ macro_rules! try_native_readers { /// Examples /// -------- /// >>> reader = DuckDBReader("duckdb://memory") -/// >>> df = reader.execute_sql("SELECT 1 as x, 2 as y") +/// >>> table = reader.execute_sql("SELECT 1 as x, 2 as y") /// /// >>> reader = DuckDBReader("duckdb://memory") -/// >>> reader.register("data", pl.DataFrame({"x": [1, 2, 3]})) -/// >>> df = reader.execute_sql("SELECT * FROM data WHERE x > 1") +/// >>> reader.register("data", pa.table({"x": [1, 2, 3]})) +/// >>> table = reader.execute_sql("SELECT * FROM data WHERE x > 1") #[pyclass(name = "DuckDBReader", unsendable)] struct PyDuckDBReader { inner: RustDuckDBReader, @@ -239,32 +274,32 @@ impl PyDuckDBReader { Ok(Self { inner }) } - /// Register a DataFrame as a queryable table. + /// Register a pyarrow Table as a queryable table. /// - /// After registration, the DataFrame can be queried by name in SQL. + /// After registration, the table can be queried by name in SQL. /// /// Parameters /// ---------- /// name : str /// The table name to register under. - /// df : polars.DataFrame - /// The DataFrame to register. Must be a polars DataFrame. + /// table : pyarrow.Table + /// The Arrow table to register. /// /// Raises /// ------ /// ValueError /// If registration fails or the table name is invalid. - #[pyo3(signature = (name, df, replace=false))] + #[pyo3(signature = (name, table, replace=false))] fn register( &self, py: Python<'_>, name: &str, - df: &Bound<'_, PyAny>, + table: &Bound<'_, PyAny>, replace: bool, ) -> PyResult<()> { - let rust_df = py_to_polars(py, df)?; + let df = py_to_df(py, table)?; self.inner - .register(name, rust_df, replace) + .register(name, df, replace) .map_err(|e| PyErr::new::(e.to_string())) } @@ -285,7 +320,7 @@ impl PyDuckDBReader { .map_err(|e| PyErr::new::(e.to_string())) } - /// Execute a SQL query and return the result as a DataFrame. + /// Execute a SQL query and return the result as a pyarrow Table. /// /// Parameters /// ---------- @@ -294,8 +329,8 @@ impl PyDuckDBReader { /// /// Returns /// ------- - /// polars.DataFrame - /// The query result as a polars DataFrame. + /// pyarrow.Table + /// The query result as a pyarrow Table. /// /// Raises /// ------ @@ -306,7 +341,7 @@ impl PyDuckDBReader { .inner .execute_sql(sql) .map_err(|e| PyErr::new::(e.to_string()))?; - polars_to_py(py, &df) + df_to_py(py, &df) } /// Execute a ggsql query and return the visualization specification. @@ -559,12 +594,12 @@ impl PySpec { /// /// Returns /// ------- - /// polars.DataFrame | None - /// The main query result DataFrame, or None if not available. + /// pyarrow.Table | None + /// The main query result as a pyarrow Table, or None if not available. fn data(&self, py: Python<'_>) -> PyResult>> { self.inner .layer_data(0) - .map(|df| polars_to_py(py, df)) + .map(|df| df_to_py(py, df)) .transpose() } @@ -577,12 +612,12 @@ impl PySpec { /// /// Returns /// ------- - /// polars.DataFrame | None - /// The layer-specific DataFrame, or None if the layer uses global data. + /// pyarrow.Table | None + /// The layer-specific data as a pyarrow Table, or None if the layer uses global data. fn layer_data(&self, py: Python<'_>, index: usize) -> PyResult>> { self.inner .layer_data(index) - .map(|df| polars_to_py(py, df)) + .map(|df| df_to_py(py, df)) .transpose() } @@ -595,12 +630,12 @@ impl PySpec { /// /// Returns /// ------- - /// polars.DataFrame | None - /// The stat transform DataFrame, or None if no stat transform. + /// pyarrow.Table | None + /// The stat transform data as a pyarrow Table, or None if no stat transform. fn stat_data(&self, py: Python<'_>, index: usize) -> PyResult>> { self.inner .stat_data(index) - .map(|df| polars_to_py(py, df)) + .map(|df| df_to_py(py, df)) .transpose() } @@ -710,7 +745,7 @@ fn validate(query: &str) -> PyResult { /// reader : Reader | object /// The database reader to execute SQL against. Can be a native Reader /// for optimal performance, or any Python object with an -/// `execute_sql(sql: str) -> polars.DataFrame` method. +/// `execute_sql(sql: str) -> pyarrow.Table` method. /// /// Returns /// ------- @@ -732,8 +767,8 @@ fn validate(query: &str) -> PyResult { /// /// >>> # Using custom Python reader /// >>> class MyReader: -/// ... def execute_sql(self, sql: str) -> pl.DataFrame: -/// ... return pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) +/// ... def execute_sql(self, sql: str) -> pa.Table: +/// ... return pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) /// >>> reader = MyReader() /// >>> spec = execute("SELECT * FROM data VISUALISE x, y DRAW point", reader) #[pyfunction] diff --git a/tests/test_ggsql.py b/tests/test_ggsql.py index fbe4b13..540abd5 100644 --- a/tests/test_ggsql.py +++ b/tests/test_ggsql.py @@ -11,13 +11,20 @@ import json import duckdb +import pyarrow as pa import pytest -import polars as pl import altair import ggsql -# Optional dependency for ibis test +# Optional dependencies +try: + import polars as pl + + HAS_POLARS = True +except ImportError: + HAS_POLARS = False + try: import ibis @@ -71,18 +78,29 @@ def test_create_in_memory(self): def test_execute_simple_query(self): reader = ggsql.DuckDBReader("duckdb://memory") - df = reader.execute_sql("SELECT 1 AS x, 2 AS y") - assert isinstance(df, pl.DataFrame) - assert df.shape == (1, 2) - assert list(df.columns) == ["x", "y"] + table = reader.execute_sql("SELECT 1 AS x, 2 AS y") + assert isinstance(table, pa.Table) + assert table.shape == (1, 2) + assert table.column_names == ["x", "y"] def test_register_and_query(self): + reader = ggsql.DuckDBReader("duckdb://memory") + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + reader.register("my_data", table) + + result = reader.execute_sql("SELECT * FROM my_data WHERE x > 1") + assert isinstance(result, pa.Table) + assert result.shape == (2, 2) + + @pytest.mark.skipif(not HAS_POLARS, reason="polars not installed") + def test_register_polars_dataframe(self): + """register() accepts polars DataFrames via automatic conversion.""" reader = ggsql.DuckDBReader("duckdb://memory") df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) reader.register("my_data", df) result = reader.execute_sql("SELECT * FROM my_data WHERE x > 1") - assert isinstance(result, pl.DataFrame) + assert isinstance(result, pa.Table) assert result.shape == (2, 2) def test_invalid_connection_string(self): @@ -109,8 +127,8 @@ def test_execute_simple_query(self): def test_execute_with_registered_data(self): reader = ggsql.DuckDBReader("duckdb://memory") - df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) - reader.register("data", df) + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + reader.register("data", table) spec = reader.execute("SELECT * FROM data VISUALISE x, y DRAW point") assert spec.metadata()["rows"] == 3 @@ -142,7 +160,7 @@ def test_execute_data_accessor(self): reader = ggsql.DuckDBReader("duckdb://memory") spec = reader.execute("SELECT 1 AS x, 2 AS y VISUALISE x, y DRAW point") data = spec.data() - assert isinstance(data, pl.DataFrame) + assert isinstance(data, pa.Table) assert data.shape == (1, 2) def test_execute_without_visualise_fails(self): @@ -168,8 +186,8 @@ def test_render_to_vegalite(self): def test_render_contains_data(self): reader = ggsql.DuckDBReader("duckdb://memory") - df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) - reader.register("data", df) + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + reader.register("data", table) spec = reader.execute("SELECT * FROM data VISUALISE x, y DRAW point") writer = ggsql.VegaLiteWriter() @@ -197,11 +215,18 @@ def test_render_multi_layer(self): class TestRenderAltairDataFrameConversion: """Tests for DataFrame handling in render_altair().""" + def test_accepts_pyarrow_table(self): + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + chart = ggsql.render_altair(table, "VISUALISE x, y DRAW point") + assert isinstance(chart, altair.TopLevelMixin) + + @pytest.mark.skipif(not HAS_POLARS, reason="polars not installed") def test_accepts_polars_dataframe(self): df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point") assert isinstance(chart, altair.TopLevelMixin) + @pytest.mark.skipif(not HAS_POLARS, reason="polars not installed") def test_accepts_polars_lazyframe(self): lf = pl.LazyFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) chart = ggsql.render_altair(lf, "VISUALISE x, y DRAW point") @@ -210,8 +235,8 @@ def test_accepts_polars_lazyframe(self): def test_accepts_narwhals_dataframe(self): import narwhals as nw - pl_df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) - nw_df = nw.from_native(pl_df) + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + nw_df = nw.from_native(table) chart = ggsql.render_altair(nw_df, "VISUALISE x, y DRAW point") assert isinstance(chart, altair.TopLevelMixin) @@ -232,20 +257,20 @@ class TestRenderAltairReturnType: """Tests for render_altair() return type.""" def test_returns_altair_chart(self): - df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) - chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point") + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + chart = ggsql.render_altair(table, "VISUALISE x, y DRAW point") assert isinstance(chart, altair.TopLevelMixin) def test_chart_has_data(self): - df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) - chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point") + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + chart = ggsql.render_altair(table, "VISUALISE x, y DRAW point") spec = chart.to_dict() # Data should be embedded in datasets assert "datasets" in spec def test_chart_can_be_serialized(self): - df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) - chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point") + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + chart = ggsql.render_altair(table, "VISUALISE x, y DRAW point") # Should not raise json_str = chart.to_json() assert len(json_str) > 0 @@ -256,15 +281,15 @@ class TestRenderAltairChartTypeDetection: def test_simple_chart_returns_layer_chart(self): """Simple DRAW specs produce LayerChart (ggsql always wraps in layer).""" - df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) - chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point") + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + chart = ggsql.render_altair(table, "VISUALISE x, y DRAW point") # ggsql wraps all charts in a layer assert isinstance(chart, altair.LayerChart) def test_layered_chart_can_round_trip(self): """LayerChart can be converted to dict and back.""" - df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) - chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point") + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + chart = ggsql.render_altair(table, "VISUALISE x, y DRAW point") # Convert to dict and back spec = chart.to_dict() @@ -276,7 +301,7 @@ def test_layered_chart_can_round_trip(self): def test_faceted_chart_returns_facet_chart(self): """FACET specs produce FacetChart.""" - df = pl.DataFrame( + table = pa.table( { "x": [1, 2, 3, 4, 5, 6], "y": [10, 20, 30, 40, 50, 60], @@ -285,13 +310,13 @@ def test_faceted_chart_returns_facet_chart(self): ) # Need validate=False because ggsql produces v6 specs chart = ggsql.render_altair( - df, "VISUALISE x, y FACET group DRAW point", validate=False + table, "VISUALISE x, y FACET group DRAW point", validate=False ) assert isinstance(chart, altair.FacetChart) def test_faceted_chart_can_round_trip(self): """FacetChart can be converted to dict and back.""" - df = pl.DataFrame( + table = pa.table( { "x": [1, 2, 3, 4, 5, 6], "y": [10, 20, 30, 40, 50, 60], @@ -299,7 +324,7 @@ def test_faceted_chart_can_round_trip(self): } ) chart = ggsql.render_altair( - df, "VISUALISE x, y FACET group DRAW point", validate=False + table, "VISUALISE x, y FACET group DRAW point", validate=False ) # Convert to dict (skip validation for ggsql specs) @@ -312,14 +337,16 @@ def test_faceted_chart_can_round_trip(self): def test_chart_with_color_encoding(self): """Charts with color encoding still return correct type.""" - df = pl.DataFrame( + table = pa.table( { "x": [1, 2, 3, 4], "y": [10, 20, 30, 40], "category": ["A", "B", "A", "B"], } ) - chart = ggsql.render_altair(df, "VISUALISE x, y, category AS color DRAW point") + chart = ggsql.render_altair( + table, "VISUALISE x, y, category AS color DRAW point" + ) # Should still be a LayerChart (ggsql wraps in layer) assert isinstance(chart, altair.LayerChart) @@ -328,9 +355,9 @@ class TestRenderAltairErrorHandling: """Tests for error handling in render_altair().""" def test_invalid_viz_raises(self): - df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) with pytest.raises(ValueError): - ggsql.render_altair(df, "NOT VALID SYNTAX") + ggsql.render_altair(table, "NOT VALID SYNTAX") class TestTwoStageAPIIntegration: @@ -342,14 +369,14 @@ def test_end_to_end_workflow(self): reader = ggsql.DuckDBReader("duckdb://memory") # Register data - df = pl.DataFrame( + table = pa.table( { "date": ["2024-01-01", "2024-01-02", "2024-01-03"], "value": [10, 20, 30], "region": ["North", "South", "North"], } ) - reader.register("sales", df) + reader.register("sales", table) # Execute visualization spec = reader.execute( @@ -399,11 +426,11 @@ class RegisterReader: def __init__(self): self.conn = duckdb.connect() - def execute_sql(self, sql: str) -> pl.DataFrame: - return self.conn.execute(sql).pl() + def execute_sql(self, sql: str) -> pa.Table: + return self.conn.execute(sql).arrow() - def register(self, name: str, df: pl.DataFrame, _replace: bool) -> None: - self.conn.register(name, df) + def register(self, name: str, table: pa.Table, _replace: bool) -> None: + self.conn.register(name, table) reader = RegisterReader() spec = ggsql.execute("SELECT 1 AS x, 2 AS y VISUALISE x, y DRAW point", reader) @@ -413,7 +440,7 @@ def test_custom_reader_error_handling(self): """Custom reader errors are propagated.""" class ErrorReader: - def execute_sql(self, sql: str) -> pl.DataFrame: + def execute_sql(self, sql: str) -> pa.Table: raise ValueError("Custom reader error") reader = ErrorReader() @@ -425,7 +452,7 @@ def test_custom_reader_wrong_return_type(self): class WrongTypeReader: def execute_sql(self, sql: str): - return {"x": [1, 2, 3]} # dict, not DataFrame + return {"x": [1, 2, 3]} # dict, not Table reader = WrongTypeReader() with pytest.raises((ValueError, TypeError)): @@ -450,11 +477,11 @@ def __init__(self): ") AS t(x, y, category)" ) - def execute_sql(self, sql: str) -> pl.DataFrame: - return self.conn.execute(sql).pl() + def execute_sql(self, sql: str) -> pa.Table: + return self.conn.execute(sql).arrow() - def register(self, name: str, df: pl.DataFrame, _replace: bool) -> None: - self.conn.register(name, df) + def register(self, name: str, table: pa.Table, _replace: bool) -> None: + self.conn.register(name, table) reader = DuckDBBackedReader() spec = ggsql.execute( @@ -480,12 +507,12 @@ def __init__(self): ) self.execute_calls = [] - def execute_sql(self, sql: str) -> pl.DataFrame: + def execute_sql(self, sql: str) -> pa.Table: self.execute_calls.append(sql) - return self.conn.execute(sql).pl() + return self.conn.execute(sql).arrow() - def register(self, name: str, df: pl.DataFrame, _replace: bool) -> None: - self.conn.register(name, df) + def register(self, name: str, table: pa.Table, _replace: bool) -> None: + self.conn.register(name, table) reader = RecordingReader() ggsql.execute( @@ -506,20 +533,20 @@ class IbisReader: def __init__(self): self.con = ibis.duckdb.connect() - def execute_sql(self, sql: str) -> pl.DataFrame: - return self.con.con.execute(sql).pl() + def execute_sql(self, sql: str) -> pa.Table: + return self.con.con.execute(sql).arrow() def register( - self, name: str, df: pl.DataFrame, replace: bool = True + self, name: str, table: pa.Table, replace: bool = True ) -> None: - self.con.create_table(name, df.to_arrow(), overwrite=replace) + self.con.create_table(name, table, overwrite=replace) def unregister(self, name: str) -> None: self.con.drop_table(name) reader = IbisReader() - df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) - reader.register("mydata", df) + table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]}) + reader.register("mydata", table) spec = ggsql.execute( "SELECT * FROM mydata VISUALISE x, y DRAW point", @@ -554,18 +581,17 @@ def test_render_chart_layer(self): def test_render_chart_facet(self): """render_chart() returns FacetChart for faceted specs.""" reader = ggsql.DuckDBReader("duckdb://memory") - df = pl.DataFrame( + table = pa.table( { "x": [1, 2, 3, 4, 5, 6], "y": [10, 20, 30, 40, 50, 60], "group": ["A", "A", "A", "B", "B", "B"], } ) - reader.register("data", df) + reader.register("data", table) spec = reader.execute( "SELECT * FROM data VISUALISE x, y FACET group DRAW point" ) writer = ggsql.VegaLiteWriter() chart = writer.render_chart(spec, validate=False) assert isinstance(chart, altair.FacetChart) - diff --git a/uv.lock b/uv.lock index d8b4a61..36acaf4 100644 --- a/uv.lock +++ b/uv.lock @@ -534,7 +534,7 @@ source = { editable = "." } dependencies = [ { name = "altair" }, { name = "narwhals" }, - { name = "polars" }, + { name = "pyarrow" }, ] [package.optional-dependencies] @@ -564,7 +564,7 @@ requires-dist = [ { name = "duckdb", marker = "extra == 'test'", specifier = ">=1.0" }, { name = "maturin", marker = "extra == 'dev'", specifier = ">=1.4" }, { name = "narwhals", specifier = ">=2.15.0" }, - { name = "polars", specifier = ">=1.0" }, + { name = "pyarrow", specifier = ">=14.0" }, { name = "pyarrow", marker = "extra == 'test'", specifier = ">=14.0" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=7.0" }, ] @@ -1467,34 +1467,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] -[[package]] -name = "polars" -version = "1.38.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "polars-runtime-32" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c6/5e/208a24471a433bcd0e9a6889ac49025fd4daad2815c8220c5bd2576e5f1b/polars-1.38.1.tar.gz", hash = "sha256:803a2be5344ef880ad625addfb8f641995cfd777413b08a10de0897345778239", size = 717667, upload-time = "2026-02-06T18:13:23.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/49/737c1a6273c585719858261753da0b688454d1b634438ccba8a9c4eb5aab/polars-1.38.1-py3-none-any.whl", hash = "sha256:a29479c48fed4984d88b656486d221f638cba45d3e961631a50ee5fdde38cb2c", size = 810368, upload-time = "2026-02-06T18:11:55.819Z" }, -] - -[[package]] -name = "polars-runtime-32" -version = "1.38.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/07/4b/04d6b3fb7cf336fbe12fbc4b43f36d1783e11bb0f2b1e3980ec44878df06/polars_runtime_32-1.38.1.tar.gz", hash = "sha256:04f20ed1f5c58771f34296a27029dc755a9e4b1390caeaef8f317e06fdfce2ec", size = 2812631, upload-time = "2026-02-06T18:13:25.206Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/a2/a00defbddadd8cf1042f52380dcba6b6592b03bac8e3b34c436b62d12d3b/polars_runtime_32-1.38.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:18154e96044724a0ac38ce155cf63aa03c02dd70500efbbf1a61b08cadd269ef", size = 44108001, upload-time = "2026-02-06T18:11:58.127Z" }, - { url = "https://files.pythonhosted.org/packages/a7/fb/599ff3709e6a303024efd7edfd08cf8de55c6ac39527d8f41cbc4399385f/polars_runtime_32-1.38.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c49acac34cc4049ed188f1eb67d6ff3971a39b4af7f7b734b367119970f313ac", size = 40230140, upload-time = "2026-02-06T18:12:01.181Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8c/3ac18d6f89dc05fe2c7c0ee1dc5b81f77a5c85ad59898232c2500fe2ebbf/polars_runtime_32-1.38.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fef2ef2626a954e010e006cc8e4de467ecf32d08008f130cea1c78911f545323", size = 41994039, upload-time = "2026-02-06T18:12:04.332Z" }, - { url = "https://files.pythonhosted.org/packages/f2/5a/61d60ec5cc0ab37cbd5a699edb2f9af2875b7fdfdfb2a4608ca3cc5f0448/polars_runtime_32-1.38.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5f7a8125e2d50e2e060296551c929aec09be23a9edcb2b12ca923f555a5ba", size = 45755804, upload-time = "2026-02-06T18:12:07.846Z" }, - { url = "https://files.pythonhosted.org/packages/91/54/02cd4074c98c361ccd3fec3bcb0bd68dbc639c0550c42a4436b0ff0f3ccf/polars_runtime_32-1.38.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:10d19cd9863e129273b18b7fcaab625b5c8143c2d22b3e549067b78efa32e4fa", size = 42159605, upload-time = "2026-02-06T18:12:10.919Z" }, - { url = "https://files.pythonhosted.org/packages/8e/f3/b2a5e720cc56eaa38b4518e63aa577b4bbd60e8b05a00fe43ca051be5879/polars_runtime_32-1.38.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:61e8d73c614b46a00d2f853625a7569a2e4a0999333e876354ac81d1bf1bb5e2", size = 45336615, upload-time = "2026-02-06T18:12:14.074Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8d/ee2e4b7de948090cfb3df37d401c521233daf97bfc54ddec5d61d1d31618/polars_runtime_32-1.38.1-cp310-abi3-win_amd64.whl", hash = "sha256:08c2b3b93509c1141ac97891294ff5c5b0c548a373f583eaaea873a4bf506437", size = 45680732, upload-time = "2026-02-06T18:12:19.097Z" }, - { url = "https://files.pythonhosted.org/packages/bf/18/72c216f4ab0c82b907009668f79183ae029116ff0dd245d56ef58aac48e7/polars_runtime_32-1.38.1-cp310-abi3-win_arm64.whl", hash = "sha256:6d07d0cc832bfe4fb54b6e04218c2c27afcfa6b9498f9f6bbf262a00d58cc7c4", size = 41639413, upload-time = "2026-02-06T18:12:22.044Z" }, -] - [[package]] name = "prometheus-client" version = "0.25.0"