diff --git a/Cargo.lock b/Cargo.lock
index e672845f60..87deef98fc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,20 @@
# It is not intended for manual editing.
version = 4
+[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if",
+ "const-random",
+ "getrandom 0.3.4",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
[[package]]
name = "aho-corasick"
version = "1.1.4"
@@ -76,6 +90,83 @@ version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+[[package]]
+name = "arrow-array"
+version = "57.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8955af33b25f3b175ee10af580577280b4bd01f7e823d94c7cdef7cf8c9aef"
+dependencies = [
+ "ahash",
+ "arrow-buffer",
+ "arrow-data",
+ "arrow-schema",
+ "chrono",
+ "half",
+ "hashbrown 0.16.1",
+ "num-complex",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "arrow-buffer"
+version = "57.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c697ddca96183182f35b3a18e50b9110b11e916d7b7799cbfd4d34662f2c56c2"
+dependencies = [
+ "bytes",
+ "half",
+ "num-bigint",
+ "num-traits",
+]
+
+[[package]]
+name = "arrow-data"
+version = "57.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fdd994a9d28e6365aa78e15da3f3950c0fdcea6b963a12fa1c391afb637b304"
+dependencies = [
+ "arrow-buffer",
+ "arrow-schema",
+ "half",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "arrow-ipc"
+version = "57.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abf7df950701ab528bf7c0cf7eeadc0445d03ef5d6ffc151eaae6b38a58feff1"
+dependencies = [
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-data",
+ "arrow-schema",
+ "arrow-select",
+ "flatbuffers",
+]
+
+[[package]]
+name = "arrow-schema"
+version = "57.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c872d36b7bf2a6a6a2b40de9156265f0242910791db366a2c17476ba8330d68"
+
+[[package]]
+name = "arrow-select"
+version = "57.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68bf3e3efbd1278f770d67e5dc410257300b161b93baedb3aae836144edcaf4b"
+dependencies = [
+ "ahash",
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-data",
+ "arrow-schema",
+ "num-traits",
+]
+
[[package]]
name = "async-lock"
version = "2.8.0"
@@ -395,6 +486,26 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "const-random"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
+dependencies = [
+ "const-random-macro",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
+dependencies = [
+ "getrandom 0.2.16",
+ "once_cell",
+ "tiny-keccak",
+]
+
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
@@ -435,6 +546,12 @@ 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"
@@ -668,6 +785,16 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+[[package]]
+name = "flatbuffers"
+version = "25.12.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3"
+dependencies = [
+ "bitflags",
+ "rustc_version",
+]
+
[[package]]
name = "float-cmp"
version = "0.9.0"
@@ -1054,6 +1181,18 @@ dependencies = [
"syn 2.0.111",
]
+[[package]]
+name = "half"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+ "num-traits",
+ "zerocopy",
+]
+
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1608,6 +1747,34 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -1724,6 +1891,9 @@ dependencies = [
name = "perspective-client"
version = "4.3.0"
dependencies = [
+ "arrow-array",
+ "arrow-ipc",
+ "arrow-schema",
"async-lock",
"futures",
"getrandom 0.3.4",
@@ -2348,6 +2518,15 @@ version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver 1.0.27",
+]
+
[[package]]
name = "rustix"
version = "1.1.2"
@@ -2476,6 +2655,7 @@ version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
+ "indexmap 2.12.1",
"itoa",
"memchr",
"ryu",
@@ -2808,6 +2988,15 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "tiny-keccak"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
+dependencies = [
+ "crunchy",
+]
+
[[package]]
name = "tokio"
version = "1.48.0"
diff --git a/examples/esbuild-duckdb-virtual/src/index.ts b/examples/esbuild-duckdb-virtual/src/index.ts
index baa7646209..7db1aefa56 100644
--- a/examples/esbuild-duckdb-virtual/src/index.ts
+++ b/examples/esbuild-duckdb-virtual/src/index.ts
@@ -31,7 +31,9 @@ import * as duckdb from "@duckdb/duckdb-wasm";
import SUPERSTORE_ARROW from "superstore-arrow/superstore.lz4.arrow";
await Promise.all([
+ // @ts-ignore
perspective.init_server(fetch(SERVER_WASM)),
+ // @ts-ignore
perspective_viewer.init_client(fetch(CLIENT_WASM)),
]);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a1f72d589f..37473fb53c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -16,8 +16,8 @@ catalogs:
specifier: 6.2.0
version: 6.2.0
'@duckdb/duckdb-wasm':
- specifier: ^1.30.0
- version: 1.32.0
+ specifier: 1.33.1-dev18.0
+ version: 1.33.1-dev18.0
'@fontsource/roboto-mono':
specifier: 4.5.10
version: 4.5.10
@@ -61,8 +61,8 @@ catalogs:
specifier: ^2.7.54
version: 2.8.8
apache-arrow:
- specifier: 18.1.0
- version: 18.1.0
+ specifier: 17.0.0
+ version: 17.0.0
arraybuffer-loader:
specifier: '>=1.0.8 <2'
version: 1.0.8
@@ -405,7 +405,7 @@ importers:
dependencies:
'@duckdb/duckdb-wasm':
specifier: 'catalog:'
- version: 1.32.0
+ version: 1.33.1-dev18.0
'@perspective-dev/client':
specifier: workspace:^
version: link:../../rust/perspective-js
@@ -1038,7 +1038,7 @@ importers:
version: 1.17.0
'@duckdb/duckdb-wasm':
specifier: 'catalog:'
- version: 1.32.0
+ version: 1.33.1-dev18.0
'@perspective-dev/esbuild-plugin':
specifier: 'workspace:'
version: link:../../tools/esbuild-plugin
@@ -1065,7 +1065,7 @@ importers:
version: 8.18.1
apache-arrow:
specifier: 'catalog:'
- version: 18.1.0
+ version: 17.0.0
lodash:
specifier: 'catalog:'
version: 4.17.21
@@ -2607,8 +2607,8 @@ packages:
resolution: {integrity: sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ==}
engines: {node: '>=20.0'}
- '@duckdb/duckdb-wasm@1.32.0':
- resolution: {integrity: sha512-IewXTNYEjsZCPE9weUWgtjGxUlMRo7qhX0GF6tq/KjK8bnY+RAl4cyUdYUfcdzbyb4b9ZxPC+FOsCcxgaKFWMg==}
+ '@duckdb/duckdb-wasm@1.33.1-dev18.0':
+ resolution: {integrity: sha512-BVF+CYmOsrMRY8rnJa0USmC5PujPs2eZovQrSEeHs85J/MZ9mSblhzDih9BPu4MgwUxK9dYbYxKkBIJ4jv3EKw==}
'@esbuild/aix-ppc64@0.25.11':
resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==}
@@ -4172,10 +4172,6 @@ packages:
resolution: {integrity: sha512-X0p7auzdnGuhYMVKYINdQssS4EcKec9TCXyez/qtJt32DrIMGbzqiaMiQ0X6fQlQpw8Fl0Qygcv4dfRAr5Gu9Q==}
hasBin: true
- apache-arrow@18.1.0:
- resolution: {integrity: sha512-v/ShMp57iBnBp4lDgV8Jx3d3Q5/Hac25FWmQ98eMahUiHPXcvwIMKJD0hBIgclm/FCG+LwPkAKtkRO1O/W0YGg==}
- hasBin: true
-
arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
@@ -7745,6 +7741,10 @@ packages:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
+ qs@6.15.0:
+ resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==}
+ engines: {node: '>=0.6'}
+
querystring@0.2.0:
resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==}
engines: {node: '>=0.4.x'}
@@ -11260,9 +11260,10 @@ snapshots:
- uglify-js
- webpack-cli
- '@duckdb/duckdb-wasm@1.32.0':
+ '@duckdb/duckdb-wasm@1.33.1-dev18.0':
dependencies:
apache-arrow: 17.0.0
+ qs: 6.15.0
'@esbuild/aix-ppc64@0.25.11':
optional: true
@@ -13411,18 +13412,6 @@ snapshots:
json-bignum: 0.0.3
tslib: 2.8.1
- apache-arrow@18.1.0:
- dependencies:
- '@swc/helpers': 0.5.17
- '@types/command-line-args': 5.2.3
- '@types/command-line-usage': 5.0.4
- '@types/node': 20.19.23
- command-line-args: 5.2.1
- command-line-usage: 7.0.3
- flatbuffers: 24.12.23
- json-bignum: 0.0.3
- tslib: 2.8.1
-
arg@5.0.2: {}
argparse@1.0.10:
@@ -17641,6 +17630,10 @@ snapshots:
dependencies:
side-channel: 1.1.0
+ qs@6.15.0:
+ dependencies:
+ side-channel: 1.1.0
+
querystring@0.2.0:
optional: true
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index d9a1b37ddf..4609d8d215 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -45,7 +45,7 @@ catalog:
# Dev Dependencies
"@clickhouse/client-web": "^1.12.0"
- "@duckdb/duckdb-wasm": "^1.30.0"
+ "@duckdb/duckdb-wasm": "1.33.1-dev18.0"
"@duckdb/duckdb-wasm-shell": "^1.30.0"
"@fontsource/roboto-mono": "4.5.10"
"@iarna/toml": "3.0.0"
@@ -62,7 +62,7 @@ catalog:
"@types/ws": ">=8"
"@types/chroma-js": "^3.1.2"
"@zip.js/zip.js": "^2.7.54"
- "apache-arrow": "18.1.0"
+ "apache-arrow": "17.0.0"
"arraybuffer-loader": ">=1.0.8 <2"
"auto-changelog": "^2.5.0"
"chalk": ">=5"
diff --git a/rust/perspective-client/Cargo.toml b/rust/perspective-client/Cargo.toml
index a9e1bb9f09..74c1db89f9 100644
--- a/rust/perspective-client/Cargo.toml
+++ b/rust/perspective-client/Cargo.toml
@@ -65,6 +65,9 @@ prost-build = { version = "0.12.3" }
protobuf-src = { version = "2.1.1", optional = true }
[dependencies]
+arrow-array = { version = "57.3.0", default-features = false }
+arrow-ipc = { version = "57.3.0", default-features = false }
+arrow-schema = { version = "57.3.0", default-features = false }
async-lock = { version = "2.5.0" }
futures = { version = "0.3.28" }
indexmap = { version = "2.2.6", features = ["serde"] }
@@ -78,7 +81,7 @@ num-traits = "0.2.19"
rand = { version = ">=0.9,<1" }
serde = { version = "1.0", features = ["derive"] }
serde_bytes = { version = "0.11" }
-serde_json = { version = "1.0.107", features = ["raw_value"] }
+serde_json = { version = "1.0.107", features = ["raw_value", "preserve_order"] }
talc = { version = "4.4.3", features = ["counters"], optional = true }
thiserror = { version = "1.0.55" }
tracing = { version = "0.1.36" }
diff --git a/rust/perspective-client/src/rust/virtual_server/data.rs b/rust/perspective-client/src/rust/virtual_server/data.rs
index 770ef88f60..5b3d33cd51 100644
--- a/rust/perspective-client/src/rust/virtual_server/data.rs
+++ b/rust/perspective-client/src/rust/virtual_server/data.rs
@@ -11,27 +11,32 @@
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
use std::error::Error;
-use std::ops::{Deref, DerefMut};
+use std::sync::Arc;
+use arrow_array::builder::{
+ BooleanBuilder, Float64Builder, Int32Builder, StringBuilder, TimestampMillisecondBuilder,
+};
+use arrow_array::{
+ Array, ArrayRef, BooleanArray, Date32Array, Decimal128Array, Float64Array, Int32Array,
+ Int64Array, RecordBatch, StringArray, TimestampMicrosecondArray, TimestampMillisecondArray,
+ TimestampNanosecondArray, TimestampSecondArray,
+};
+use arrow_ipc::reader::{FileReader, StreamReader};
+use arrow_ipc::writer::StreamWriter;
+use arrow_schema::{DataType, Field, Schema, TimeUnit};
use indexmap::IndexMap;
use serde::Serialize;
-use crate::config::{Scalar, ViewConfig};
+use crate::config::{GroupRollupMode, Scalar, ViewConfig};
-/// A column of data returned from a virtual server query.
-///
-/// Each variant represents a different column type, containing a vector
-/// of optional values. `None` values represent null/missing data.
-#[derive(Debug, Serialize)]
-#[serde(untagged)]
-pub enum VirtualDataColumn {
- Boolean(Vec