From d653b0cb1933281da268c0ca0df56508411adbaf Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 29 Apr 2026 10:25:42 -0500 Subject: [PATCH 1/8] =?UTF-8?q?Replace=20polars=20with=20arrow=20for=20Rus?= =?UTF-8?q?t=E2=86=94Python=20data=20bridge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upstream ggsql crate (v0.3.0) removed polars in favor of a custom DataFrame built on Arrow RecordBatch. This updates the Python bindings to match: Rust side: IPC serialization now uses arrow::ipc::{StreamWriter,StreamReader} instead of polars IpcWriter/IpcReader. The py_to_df() helper accepts any object that pyarrow.table() can convert, so custom readers returning polars DataFrames still work without changes. Python side: render_altair() calls df.to_arrow() instead of df.to_polars(). Runtime dependency changed from polars to pyarrow. Public API changes: - DuckDBReader.execute_sql() returns pyarrow.Table (was polars.DataFrame) - DuckDBReader.register() accepts pyarrow.Table (polars still works via automatic conversion) - Spec.data()/layer_data()/stat_data() return pyarrow.Table --- Cargo.lock | 1843 +------------------------------------- Cargo.toml | 4 +- pyproject.toml | 2 +- python/ggsql/__init__.py | 4 +- src/lib.rs | 189 ++-- tests/test_ggsql.py | 17 +- uv.lock | 32 +- 7 files changed, 157 insertions(+), 1934 deletions(-) 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..008f798 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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/pyproject.toml b/pyproject.toml index 806d993..0973f0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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..2a26624 100644 --- a/python/ggsql/__init__.py +++ b/python/ggsql/__init__.py @@ -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..dfee26d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,77 +6,116 @@ 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(|_| { + PyErr::new::( + "Expected a pyarrow.Table or compatible type", + ) + })? + }; + + 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 +160,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 +175,17 @@ 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 +242,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 +278,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 +324,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 +333,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 +345,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 +598,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 +616,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 +634,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 +749,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 +771,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..1b2f773 100644 --- a/tests/test_ggsql.py +++ b/tests/test_ggsql.py @@ -11,6 +11,7 @@ import json import duckdb +import pyarrow as pa import pytest import polars as pl import altair @@ -71,18 +72,18 @@ 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") - df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]}) - reader.register("my_data", df) + 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, pl.DataFrame) + assert isinstance(result, pa.Table) assert result.shape == (2, 2) def test_invalid_connection_string(self): @@ -142,7 +143,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): 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" From 59339fb194eeef52ebd467a7b7f93a32ea04f18b Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 29 Apr 2026 10:35:38 -0500 Subject: [PATCH 2/8] Apply cargo fmt --- src/lib.rs | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dfee26d..a2ff868 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,13 +25,12 @@ fn df_to_py(py: Python<'_>, df: &DataFrame) -> PyResult> { let rb = df.inner(); let mut buffer = Vec::new(); { - let mut writer = StreamWriter::try_new(&mut buffer, &rb.schema()) - .map_err(|e| { - PyErr::new::(format!( - "Failed to create IPC writer: {}", - 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: {}", @@ -79,10 +78,7 @@ fn py_to_df(py: Python<'_>, obj: &Bound<'_, PyAny>) -> PyResult { 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 - )) + PyErr::new::(format!("Failed to read IPC stream: {}", e)) })?; let batches: Vec<_> = reader @@ -99,14 +95,9 @@ fn py_to_df(py: Python<'_>, obj: &Bound<'_, PyAny>) -> PyResult { return Ok(DataFrame::empty()); } - let combined = arrow::compute::concat_batches(&batches[0].schema(), &batches).map_err( - |e| { - PyErr::new::(format!( - "Failed to concat batches: {}", - e - )) - }, - )?; + let combined = arrow::compute::concat_batches(&batches[0].schema(), &batches).map_err(|e| { + PyErr::new::(format!("Failed to concat batches: {}", e)) + })?; Ok(DataFrame::from_record_batch(combined)) } @@ -181,8 +172,7 @@ impl Reader for PyReaderBridge { fn register(&self, name: &str, df: DataFrame, replace: bool) -> ggsql::Result<()> { Python::attach(|py| { - let py_table = - df_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_table, replace)) From 412a460f8fc092674e7554f2c7eda5907fab6747 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 29 Apr 2026 10:38:54 -0500 Subject: [PATCH 3/8] Add changelog entries for 0.2.7 and upcoming 0.3.0 --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 CHANGELOG.md 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. From 5c0f8ed12ca12a72e361d5ca427bdb307edc3196 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 29 Apr 2026 10:45:33 -0500 Subject: [PATCH 4/8] =?UTF-8?q?Update=20README=20and=20CLAUDE.md=20for=20p?= =?UTF-8?q?olars=E2=86=92arrow=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update all code examples, API docs, and architecture description to reflect that the data bridge now uses pyarrow instead of polars. register() accepts any type that pyarrow.table() can convert (pyarrow, polars, pandas, etc.). --- CLAUDE.md | 43 +++++++++++++++++++++++++++++ README.md | 81 ++++++++++++++++++++++++++++--------------------------- 2 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 CLAUDE.md 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/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 From 574c0abc2fdd6669ca8873f8ed1a07a5071a2b52 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 29 Apr 2026 10:51:31 -0500 Subject: [PATCH 5/8] Convert tests to pyarrow; fix render_altair() LazyFrame check - Replace all pl.DataFrame/pl.LazyFrame usage in tests with pa.table() - Make polars optional in tests (skip polars-specific tests when not installed) - Custom readers now use duckdb .arrow() instead of .pl() - Fix render_altair() where the LazyFrame collect + DataFrame isinstance checks were accidentally collapsed into a single inverted check --- tests/test_ggsql.py | 127 ++++++++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 51 deletions(-) diff --git a/tests/test_ggsql.py b/tests/test_ggsql.py index 1b2f773..540abd5 100644 --- a/tests/test_ggsql.py +++ b/tests/test_ggsql.py @@ -13,12 +13,18 @@ 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 @@ -86,6 +92,17 @@ def test_register_and_query(self): 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, pa.Table) + assert result.shape == (2, 2) + def test_invalid_connection_string(self): with pytest.raises(ValueError): ggsql.DuckDBReader("invalid://connection") @@ -110,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 @@ -169,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() @@ -198,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") @@ -211,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) @@ -233,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 @@ -257,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() @@ -277,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], @@ -286,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], @@ -300,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) @@ -313,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) @@ -329,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: @@ -343,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( @@ -400,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) @@ -414,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() @@ -426,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)): @@ -451,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( @@ -481,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( @@ -507,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", @@ -555,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) - From 3b4caf0b9ae30754d42e8f47abc320550a136a60 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 29 Apr 2026 11:08:37 -0500 Subject: [PATCH 6/8] Add build.rs to link rstrtmgr on Windows DuckDB 1.4+ uses the Windows Restart Manager API (RmStartSession, etc.) which requires linking against rstrtmgr.lib. Without this, Windows builds fail with LNK2019 unresolved externals. --- build.rs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 build.rs 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"); + } +} From 2e6b8676ebc9f77d7e9ab83a78745ed022bc2f0e Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 29 Apr 2026 11:29:10 -0500 Subject: [PATCH 7/8] Include type name and cause in py_to_df conversion error Addresses PR feedback: instead of a generic "Expected a pyarrow.Table or compatible type" message, include the actual Python type name and the underlying pyarrow error for easier debugging. --- src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a2ff868..822c2d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,10 +60,16 @@ fn py_to_df(py: Python<'_>, obj: &Bound<'_, PyAny>) -> PyResult { let table = if obj.is_instance(&pa.getattr("Table")?)? { obj.clone() } else { - pa.call_method1("table", (obj,)).map_err(|_| { - PyErr::new::( - "Expected a pyarrow.Table or compatible type", - ) + 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 + )) })? }; From 3a941bf5378cb3c12ac003fc92f2ff8e96ebb8dd Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 29 Apr 2026 11:30:23 -0500 Subject: [PATCH 8/8] Bump version to 0.3.0 --- Cargo.toml | 2 +- pyproject.toml | 2 +- python/ggsql/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 008f798..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" diff --git a/pyproject.toml b/pyproject.toml index 0973f0e..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" diff --git a/python/ggsql/__init__.py b/python/ggsql/__init__.py index 2a26624..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[