From 57a6377fbc9151a411055c8fe548b1fd5b74127f Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Thu, 4 Jun 2026 10:14:30 +0200 Subject: [PATCH 1/8] test(tesseract): PoC integration tests over external pre-aggregations on live CubeStore Add an integration-cubestore feature that runs Tesseract queries fully covered by external pre-aggregations against a locally built cubestored: - cubestore_service.rs spawns a shared cubestored (free ports, temp data dir) and gives each test an isolated schema over mysql_async - TestContext::try_execute_cubestore builds pre-agg tables in Postgres, uploads them into CubeStore (CREATE TABLE + INSERT), then runs the outer query rendered in the CubeStore dialect - MockBaseTools carries external driver tools; cubestore_templates mirrors CubeStoreQuery.sqlTemplates() so external-covered queries render CubeStore SQL - pre-agg YAML indexes parsed into the mock and emitted as INDEX / AGGREGATE INDEX + AGGREGATIONS on the CubeStore table --- rust/cube/Cargo.lock | 423 +++++++++++++++++- .../cubesqlplanner/cubesqlplanner/Cargo.toml | 4 + .../cube_bridge/mock_base_tools.rs | 18 +- .../mock_pre_aggregation_description.rs | 17 + .../cube_bridge/mock_sql_templates_render.rs | 22 + .../src/test_fixtures/cube_bridge/mod.rs | 4 +- .../cube_bridge/yaml/pre_aggregation.rs | 23 +- .../common/integration_cubestore_basic.yaml | 35 ++ .../test_utils/cubestore_service.rs | 142 ++++++ .../test_utils/integration_context.rs | 4 + .../src/test_fixtures/test_utils/mod.rs | 2 + .../test_fixtures/test_utils/test_context.rs | 387 +++++++++++++++- .../src/tests/integration/cubestore/basic.rs | 36 ++ .../src/tests/integration/cubestore/mod.rs | 1 + ...basic__basic_pre_agg_cubestore_result.snap | 10 + .../src/tests/integration/mod.rs | 1 + 16 files changed, 1112 insertions(+), 17 deletions(-) create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/integration_cubestore_basic.yaml create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/basic.rs create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/mod.rs create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap diff --git a/rust/cube/Cargo.lock b/rust/cube/Cargo.lock index 42f0fbffd43f7..34bcccfaa9f24 100644 --- a/rust/cube/Cargo.lock +++ b/rust/cube/Cargo.lock @@ -12,6 +12,12 @@ dependencies = [ "regex", ] +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "ahash" version = "0.8.12" @@ -44,6 +50,12 @@ dependencies = [ "cc", ] +[[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" @@ -345,6 +357,24 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.1", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -425,6 +455,15 @@ dependencies = [ "serde_with", ] +[[package]] +name = "btoi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad" +dependencies = [ + "num-traits", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -456,9 +495,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -551,6 +601,17 @@ dependencies = [ "half", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.6.1" @@ -600,6 +661,15 @@ dependencies = [ "error-code", ] +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "cmov" version = "0.5.3" @@ -710,6 +780,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "criterion" version = "0.8.2" @@ -743,6 +822,56 @@ dependencies = [ "itertools 0.13.0", ] +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[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" @@ -857,6 +986,7 @@ dependencies = [ "itertools 0.10.5", "lazy_static", "minijinja", + "mysql_async", "nativebridge", "neon", "petgraph", @@ -1181,6 +1311,17 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + [[package]] name = "foldhash" version = "0.1.5" @@ -1350,6 +1491,12 @@ dependencies = [ "wasip3", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "half" version = "2.7.1" @@ -1374,6 +1521,8 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", + "equivalent", "foldhash", ] @@ -1536,7 +1685,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.3", "tokio", "tower-service", "tracing", @@ -1810,6 +1959,16 @@ dependencies = [ "syn", ] +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + [[package]] name = "js-sys" version = "0.3.97" @@ -1822,6 +1981,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keyed_priority_queue" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee7893dab2e44ae5f9d0173f26ff4aa327c10b01b06a72b52dd9405b628640d" +dependencies = [ + "indexmap 2.14.0", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1925,6 +2093,17 @@ dependencies = [ "redox_syscall 0.7.4", ] +[[package]] +name = "libz-sys" +version = "1.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bc9657773828b90eeb625adff10eeac83cc21bbfd8e23a03eaa8a33c9e28d9" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linkme" version = "0.3.36" @@ -1972,6 +2151,15 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "lru-slab" version = "0.1.2" @@ -2028,6 +2216,22 @@ dependencies = [ "serde_json", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.0" @@ -2039,6 +2243,68 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "mysql_async" +version = "0.34.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b66e411c31265e879d9814d03721f2daa7ad07337b6308cb4bb0cde7e6fd47" +dependencies = [ + "bytes", + "crossbeam", + "flate2", + "futures-core", + "futures-sink", + "futures-util", + "keyed_priority_queue", + "lru", + "mysql_common", + "pem", + "percent-encoding", + "pin-project", + "rand 0.8.6", + "serde", + "serde_json", + "socket2 0.5.10", + "thiserror 1.0.69", + "tokio", + "tokio-util", + "twox-hash", + "url", +] + +[[package]] +name = "mysql_common" +version = "0.32.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478b0ff3f7d67b79da2b96f56f334431aef65e15ba4b29dd74a4236e29582bdc" +dependencies = [ + "base64 0.21.7", + "bindgen", + "bitflags 2.11.1", + "btoi", + "byteorder", + "bytes", + "cc", + "cmake", + "crc32fast", + "flate2", + "lazy_static", + "num-bigint", + "num-traits", + "rand 0.8.6", + "regex", + "saturating", + "serde", + "serde_json", + "sha1", + "sha2 0.10.9", + "smallvec", + "subprocess", + "thiserror 1.0.69", + "uuid 1.23.1", + "zstd", +] + [[package]] name = "native-tls" version = "0.2.18" @@ -2118,6 +2384,16 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -2320,6 +2596,16 @@ dependencies = [ "regex", ] +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64 0.22.1", + "serde_core", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -2393,6 +2679,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -2440,7 +2746,7 @@ dependencies = [ "md-5", "memchr", "rand 0.10.1", - "sha2", + "sha2 0.11.0", "stringprep", ] @@ -2511,7 +2817,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", + "socket2 0.6.3", "thiserror 2.0.18", "tokio", "tracing", @@ -2548,7 +2854,7 @@ dependencies = [ "cfg_aliases 0.2.1", "libc", "once_cell", - "socket2", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] @@ -2590,6 +2896,8 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ + "libc", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -2599,7 +2907,7 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -2614,6 +2922,16 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -2629,6 +2947,9 @@ 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" @@ -2949,6 +3270,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "saturating" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece8e78b2f38ec51c51f5d475df0a7187ba5111b2a28bdc761ee05b075d40a71" + [[package]] name = "schannel" version = "0.1.29" @@ -3150,6 +3477,17 @@ dependencies = [ "digest 0.10.7", ] +[[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 0.10.7", +] + [[package]] name = "sha2" version = "0.11.0" @@ -3177,6 +3515,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "similar" version = "2.7.0" @@ -3201,6 +3545,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.3" @@ -3217,6 +3571,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stringprep" version = "0.1.5" @@ -3257,6 +3617,16 @@ dependencies = [ "syn", ] +[[package]] +name = "subprocess" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c56e8662b206b9892d7a5a3f2ecdbcb455d3d6b259111373b7e08b8055158a8" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "subtle" version = "2.6.1" @@ -3472,7 +3842,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.3", "tokio-macros", "tracing", "windows-sys 0.61.2", @@ -3519,7 +3889,7 @@ dependencies = [ "postgres-protocol", "postgres-types", "rand 0.10.1", - "socket2", + "socket2 0.6.3", "tokio", "tokio-util", "whoami", @@ -3675,6 +4045,17 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "rand 0.8.6", + "static_assertions", +] + [[package]] name = "typed-builder" version = "0.21.2" @@ -4552,3 +4933,31 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[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/rust/cube/cubesqlplanner/cubesqlplanner/Cargo.toml b/rust/cube/cubesqlplanner/cubesqlplanner/Cargo.toml index ec5307d33f721..e8be886f143f6 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/Cargo.toml +++ b/rust/cube/cubesqlplanner/cubesqlplanner/Cargo.toml @@ -27,6 +27,9 @@ indexmap = "2.12" [features] integration-postgres = [] +# Runs queries over external pre-aggregations against a locally built +# cubestored binary (CUBESTORED_BIN_PATH or rust/cubestore/target/*). +integration-cubestore = ["integration-postgres"] [dev-dependencies] petgraph = "0.6" @@ -35,6 +38,7 @@ insta = "1.34" testcontainers = "0.23" testcontainers-modules = { version = "0.11", features = ["postgres"] } tokio-postgres = "0.7" +mysql_async = { version = "0.34", default-features = false, features = ["minimal"] } [dependencies.neon] version = "=1" diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_base_tools.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_base_tools.rs index 4140d81bf1a06..347d6d355b9fb 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_base_tools.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_base_tools.rs @@ -27,6 +27,11 @@ pub struct MockBaseTools { #[builder(default = Rc::new(MockDriverTools::new()))] driver_tools: Rc, + /// Driver tools returned for `driver_tools(external: true)` — the + /// external pre-aggregations dialect (CubeStore in production). + #[builder(default)] + external_driver_tools: Option>, + #[builder(default = Rc::new(MockSqlTemplatesRender::default_templates()))] sql_templates: Rc, @@ -41,6 +46,12 @@ pub struct MockBaseTools { cube_members: HashMap>, } +impl MockBaseTools { + pub fn set_external_driver_tools(&mut self, tools: Rc) { + self.external_driver_tools = Some(tools); + } +} + impl Default for MockBaseTools { fn default() -> Self { Self::builder().build() @@ -52,7 +63,12 @@ impl BaseTools for MockBaseTools { self } - fn driver_tools(&self, _external: bool) -> Result, CubeError> { + fn driver_tools(&self, external: bool) -> Result, CubeError> { + if external { + if let Some(tools) = &self.external_driver_tools { + return Ok(tools.clone()); + } + } Ok(self.driver_tools.clone()) } diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_pre_aggregation_description.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_pre_aggregation_description.rs index e7295d03d4479..73b3df6600493 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_pre_aggregation_description.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_pre_aggregation_description.rs @@ -10,6 +10,15 @@ use std::any::Any; use std::rc::Rc; use typed_builder::TypedBuilder; +/// Mirrors the JS pre-aggregation description's `createTableIndexes` entries: +/// member references resolved to columns at table creation time. +#[derive(Debug, Clone)] +pub struct MockPreAggregationIndex { + pub name: String, + pub columns: Vec, + pub index_type: String, +} + #[derive(TypedBuilder)] pub struct MockPreAggregationDescription { name: String, @@ -36,6 +45,14 @@ pub struct MockPreAggregationDescription { rollup_references: Option>, #[builder(default)] time_dimension_references: Option>>, + #[builder(default)] + indexes: Vec, +} + +impl MockPreAggregationDescription { + pub fn indexes(&self) -> &[MockPreAggregationIndex] { + &self.indexes + } } impl_static_data!( diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_sql_templates_render.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_sql_templates_render.rs index 7ed6e94476523..5e246a5f8cdb3 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_sql_templates_render.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_sql_templates_render.rs @@ -569,6 +569,28 @@ impl MockSqlTemplatesRender { render.jinja = jinja; render } + + /// Templates matching `CubeStoreQuery.sqlTemplates()` from the JS + /// schema-compiler: BaseQuery defaults plus CubeStore-specific overrides. + pub fn cubestore_templates() -> Self { + let render = Self::default_templates(); + let mut templates = render.templates; + templates.insert( + "statements/time_series_select".to_string(), + concat!( + "{% for time_item in seria %}", + "select to_timestamp('{{ time_item[0] }}') date_from, to_timestamp('{{ time_item[1] }}') date_to \n", + "{% if not loop.last %} UNION ALL\n{% endif %}", + "{% endfor %}" + ) + .to_string(), + ); + templates.insert( + "operators/is_not_distinct_from".to_string(), + "IS NOT DISTINCT FROM".to_string(), + ); + Self::try_new(templates).expect("CubeStore templates should always parse successfully") + } } impl SqlTemplatesRender for MockSqlTemplatesRender { diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs index e3329aab14ee3..27a41196d8c98 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs @@ -65,7 +65,9 @@ pub use mock_member_order_by::MockMemberOrderBy; pub use mock_member_sql::MockMemberSql; pub use mock_multi_stage_filter::MockMultiStageFilterReferences; pub use mock_multi_stage_grain::MockMultiStageGrainReferences; -pub use mock_pre_aggregation_description::MockPreAggregationDescription; +pub use mock_pre_aggregation_description::{ + MockPreAggregationDescription, MockPreAggregationIndex, +}; pub use mock_pre_aggregation_time_dimension::MockPreAggregationTimeDimension; pub use mock_schema::{MockSchema, MockSchemaBuilder}; pub use mock_security_context::MockSecurityContext; diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/yaml/pre_aggregation.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/yaml/pre_aggregation.rs index 3df3da5d77218..0259090495f5a 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/yaml/pre_aggregation.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/yaml/pre_aggregation.rs @@ -1,7 +1,8 @@ use crate::cube_bridge::member_sql::MemberSql; use crate::test_fixtures::cube_bridge::yaml::pre_aggregation_time_dimension::YamlPreAggregationTimeDimension; use crate::test_fixtures::cube_bridge::{ - MockMemberSql, MockPreAggregationDescription, MockPreAggregationTimeDimension, + MockMemberSql, MockPreAggregationDescription, MockPreAggregationIndex, + MockPreAggregationTimeDimension, }; use cubenativeutils::CubeError; use serde::Deserialize; @@ -54,7 +55,6 @@ pub struct YamlPreAggregationDefinition { #[allow(dead_code)] union_with_source_data: Option, #[serde(default)] - #[allow(dead_code)] indexes: Option>, #[serde(default)] rollups: Option>, @@ -72,10 +72,15 @@ struct YamlRefreshKey { } #[derive(Debug, Deserialize)] -#[allow(dead_code)] struct YamlIndex { name: String, columns: Vec, + #[serde(rename = "type", default = "default_index_type")] + index_type: String, +} + +fn default_index_type() -> String { + "regular".to_string() } fn default_type() -> String { @@ -118,6 +123,17 @@ impl YamlPreAggregationDefinition { .transpose() .expect("Failed to build rollup references"); + let indexes = self + .indexes + .unwrap_or_default() + .into_iter() + .map(|i| MockPreAggregationIndex { + name: i.name, + columns: i.columns, + index_type: i.index_type, + }) + .collect::>(); + Rc::new( MockPreAggregationDescription::builder() .name(name) @@ -132,6 +148,7 @@ impl YamlPreAggregationDefinition { .segment_references_opt(segment_references) .rollup_references_opt(rollup_references) .time_dimension_references(time_dimension_references) + .indexes(indexes) .build(), ) } diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/integration_cubestore_basic.yaml b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/integration_cubestore_basic.yaml new file mode 100644 index 0000000000000..ddeff08dbd979 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/integration_cubestore_basic.yaml @@ -0,0 +1,35 @@ +cubes: + - name: visitors + sql: "SELECT * FROM visitors" + dimensions: + - name: id + type: number + sql: id + primary_key: true + - name: source + type: string + sql: source + - name: created_at + type: time + sql: created_at + measures: + - name: count + type: count + pre_aggregations: + - name: daily_rollup + type: rollup + external: true + measures: + - count + dimensions: + - source + time_dimension: created_at + granularity: day + indexes: + - name: source_idx + columns: + - source + - name: source_agg_idx + columns: + - source + type: aggregate diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs new file mode 100644 index 0000000000000..901f2bc452557 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs @@ -0,0 +1,142 @@ +use std::net::TcpListener; +use std::path::PathBuf; +use std::process::{Child, Command, Stdio}; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::OnceLock; +use std::time::{Duration, Instant}; +use tokio::sync::OnceCell; + +struct CubeStoreInstance { + mysql_port: u16, + _child: Child, +} + +static CLEANUP_PID: OnceLock = OnceLock::new(); + +extern "C" fn cleanup_cubestored() { + if let Some(pid) = CLEANUP_PID.get() { + let _ = Command::new("kill").arg(pid.to_string()).output(); + } +} + +fn register_atexit_cleanup(pid: u32) { + CLEANUP_PID.set(pid).ok(); + extern "C" { + fn atexit(cb: extern "C" fn()) -> std::os::raw::c_int; + } + unsafe { + atexit(cleanup_cubestored); + } +} + +static CS_INSTANCE: OnceCell = OnceCell::const_new(); +static SCHEMA_COUNTER: AtomicU64 = AtomicU64::new(0); + +fn cubestored_bin() -> PathBuf { + if let Ok(path) = std::env::var("CUBESTORED_BIN_PATH") { + let path = PathBuf::from(path); + if path.exists() { + return path; + } + panic!("CUBESTORED_BIN_PATH points to a missing file: {:?}", path); + } + + let cubestore_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../cubestore"); + for candidate in [ + cubestore_root.join("target/release/cubestored"), + cubestore_root.join("target/debug/cubestored"), + cubestore_root.join("downloaded/latest/bin/cubestored"), + ] { + if candidate.exists() { + return candidate; + } + } + + panic!( + "cubestored binary not found. Build it with \ + `cargo build -p cubestore --bin cubestored` in rust/cubestore \ + or set CUBESTORED_BIN_PATH" + ); +} + +fn free_port() -> u16 { + TcpListener::bind("127.0.0.1:0") + .expect("Failed to bind to a free port") + .local_addr() + .expect("Failed to get local addr") + .port() +} + +async fn init_cubestore() -> CubeStoreInstance { + let bin = cubestored_bin(); + let mysql_port = free_port(); + let http_port = free_port(); + let status_port = free_port(); + + let data_dir = std::env::temp_dir().join(format!( + "cubestored-test-{}-{}", + std::process::id(), + mysql_port + )); + + let child = Command::new(&bin) + .env("CUBESTORE_PORT", mysql_port.to_string()) + .env("CUBESTORE_HTTP_PORT", http_port.to_string()) + .env("CUBESTORE_STATUS_PORT", status_port.to_string()) + .env("CUBESTORE_BIND_ADDR", format!("127.0.0.1:{}", mysql_port)) + .env( + "CUBESTORE_HTTP_BIND_ADDR", + format!("127.0.0.1:{}", http_port), + ) + .env( + "CUBESTORE_STATUS_BIND_ADDR", + format!("127.0.0.1:{}", status_port), + ) + .env("CUBESTORE_DATA_DIR", &data_dir) + .env("CUBESTORE_SELECT_WORKERS", "0") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .unwrap_or_else(|e| panic!("Failed to spawn cubestored from {:?}: {}", bin, e)); + + register_atexit_cleanup(child.id()); + + let deadline = Instant::now() + Duration::from_secs(60); + loop { + if std::net::TcpStream::connect(("127.0.0.1", mysql_port)).is_ok() { + break; + } + if Instant::now() > deadline { + panic!( + "cubestored did not open MySQL port {} within 60s (binary: {:?})", + mysql_port, bin + ); + } + tokio::time::sleep(Duration::from_millis(100)).await; + } + + CubeStoreInstance { + mysql_port, + _child: child, + } +} + +/// Connects to the shared cubestored instance and creates a fresh +/// per-test schema; returns the connection and the schema name. +pub async fn connect_with_schema() -> (mysql_async::Conn, String) { + let instance = CS_INSTANCE.get_or_init(init_cubestore).await; + + let url = format!("mysql://root:@127.0.0.1:{}/", instance.mysql_port); + let opts = mysql_async::Opts::from_url(&url).expect("Invalid cubestore connection URL"); + let mut conn = mysql_async::Conn::new(opts) + .await + .expect("Failed to connect to cubestored"); + + let schema = format!("test_{}", SCHEMA_COUNTER.fetch_add(1, Ordering::Relaxed)); + use mysql_async::prelude::Queryable; + conn.query_drop(format!("CREATE SCHEMA {}", schema)) + .await + .unwrap_or_else(|e| panic!("Failed to create schema {}: {}", schema, e)); + + (conn, schema) +} diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/integration_context.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/integration_context.rs index 236bc79900f78..0e3efbe8dba46 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/integration_context.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/integration_context.rs @@ -20,6 +20,10 @@ pub fn format_simple_query_results(messages: &[tokio_postgres::SimpleQueryMessag } } + format_rows_table(columns, rows) +} + +pub fn format_rows_table(columns: Vec, rows: Vec>) -> String { if columns.is_empty() { return "(empty result)".to_string(); } diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/mod.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/mod.rs index 20ee65ce6e347..b6a5927918606 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/mod.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/mod.rs @@ -1,5 +1,7 @@ mod test_context; +#[cfg(feature = "integration-cubestore")] +pub(crate) mod cubestore_service; #[cfg(feature = "integration-postgres")] pub(crate) mod integration_context; #[cfg(feature = "integration-postgres")] diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs index a9656e9fbd0c9..7e1891e94f816 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs @@ -33,6 +33,10 @@ pub struct TestContext { /// `statements/generated_time_series_select` templates injected by /// `new_with_generated_time_series`. custom_sql_templates: Option, + /// When set, `driver_tools(external: true)` resolves to the CubeStore + /// dialect templates, so queries fully covered by external + /// pre-aggregations render CubeStore SQL. + external_cubestore: bool, } impl TestContext { @@ -40,6 +44,28 @@ impl TestContext { Self::new_with_options(schema, Tz::UTC, None, None, false, false) } + #[allow(dead_code)] + pub fn new_with_external_cubestore(schema: MockSchema) -> Result { + let mut ctx = Self::new(schema)?; + ctx.external_cubestore = true; + ctx.rebuild_with_external_cubestore() + } + + /// Rebuilds query tools so that MockBaseTools carries CubeStore + /// external driver tools alongside the default ones. + fn rebuild_with_external_cubestore(self) -> Result { + Self::new_with_options_internal_ext( + self.schema.clone(), + Tz::UTC, + None, + None, + false, + false, + self.custom_sql_templates.clone(), + true, + ) + } + #[allow(dead_code)] pub fn new_with_base_tools( schema: MockSchema, @@ -67,6 +93,7 @@ impl TestContext { query_tools, security_context, custom_sql_templates: None, + external_cubestore: false, }) } @@ -108,7 +135,7 @@ impl TestContext { .and_then(|tz| tz.parse::().ok()) .unwrap_or(Tz::UTC); - Self::new_with_options_internal( + Self::new_with_options_internal_ext( self.schema.clone(), timezone, static_data.masked_members.clone(), @@ -118,6 +145,7 @@ impl TestContext { .convert_tz_for_raw_time_dimension .unwrap_or(false), self.custom_sql_templates.clone(), + self.external_cubestore, ) } @@ -129,7 +157,7 @@ impl TestContext { export_annotated_sql: bool, convert_tz_for_raw_time_dimension: bool, ) -> Result { - Self::new_with_options_internal( + Self::new_with_options_internal_ext( schema, timezone, masked_members, @@ -137,11 +165,12 @@ impl TestContext { export_annotated_sql, convert_tz_for_raw_time_dimension, None, + false, ) } #[allow(clippy::too_many_arguments)] - fn new_with_options_internal( + fn new_with_options_internal_ext( schema: MockSchema, timezone: Tz, masked_members: Option>, @@ -149,15 +178,24 @@ impl TestContext { export_annotated_sql: bool, convert_tz_for_raw_time_dimension: bool, custom_sql_templates: Option, + external_cubestore: bool, ) -> Result { - let base_tools = if let Some(templates) = custom_sql_templates.clone() { - use crate::test_fixtures::cube_bridge::MockDriverTools; + use crate::test_fixtures::cube_bridge::MockDriverTools; + let mut base_tools = if let Some(templates) = custom_sql_templates.clone() { let driver_tools = MockDriverTools::with_sql_templates_and_timezone(templates, timezone.to_string()); schema.create_base_tools_with_driver(driver_tools)? } else { schema.create_base_tools_with_timezone(timezone.to_string())? }; + if external_cubestore { + use crate::test_fixtures::cube_bridge::MockSqlTemplatesRender; + let driver_tools = MockDriverTools::with_sql_templates_and_timezone( + MockSqlTemplatesRender::cubestore_templates(), + timezone.to_string(), + ); + base_tools.set_external_driver_tools(Rc::new(driver_tools)); + } let join_graph = Rc::new(schema.create_join_graph()?); let evaluator = schema.clone().create_evaluator(); let security_context: Rc = @@ -180,6 +218,7 @@ impl TestContext { query_tools, security_context, custom_sql_templates, + external_cubestore, }) } @@ -658,6 +697,344 @@ impl TestContext { None } + /// Executes the query against a live CubeStore instance: source data and + /// pre-aggregation tables are built in Postgres, then uploaded into + /// CubeStore (CREATE TABLE + INSERT, mirroring the driver's rows-based + /// path), and the outer query rendered in the CubeStore dialect runs + /// there. Requires the context to be created with + /// `new_with_external_cubestore` and all matched pre-aggregations to be + /// `external: true`. + #[cfg(feature = "integration-cubestore")] + pub async fn try_execute_cubestore(&self, query_yaml: &str, seed_file: &str) -> Option { + use mysql_async::prelude::Queryable; + + let options = self.create_query_options_from_yaml(query_yaml); + let client = super::pg_service::connect_and_seed(seed_file).await; + + let ctx = self + .for_options(options.as_ref()) + .expect("Failed to create context"); + let request = QueryPropertiesCompiler::new(ctx.query_tools.clone()) + .build(options) + .expect("Failed to create query properties"); + let planner = TopLevelPlanner::new(request, ctx.query_tools.clone(), true); + let (raw_sql, pre_aggregations) = planner.plan().expect("Failed to plan query"); + + assert!( + !pre_aggregations.is_empty(), + "CubeStore execution requires the query to be covered by pre-aggregations" + ); + assert!( + pre_aggregations + .iter() + .all(|u| u.pre_aggregation.external()), + "CubeStore execution requires all matched pre-aggregations to be external" + ); + + self.create_pre_agg_tables(&client, &pre_aggregations).await; + + let (mut conn, cs_schema) = super::cubestore_service::connect_with_schema().await; + let table_names = self + .upload_pre_agg_tables_to_cubestore(&client, &mut conn, &cs_schema, &pre_aggregations) + .await; + + let templates = ctx + .query_tools + .plan_sql_templates(true) + .expect("Failed to get SQL templates"); + let (sql, params) = ctx + .query_tools + .build_sql_and_params(&raw_sql, true, &templates) + .expect("Failed to build SQL and params"); + + let sql = pre_aggregations + .iter() + .fold(sql, |s, u| s.replace(&format!("__usage_{}", u.index), "")); + let mut final_sql = Self::inline_params(&sql, ¶ms); + + // CubeStore requires schema-qualified table names + for table in &table_names { + let re = regex::Regex::new(&format!(r#"(FROM|JOIN)(\s+)("?){}("?)"#, table)) + .expect("Failed to build table name regex"); + final_sql = re + .replace_all( + &final_sql, + format!("${{1}}${{2}}{}.${{3}}{}${{4}}", cs_schema, table), + ) + .into_owned(); + } + + let rows: Vec = conn.query(&final_sql).await.unwrap_or_else(|e| { + panic!( + "CubeStore SQL execution failed:\n{}\n\nError: {:?}", + final_sql, e + ) + }); + + let columns: Vec = rows + .first() + .map(|r| { + r.columns_ref() + .iter() + .map(|c| c.name_str().to_string()) + .collect() + }) + .unwrap_or_default(); + let formatted_rows: Vec> = rows + .iter() + .map(|r| { + (0..r.columns_ref().len()) + .map(|i| Self::mysql_value_to_string(r.as_ref(i))) + .collect() + }) + .collect(); + + Some(super::integration_context::format_rows_table( + columns, + formatted_rows, + )) + } + + #[cfg(not(feature = "integration-cubestore"))] + pub async fn try_execute_cubestore( + &self, + _query_yaml: &str, + _seed_file: &str, + ) -> Option { + None + } + + #[cfg(feature = "integration-cubestore")] + async fn upload_pre_agg_tables_to_cubestore( + &self, + client: &tokio_postgres::Client, + conn: &mut mysql_async::Conn, + cs_schema: &str, + pre_aggregations: &[PreAggregationUsage], + ) -> Vec { + use itertools::Itertools; + use mysql_async::prelude::Queryable; + use std::collections::HashSet; + + let mut created: Vec = Vec::new(); + let mut seen: HashSet = HashSet::new(); + + for usage in pre_aggregations { + let pre_agg = &usage.pre_aggregation; + let tables = Self::collect_pre_agg_source_tables(pre_agg.source()); + for table in &tables { + let name = table.alias.clone().unwrap_or_else(|| table.name.clone()); + let table_name = + PlanSqlTemplates::alias_name(&format!("{}.{}", table.cube_name, name)); + if !seen.insert(table_name.clone()) { + continue; + } + + let stmt = client + .prepare(&format!("SELECT * FROM \"{}\"", table_name)) + .await + .unwrap_or_else(|e| { + panic!("Failed to introspect pre-agg table {}: {}", table_name, e) + }); + let columns: Vec<(String, &'static str)> = stmt + .columns() + .iter() + .map(|c| (c.name().to_string(), Self::cubestore_type(c.type_()))) + .collect(); + + let messages = client + .simple_query(&format!("SELECT * FROM \"{}\"", table_name)) + .await + .unwrap_or_else(|e| { + panic!("Failed to read pre-agg table {}: {}", table_name, e) + }); + + let cols_sql = columns + .iter() + .map(|(n, t)| format!("\"{}\" {}", n, t)) + .join(", "); + let extra_sql = + self.cubestore_table_extras_sql(&table.cube_name, &name, pre_agg, &columns); + let create_sql = format!( + "CREATE TABLE {}.\"{}\" ({}){}", + cs_schema, table_name, cols_sql, extra_sql + ); + conn.query_drop(&create_sql).await.unwrap_or_else(|e| { + panic!( + "Failed to create CubeStore table:\n{}\n\nError: {:?}", + create_sql, e + ) + }); + + let mut values: Vec = Vec::new(); + for msg in &messages { + if let tokio_postgres::SimpleQueryMessage::Row(row) = msg { + let row_values = (0..columns.len()) + .map(|i| { + Self::cubestore_literal( + row.try_get(i).unwrap_or(None), + columns[i].1, + ) + }) + .join(", "); + values.push(format!("({})", row_values)); + } + } + if !values.is_empty() { + let insert_sql = format!( + "INSERT INTO {}.\"{}\" ({}) VALUES {}", + cs_schema, + table_name, + columns.iter().map(|(n, _)| format!("\"{}\"", n)).join(", "), + values.join(", ") + ); + conn.query_drop(&insert_sql).await.unwrap_or_else(|e| { + panic!( + "Failed to insert into CubeStore table {}:\n{}\n\nError: {:?}", + table_name, insert_sql, e + ) + }); + } + + created.push(table_name); + } + } + + created + } + + /// Renders the CubeStore CREATE TABLE tail: `AGGREGATIONS (...)` plus + /// `[AGGREGATE ]INDEX name (cols)` clauses from the mock pre-aggregation + /// indexes, mirroring the JS `createTableIndexes` flow. + #[cfg(feature = "integration-cubestore")] + fn cubestore_table_extras_sql( + &self, + cube_name: &str, + pre_agg_name: &str, + pre_agg: &PreAggregation, + columns: &[(String, &'static str)], + ) -> String { + use itertools::Itertools; + + let Some(desc) = self.schema.get_pre_aggregation(cube_name, pre_agg_name) else { + return String::new(); + }; + if desc.indexes().is_empty() { + return String::new(); + } + + let resolve_column = |member_ref: &str| -> String { + let full = if member_ref.contains('.') { + member_ref.to_string() + } else { + format!("{}.{}", cube_name, member_ref) + }; + let alias = PlanSqlTemplates::alias_name(&full); + columns + .iter() + .find(|(n, _)| *n == alias) + // time dimensions carry a granularity suffix in the table + .or_else(|| { + columns + .iter() + .find(|(n, _)| n.starts_with(&format!("{}_", alias))) + }) + .map(|(n, _)| format!("\"{}\"", n)) + .unwrap_or_else(|| { + panic!( + "Index column {} not found among pre-agg table columns {:?}", + member_ref, columns + ) + }) + }; + + let mut result = String::new(); + let has_aggregate_index = desc.indexes().iter().any(|i| i.index_type == "aggregate"); + + if has_aggregate_index { + let aggregations: Vec = pre_agg + .measures() + .iter() + .filter_map(|m| { + let func = match m.as_measure().ok()?.measure_type() { + "count" | "sum" => "sum", + "min" => "min", + "max" => "max", + _ => return None, + }; + let alias = m.alias(); + let column = columns.iter().find(|(n, _)| *n == alias)?; + Some(format!("{}(\"{}\")", func, column.0)) + }) + .collect(); + if !aggregations.is_empty() { + result.push_str(&format!(" AGGREGATIONS ({})", aggregations.join(", "))); + } + } + + for index in desc.indexes() { + let cols = index.columns.iter().map(|c| resolve_column(c)).join(", "); + let prefix = if index.index_type == "aggregate" { + "AGGREGATE " + } else { + "" + }; + result.push_str(&format!(" {}INDEX {} ({})", prefix, index.name, cols)); + } + + result + } + + #[cfg(feature = "integration-cubestore")] + fn cubestore_type(pg_type: &tokio_postgres::types::Type) -> &'static str { + use tokio_postgres::types::Type; + match *pg_type { + Type::INT2 | Type::INT4 | Type::INT8 => "bigint", + Type::FLOAT4 | Type::FLOAT8 => "double", + Type::NUMERIC => "decimal", + Type::BOOL => "boolean", + Type::TIMESTAMP | Type::TIMESTAMPTZ | Type::DATE => "timestamp", + _ => "varchar", + } + } + + #[cfg(feature = "integration-cubestore")] + fn cubestore_literal(value: Option<&str>, cubestore_type: &str) -> String { + let Some(value) = value else { + return "NULL".to_string(); + }; + match cubestore_type { + "bigint" | "double" | "decimal" => value.to_string(), + "boolean" => (if value == "t" { "true" } else { "false" }).to_string(), + "timestamp" => format!("'{}'", value.trim_end_matches("+00")), + _ => format!("'{}'", value.replace('\'', "''")), + } + } + + #[cfg(feature = "integration-cubestore")] + fn mysql_value_to_string(value: Option<&mysql_async::Value>) -> String { + use mysql_async::Value; + match value { + None | Some(Value::NULL) => "NULL".to_string(), + Some(Value::Bytes(bytes)) => String::from_utf8_lossy(bytes).into_owned(), + Some(Value::Int(v)) => v.to_string(), + Some(Value::UInt(v)) => v.to_string(), + Some(Value::Float(v)) => v.to_string(), + Some(Value::Double(v)) => v.to_string(), + Some(Value::Date(y, m, d, h, min, s, micros)) => { + if *micros == 0 { + format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}", y, m, d, h, min, s) + } else { + format!( + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:06}", + y, m, d, h, min, s, micros + ) + } + } + Some(other) => format!("{:?}", other), + } + } + #[cfg(feature = "integration-postgres")] fn inline_params(sql: &str, params: &[String]) -> String { let mut result = sql.to_string(); diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/basic.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/basic.rs new file mode 100644 index 0000000000000..712a8efef0cac --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/basic.rs @@ -0,0 +1,36 @@ +//! Queries over external pre-aggregations executed against a live +//! CubeStore instance (requires `--features integration-cubestore` +//! and a locally built `cubestored` binary). + +use crate::test_fixtures::cube_bridge::MockSchema; +use crate::test_fixtures::test_utils::TestContext; +use indoc::indoc; + +#[tokio::test(flavor = "multi_thread")] +async fn test_basic_pre_agg_cubestore() { + let schema = MockSchema::from_yaml_file("common/integration_cubestore_basic.yaml"); + let ctx = TestContext::new_with_external_cubestore(schema).unwrap(); + + let query_yaml = indoc! {" + measures: + - visitors.count + dimensions: + - visitors.source + order: + - id: visitors.source + "}; + + let (_sql, pre_aggrs) = ctx + .build_sql_with_used_pre_aggregations(query_yaml) + .unwrap(); + + assert_eq!(pre_aggrs.len(), 1, "Should use one pre-aggregation"); + assert_eq!(pre_aggrs[0].name(), "daily_rollup"); + + if let Some(result) = ctx + .try_execute_cubestore(query_yaml, "pre_aggregation_tables.sql") + .await + { + insta::assert_snapshot!("basic_pre_agg_cubestore_result", result); + } +} diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/mod.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/mod.rs new file mode 100644 index 0000000000000..1bca5f8cba779 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/mod.rs @@ -0,0 +1 @@ +mod basic; diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap new file mode 100644 index 0000000000000..62145a0ccfd3d --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap @@ -0,0 +1,10 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/basic.rs +expression: result +--- +visitors__source | visitors__count +-----------------+---------------- +google | 5 +organic | 2 +twitter | 2 +NULL | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/mod.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/mod.rs index 2ac72dbd1b71a..3d93a9e2731de 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/mod.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/mod.rs @@ -4,6 +4,7 @@ mod calc_groups; mod calendar; mod chained_subquery; mod combinations; +mod cubestore; mod custom_granularities; mod filtered_measures; mod filters_segments; From b6f572283a3bd6b04a80211e09790231af22745c Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 5 Jun 2026 09:38:34 +0200 Subject: [PATCH 2/8] test(tesseract): load cubestore pre-agg data via CSV + LOCATION Replace CREATE TABLE + INSERT with writing a local CSV and loading it through CREATE TABLE ... LOCATION, the same import pipeline production rollups use. Values are serialized to match the JS postgres type parsers (timestamp -> YYYY-MM-DDTHH:mm:ss.SSS, bool true/false, NULL as empty field), which removes the timestamptz trim workaround. Also harden the schema-qualifying regex with a trailing token boundary to avoid prefix-name collisions, and remove the cubestored temp data dir on exit. --- .../test_utils/cubestore_service.rs | 9 +- .../test_fixtures/test_utils/test_context.rs | 150 ++++++++++++------ 2 files changed, 110 insertions(+), 49 deletions(-) diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs index 901f2bc452557..8fc9e37dc1f1a 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs @@ -12,15 +12,20 @@ struct CubeStoreInstance { } static CLEANUP_PID: OnceLock = OnceLock::new(); +static CLEANUP_DATA_DIR: OnceLock = OnceLock::new(); extern "C" fn cleanup_cubestored() { if let Some(pid) = CLEANUP_PID.get() { let _ = Command::new("kill").arg(pid.to_string()).output(); } + if let Some(dir) = CLEANUP_DATA_DIR.get() { + let _ = std::fs::remove_dir_all(dir); + } } -fn register_atexit_cleanup(pid: u32) { +fn register_atexit_cleanup(pid: u32, data_dir: PathBuf) { CLEANUP_PID.set(pid).ok(); + CLEANUP_DATA_DIR.set(data_dir).ok(); extern "C" { fn atexit(cb: extern "C" fn()) -> std::os::raw::c_int; } @@ -99,7 +104,7 @@ async fn init_cubestore() -> CubeStoreInstance { .spawn() .unwrap_or_else(|e| panic!("Failed to spawn cubestored from {:?}: {}", bin, e)); - register_atexit_cleanup(child.id()); + register_atexit_cleanup(child.id(), data_dir.clone()); let deadline = Instant::now() + Duration::from_secs(60); loop { diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs index 7e1891e94f816..5f2186e800fdc 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs @@ -698,12 +698,11 @@ impl TestContext { } /// Executes the query against a live CubeStore instance: source data and - /// pre-aggregation tables are built in Postgres, then uploaded into - /// CubeStore (CREATE TABLE + INSERT, mirroring the driver's rows-based - /// path), and the outer query rendered in the CubeStore dialect runs - /// there. Requires the context to be created with - /// `new_with_external_cubestore` and all matched pre-aggregations to be - /// `external: true`. + /// pre-aggregation tables are built in Postgres, loaded into CubeStore as + /// CSV via `CREATE TABLE ... LOCATION`, and the outer query rendered in + /// the CubeStore dialect runs there. Requires the context to be created + /// with `new_with_external_cubestore` and all matched pre-aggregations to + /// be `external: true`. #[cfg(feature = "integration-cubestore")] pub async fn try_execute_cubestore(&self, query_yaml: &str, seed_file: &str) -> Option { use mysql_async::prelude::Queryable; @@ -752,14 +751,20 @@ impl TestContext { .fold(sql, |s, u| s.replace(&format!("__usage_{}", u.index), "")); let mut final_sql = Self::inline_params(&sql, ¶ms); - // CubeStore requires schema-qualified table names + // CubeStore requires schema-qualified table names. The trailing + // boundary group ($5) guards against one table name being a prefix + // of another (`visitors` vs `visitors_daily`); the regex crate has + // no lookahead, so the boundary char is captured and re-emitted. for table in &table_names { - let re = regex::Regex::new(&format!(r#"(FROM|JOIN)(\s+)("?){}("?)"#, table)) - .expect("Failed to build table name regex"); + let re = regex::Regex::new(&format!( + r#"(FROM|JOIN)(\s+)("?){}("?)([\s,)]|$)"#, + regex::escape(table) + )) + .expect("Failed to build table name regex"); final_sql = re .replace_all( &final_sql, - format!("${{1}}${{2}}{}.${{3}}{}${{4}}", cs_schema, table), + format!("${{1}}${{2}}{}.${{3}}{}${{4}}${{5}}", cs_schema, table), ) .into_owned(); } @@ -849,6 +854,35 @@ impl TestContext { panic!("Failed to read pre-agg table {}: {}", table_name, e) }); + // Write the rows to a local CSV file and load it through + // `LOCATION` — the same CSV import pipeline production rollups + // go through. + let mut csv = columns.iter().map(|(n, _)| Self::csv_field(n)).join(","); + csv.push('\n'); + for msg in &messages { + if let tokio_postgres::SimpleQueryMessage::Row(row) = msg { + let line = (0..columns.len()) + .map(|i| { + Self::csv_field(&Self::cubestore_csv_value( + row.try_get(i).unwrap_or(None), + columns[i].1, + )) + }) + .join(","); + csv.push_str(&line); + csv.push('\n'); + } + } + + let csv_path = std::env::temp_dir().join(format!( + "cubestore-test-{}-{}-{}.csv", + std::process::id(), + cs_schema, + table_name + )); + std::fs::write(&csv_path, csv) + .unwrap_or_else(|e| panic!("Failed to write CSV {:?}: {}", csv_path, e)); + let cols_sql = columns .iter() .map(|(n, t)| format!("\"{}\" {}", n, t)) @@ -856,8 +890,12 @@ impl TestContext { let extra_sql = self.cubestore_table_extras_sql(&table.cube_name, &name, pre_agg, &columns); let create_sql = format!( - "CREATE TABLE {}.\"{}\" ({}){}", - cs_schema, table_name, cols_sql, extra_sql + "CREATE TABLE {}.\"{}\" ({}){} LOCATION '{}'", + cs_schema, + table_name, + cols_sql, + extra_sql, + csv_path.to_string_lossy() ); conn.query_drop(&create_sql).await.unwrap_or_else(|e| { panic!( @@ -865,36 +903,9 @@ impl TestContext { create_sql, e ) }); - - let mut values: Vec = Vec::new(); - for msg in &messages { - if let tokio_postgres::SimpleQueryMessage::Row(row) = msg { - let row_values = (0..columns.len()) - .map(|i| { - Self::cubestore_literal( - row.try_get(i).unwrap_or(None), - columns[i].1, - ) - }) - .join(", "); - values.push(format!("({})", row_values)); - } - } - if !values.is_empty() { - let insert_sql = format!( - "INSERT INTO {}.\"{}\" ({}) VALUES {}", - cs_schema, - table_name, - columns.iter().map(|(n, _)| format!("\"{}\"", n)).join(", "), - values.join(", ") - ); - conn.query_drop(&insert_sql).await.unwrap_or_else(|e| { - panic!( - "Failed to insert into CubeStore table {}:\n{}\n\nError: {:?}", - table_name, insert_sql, e - ) - }); - } + // CREATE TABLE ... LOCATION imports synchronously; the file is + // no longer needed once it returns. + let _ = std::fs::remove_file(&csv_path); created.push(table_name); } @@ -998,16 +1009,61 @@ impl TestContext { } } + /// Serializes a Postgres text value into the canonical form CubeStore's + /// CSV import expects, matching the JS postgres-driver type parsers: + /// timestamps become `YYYY-MM-DDTHH:mm:ss.SSS`, booleans `true`/`false`, + /// NULL an empty field; numerics pass through unchanged. #[cfg(feature = "integration-cubestore")] - fn cubestore_literal(value: Option<&str>, cubestore_type: &str) -> String { + fn cubestore_csv_value(value: Option<&str>, cubestore_type: &str) -> String { let Some(value) = value else { - return "NULL".to_string(); + return String::new(); }; match cubestore_type { - "bigint" | "double" | "decimal" => value.to_string(), "boolean" => (if value == "t" { "true" } else { "false" }).to_string(), - "timestamp" => format!("'{}'", value.trim_end_matches("+00")), - _ => format!("'{}'", value.replace('\'', "''")), + "timestamp" => Self::normalize_pg_timestamp(value), + _ => value.to_string(), + } + } + + /// `YYYY-MM-DD[ HH:MM:SS[.f...][+TZ]]` → `YYYY-MM-DDTHH:mm:ss.SSS` (UTC, + /// 3-digit millis), mirroring the JS postgres timestamp/date parsers. + #[cfg(feature = "integration-cubestore")] + fn normalize_pg_timestamp(value: &str) -> String { + let value = value.trim(); + let (date, time) = match value.split_once(' ') { + Some((d, t)) => (d, t), + None => (value, "00:00:00"), + }; + // Strip the timezone suffix (e.g. `+00`, `-05:30`): the offset sign + // sits after the HH:MM:SS portion. + let tz_pos = time + .char_indices() + .skip(8) + .find(|(_, c)| *c == '+' || *c == '-') + .map(|(i, _)| i); + let time = match tz_pos { + Some(i) => &time[..i], + None => time, + }; + let (hms, frac) = match time.split_once('.') { + Some((h, f)) => (h, f), + None => (time, ""), + }; + let mut ms = String::with_capacity(3); + ms.push_str(frac.get(..frac.len().min(3)).unwrap_or("")); + while ms.len() < 3 { + ms.push('0'); + } + format!("{}T{}.{}", date, hms, ms) + } + + /// RFC 4180 CSV field escaping. + #[cfg(feature = "integration-cubestore")] + fn csv_field(value: &str) -> String { + if value.contains([',', '"', '\n', '\r']) { + format!("\"{}\"", value.replace('"', "\"\"")) + } else { + value.to_string() } } From d31cde7a48fcd8cc0047927899ec4031a9fc2065 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 5 Jun 2026 11:54:53 +0200 Subject: [PATCH 3/8] test(tesseract): route external pre-agg tests to live CubeStore Make pre-aggregation integration tests follow production routing: rollup and rollupJoin default to external (CUBEJS_EXTERNAL_DEFAULT=true) in the YAML mock, and TestContext::try_execute runs external-covered queries on CubeStore (skipped without the integration-cubestore feature) and everything else on Postgres. All pre_aggregations tests migrated. Mirror the CubeStore dialect's native DriverTools methods in the mock (date_bin -> DATE_BIN, time_stamp_cast -> CAST as TIMESTAMP, date_time_cast -> to_timestamp, convert_tz -> CONVERT_TZ); previously the external mock carried CubeStore templates but Postgres method bodies, so it ran Postgres SQL against CubeStore. Sort result rows for deterministic snapshots. --- .../cube_bridge/mock_driver_tools.rs | 48 ++++++++ .../cube_bridge/yaml/pre_aggregation.rs | 8 +- .../test_fixtures/test_utils/test_context.rs | 48 +++++++- ...basic__basic_pre_agg_cubestore_result.snap | 4 +- .../pre_aggregations/multi_fact.rs | 24 ++-- .../pre_aggregations/multi_stage.rs | 4 +- ...act_plus_multiplied_separate_pre_aggs.snap | 6 +- ..._fact_separate_pre_aggs_by_shared_dim.snap | 8 +- ...arate_pre_aggs_by_shared_dim_filtered.snap | 4 +- ...__multi_fact_separate_pre_aggs_totals.snap | 4 +- ..._fact_whole_query_single_rollup_match.snap | 8 +- ...le_query_single_rollup_match_filtered.snap | 4 +- ...plied_whole_query_single_rollup_match.snap | 8 +- ...le_query_single_rollup_match_filtered.snap | 6 +- ..._time_shift_pre_agg_with_leaf_measure.snap | 8 +- ...hift_pre_agg_with_multi_stage_measure.snap | 6 +- ...d_measure_full_match_cubestore_result.snap | 13 ++ ...lculated_measure_full_match_pg_result.snap | 13 -- ...easure_parital_match_cubestore_result.snap | 9 ++ ...lated_measure_parital_match_pg_result.snap | 9 -- ...__basic_pre_agg_sql_cubestore_result.snap} | 6 +- ...anularity_full_match_cubestore_result.snap | 9 ++ ...stom_granularity_full_match_pg_result.snap | 9 -- ..._additive_full_match_cubestore_result.snap | 9 ++ ...ity_non_additive_full_match_pg_result.snap | 9 -- ...on_strict_self_match_cubestore_result.snap | 7 ++ ...arity_non_strict_self_match_pg_result.snap | 7 -- ..._coarser_granularity_cubestore_result.snap | 12 ++ ..._rollup_coarser_granularity_pg_result.snap | 12 -- ...ly_rollup_full_match_cubestore_result.snap | 13 ++ ...on__daily_rollup_full_match_pg_result.snap | 13 -- ..._additive_full_match_cubestore_result.snap | 13 ++ ...lup_non_additive_full_match_pg_result.snap | 13 -- ...ll_match_main_rollup_cubestore_result.snap | 13 ++ ...ion__full_match_main_rollup_pg_result.snap | 13 -- ...non_additive_measure_cubestore_result.snap | 13 ++ ..._match_non_additive_measure_pg_result.snap | 13 -- ..._measures_full_match_cubestore_result.snap | 13 ++ ...ll_base_measures_full_match_pg_result.snap | 13 -- ...asures_partial_match_cubestore_result.snap | 9 ++ ...base_measures_partial_match_pg_result.snap | 9 -- ...d_measure_full_match_cubestore_result.snap | 13 ++ ...lculated_measure_full_match_pg_result.snap | 13 -- ...d_measure_full_match_cubestore_result.snap | 13 ++ ...el_mixed_measure_full_match_pg_result.snap | 13 -- ...uarter_with_pre_agg_cubestore_result.snap} | 2 +- ...e_separate_pre_aggs_cubestore_result.snap} | 4 +- ..._pre_aggs_time_shift_cubestore_result.snap | 9 ++ ...eparate_pre_aggs_time_shift_pg_result.snap | 9 -- ...al_match_main_rollup_cubestore_result.snap | 9 ++ ...__partial_match_main_rollup_pg_result.snap | 9 -- ...__segment_full_match_cubestore_result.snap | 11 ++ ...eration__segment_full_match_pg_result.snap | 11 -- ...match_unused_segment_cubestore_result.snap | 16 +++ ...artial_match_unused_segment_pg_result.snap | 16 --- ..._coarser_granularity_cubestore_result.snap | 10 ++ ...nt_with_coarser_granularity_pg_result.snap | 10 -- ...coarser_custom_query_cubestore_result.snap | 9 ++ ...re_agg_coarser_custom_query_pg_result.snap | 9 -- .../pre_aggregations/sql_generation.rs | 116 +++++++++++------- 60 files changed, 447 insertions(+), 325 deletions(-) create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_full_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_full_match_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_parital_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_parital_match_pg_result.snap rename rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/{cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_pg_result.snap => cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_cubestore_result.snap} (52%) create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_full_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_full_match_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_additive_full_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_additive_full_match_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_strict_self_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_strict_self_match_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_coarser_granularity_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_coarser_granularity_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_full_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_full_match_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_non_additive_full_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_non_additive_full_match_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_main_rollup_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_main_rollup_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_non_additive_measure_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_non_additive_measure_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_full_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_full_match_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_partial_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_partial_match_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_calculated_measure_full_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_calculated_measure_full_match_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_mixed_measure_full_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_mixed_measure_full_match_pg_result.snap rename rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/{cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_count_distinct_sum_by_quarter_with_pre_agg_pg_result.snap => cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_count_distinct_sum_by_quarter_with_pre_agg_cubestore_result.snap} (52%) rename rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/{cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_pg_result.snap => cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_cubestore_result.snap} (53%) create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_time_shift_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_time_shift_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__partial_match_main_rollup_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__partial_match_main_rollup_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_full_match_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_full_match_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_partial_match_unused_segment_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_partial_match_unused_segment_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_with_coarser_granularity_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_with_coarser_granularity_pg_result.snap create mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__standard_pre_agg_coarser_custom_query_cubestore_result.snap delete mode 100644 rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__standard_pre_agg_coarser_custom_query_pg_result.snap diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_driver_tools.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_driver_tools.rs index 40f5631e0d761..d0a4c36862f6d 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_driver_tools.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_driver_tools.rs @@ -18,6 +18,9 @@ pub struct MockDriverTools { timestamp_precision: u32, sql_templates: Rc, visible_in_db_time_zone: bool, + /// When set, dialect-specific native methods mirror CubeStoreQuery + /// instead of the Postgres defaults (used for external pre-aggregations). + cubestore: bool, } impl MockDriverTools { @@ -27,6 +30,7 @@ impl MockDriverTools { timestamp_precision: 3, sql_templates: Rc::new(MockSqlTemplatesRender::default_templates()), visible_in_db_time_zone: false, + cubestore: false, } } @@ -37,6 +41,7 @@ impl MockDriverTools { timestamp_precision: 3, sql_templates: Rc::new(MockSqlTemplatesRender::default_templates()), visible_in_db_time_zone: false, + cubestore: false, } } @@ -47,6 +52,7 @@ impl MockDriverTools { timestamp_precision: 3, sql_templates: Rc::new(sql_templates), visible_in_db_time_zone: false, + cubestore: false, } } @@ -59,6 +65,7 @@ impl MockDriverTools { timestamp_precision: 3, sql_templates: Rc::new(sql_templates), visible_in_db_time_zone: false, + cubestore: false, } } @@ -67,6 +74,26 @@ impl MockDriverTools { self.visible_in_db_time_zone = true; self } + + /// Switches dialect-specific native methods to their CubeStore form, + /// mirroring CubeStoreQuery.ts (used for external pre-aggregations). + pub fn with_cubestore_dialect(mut self) -> Self { + self.cubestore = true; + self + } + + /// Mirrors CubeStoreQuery.formatInterval: a `" "` interval + /// (possibly multi-unit) rendered in CubeStore (DataFusion) form, e.g. + /// `6 month` → `'6 MONTH'`. + fn format_interval(interval: &str) -> String { + let mut tokens = interval.split_whitespace(); + let mut parts: Vec = Vec::new(); + while let (Some(num), Some(unit)) = (tokens.next(), tokens.next()) { + let unit = unit.trim_end_matches('s').to_uppercase(); + parts.push(format!("{} {}", num, unit)); + } + format!("'{}'", parts.join(" ")) + } } impl Default for MockDriverTools { @@ -81,6 +108,10 @@ impl DriverTools for MockDriverTools { } fn convert_tz(&self, field: String) -> Result { + if self.cubestore { + // CubeStoreQuery.convertTz; sessions are pinned to UTC here. + return Ok(format!("CONVERT_TZ({}, '+00:00')", field)); + } Ok(format!( "({}::timestamptz AT TIME ZONE '{}')", field, self.timezone @@ -122,10 +153,18 @@ impl DriverTools for MockDriverTools { } fn time_stamp_cast(&self, field: String) -> Result { + if self.cubestore { + // CubeStoreQuery.timeStampCast (DataFusion has no `::timestamptz`). + return Ok(format!("CAST({} as TIMESTAMP)", field)); + } Ok(format!("{}::timestamptz", field)) } fn date_time_cast(&self, field: String) -> Result { + if self.cubestore { + // CubeStoreQuery.dateTimeCast. + return Ok(format!("to_timestamp({})", field)); + } Ok(format!("{}::timestamp", field)) } @@ -237,6 +276,15 @@ impl DriverTools for MockDriverTools { source: String, origin: String, ) -> Result { + if self.cubestore { + // Mirror CubeStoreQuery.dateBin: DATE_BIN over to_timestamp casts. + return Ok(format!( + "DATE_BIN(INTERVAL {}, to_timestamp({}), to_timestamp('{}'))", + Self::format_interval(&interval), + source, + origin + )); + } Ok(format!( "('{}' ::timestamp + INTERVAL '{}' * FLOOR(EXTRACT(EPOCH FROM ({} - '{}'::timestamp)) / EXTRACT(EPOCH FROM INTERVAL '{}')))", origin, interval, source, origin, interval diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/yaml/pre_aggregation.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/yaml/pre_aggregation.rs index 0259090495f5a..be92abb122423 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/yaml/pre_aggregation.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/yaml/pre_aggregation.rs @@ -134,13 +134,19 @@ impl YamlPreAggregationDefinition { }) .collect::>(); + // Mirror the JS default (CUBEJS_EXTERNAL_DEFAULT=true): rollup and + // rollupJoin are external unless explicitly set otherwise. + let external = self.external.or_else(|| { + matches!(self.pre_aggregation_type.as_str(), "rollup" | "rollupJoin").then_some(true) + }); + Rc::new( MockPreAggregationDescription::builder() .name(name) .pre_aggregation_type(self.pre_aggregation_type) .granularity(self.granularity) .sql_alias(self.sql_alias) - .external(self.external) + .external(external) .allow_non_strict_date_range_match(self.allow_non_strict_date_range_match) .measure_references_opt(measure_references) .dimension_references_opt(dimension_references) diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs index 5f2186e800fdc..1a049c7981420 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs @@ -193,7 +193,8 @@ impl TestContext { let driver_tools = MockDriverTools::with_sql_templates_and_timezone( MockSqlTemplatesRender::cubestore_templates(), timezone.to_string(), - ); + ) + .with_cubestore_dialect(); base_tools.set_external_driver_tools(Rc::new(driver_tools)); } let join_graph = Rc::new(schema.create_join_graph()?); @@ -478,6 +479,44 @@ impl TestContext { planner.plan() } + /// Executes the query the way production would route it: when the query + /// is covered by external pre-aggregations it runs on CubeStore (skipped, + /// returning `None`, unless built with `integration-cubestore`); otherwise + /// it runs against the source Postgres. Matches the JS default where + /// rollups are external (CUBEJS_EXTERNAL_DEFAULT=true) and thus served by + /// CubeStore, while `external: false` pre-aggs stay in the source DB. + #[cfg(feature = "integration-postgres")] + pub async fn try_execute(&self, query_yaml: &str, seed_file: &str) -> Option { + let (_, usages) = self + .build_sql_with_used_pre_aggregations(query_yaml) + .expect("Failed to plan query"); + let external = !usages.is_empty() && usages.iter().all(|u| u.pre_aggregation.external()); + + if external { + // Ensure the CubeStore dialect regardless of how this context was + // built, so callers need not pick the constructor. + let cs_ctx = if self.external_cubestore { + None + } else { + Some( + Self::new_with_external_cubestore(self.schema.clone()) + .expect("Failed to create external CubeStore context"), + ) + }; + match &cs_ctx { + Some(ctx) => ctx.try_execute_cubestore(query_yaml, seed_file).await, + None => self.try_execute_cubestore(query_yaml, seed_file).await, + } + } else { + self.try_execute_pg(query_yaml, seed_file).await + } + } + + #[cfg(not(feature = "integration-postgres"))] + pub async fn try_execute(&self, _query_yaml: &str, _seed_file: &str) -> Option { + None + } + #[cfg(feature = "integration-postgres")] pub async fn try_execute_pg(&self, query_yaml: &str, seed_file: &str) -> Option { let options = self.create_query_options_from_yaml(query_yaml); @@ -785,7 +824,7 @@ impl TestContext { .collect() }) .unwrap_or_default(); - let formatted_rows: Vec> = rows + let mut formatted_rows: Vec> = rows .iter() .map(|r| { (0..r.columns_ref().len()) @@ -794,6 +833,11 @@ impl TestContext { }) .collect(); + // CubeStore parallel aggregation yields rows in nondeterministic + // order; sort for stable snapshots without requiring ORDER BY in + // every query. + formatted_rows.sort(); + Some(super::integration_context::format_rows_table( columns, formatted_rows, diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap index 62145a0ccfd3d..671c812914b67 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap @@ -4,7 +4,7 @@ expression: result --- visitors__source | visitors__count -----------------+---------------- +NULL | 1 google | 5 organic | 2 -twitter | 2 -NULL | 1 +twitter | 2 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs index 2bfed059e4cb1..c9ff8fbc71b3d 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs @@ -36,7 +36,7 @@ async fn test_multi_fact_separate_pre_aggs_totals() { names ); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -72,7 +72,7 @@ async fn test_multi_fact_separate_pre_aggs_by_shared_dim() { names ); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -111,7 +111,7 @@ async fn test_multi_fact_whole_query_single_rollup_match() { ); assert_eq!(pre_aggrs[0].name(), "multi_fact_combined"); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -158,7 +158,7 @@ async fn test_fact_plus_multiplied_separate_pre_aggs() { names ); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -195,7 +195,7 @@ async fn test_multiplied_whole_query_single_rollup_match() { ); assert_eq!(pre_aggrs[0].name(), "customers_by_order_status"); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -242,7 +242,7 @@ async fn test_multi_fact_plus_multiplied_shared_pre_agg() { names ); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -299,7 +299,7 @@ async fn test_regular_plus_two_multiplied_separate_pre_aggs() { names ); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -343,7 +343,7 @@ async fn test_multi_fact_separate_pre_aggs_by_shared_dim_filtered() { assert!(names.contains(&"orders_by_customer_city_with_name")); assert!(names.contains(&"returns_by_customer_city_with_name")); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -375,7 +375,7 @@ async fn test_multi_fact_whole_query_single_rollup_match_filtered() { assert_eq!(pre_aggrs.len(), 1); assert_eq!(pre_aggrs[0].name(), "multi_fact_combined_with_name"); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -415,7 +415,7 @@ async fn test_multi_fact_plus_multiplied_shared_pre_agg_filtered() { assert_eq!(combo_count, 2); assert!(names.contains(&"returns_by_customer_city_with_name")); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -445,7 +445,7 @@ async fn test_multiplied_whole_query_single_rollup_match_filtered() { assert_eq!(pre_aggrs.len(), 1); assert_eq!(pre_aggrs[0].name(), "customers_by_order_status_with_name"); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -480,7 +480,7 @@ async fn test_multi_fact_partial_match_rolls_back() { .collect::>() ); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs index a2e89d919594f..bfa383a8a7f2a 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs @@ -66,7 +66,7 @@ async fn test_multi_stage_time_shift_pre_agg_with_leaf_measure() { "Shifted and unshifted usages must have different usage indexes" ); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } @@ -99,7 +99,7 @@ async fn test_multi_stage_time_shift_pre_agg_with_multi_stage_measure() { ); assert_eq!(pre_aggrs[0].name(), "customers_lifetime_prev_month_pre_agg"); - if let Some(result) = ctx.try_execute_pg(query, SEED).await { + if let Some(result) = ctx.try_execute(query, SEED).await { insta::assert_snapshot!(result); } } diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__fact_plus_multiplied_separate_pre_aggs.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__fact_plus_multiplied_separate_pre_aggs.snap index 72e675896a108..b0dc88dd3eb5c 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__fact_plus_multiplied_separate_pre_aggs.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__fact_plus_multiplied_separate_pre_aggs.snap @@ -1,9 +1,9 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs expression: result --- orders__status | orders__count | customers__count ---------------+---------------+----------------- +NULL | NULL | NULL completed | 5 | 3 -pending | 3 | 2 -NULL | NULL | 1 +pending | 3 | 2 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_by_shared_dim.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_by_shared_dim.snap index 453cce9872dae..b455ee5ac5c5f 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_by_shared_dim.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_by_shared_dim.snap @@ -1,9 +1,9 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs expression: result --- customers__city | orders__count | orders__total_amount | returns__count | returns__total_refund ----------------+---------------+----------------------+----------------+---------------------- -Boston | 3 | 550.00 | 2 | 130.00 -Chicago | 0 | NULL | 2 | 100.00 -New York | 5 | 825.00 | 1 | 100.00 +Boston | 3 | 550 | 2 | 130 +Chicago | 0 | NULL | 2 | 100 +New York | 5 | 825 | 1 | 100 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_by_shared_dim_filtered.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_by_shared_dim_filtered.snap index 2c74cbbdf13a5..3e339deb7c99f 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_by_shared_dim_filtered.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_by_shared_dim_filtered.snap @@ -1,7 +1,7 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs expression: result --- customers__city | orders__count | orders__total_amount | returns__count | returns__total_refund ----------------+---------------+----------------------+----------------+---------------------- -New York | 4 | 425.00 | 1 | 100.00 +New York | 4 | 425 | 1 | 100 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_totals.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_totals.snap index 96d22807b8ea0..f86295691dc96 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_totals.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_separate_pre_aggs_totals.snap @@ -1,7 +1,7 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs expression: result --- orders__count | orders__total_amount | returns__count | returns__total_refund --------------+----------------------+----------------+---------------------- -8 | 1375.00 | 5 | 330.00 +8 | 1375 | 5 | 330 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_whole_query_single_rollup_match.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_whole_query_single_rollup_match.snap index 453cce9872dae..b455ee5ac5c5f 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_whole_query_single_rollup_match.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_whole_query_single_rollup_match.snap @@ -1,9 +1,9 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs expression: result --- customers__city | orders__count | orders__total_amount | returns__count | returns__total_refund ----------------+---------------+----------------------+----------------+---------------------- -Boston | 3 | 550.00 | 2 | 130.00 -Chicago | 0 | NULL | 2 | 100.00 -New York | 5 | 825.00 | 1 | 100.00 +Boston | 3 | 550 | 2 | 130 +Chicago | 0 | NULL | 2 | 100 +New York | 5 | 825 | 1 | 100 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_whole_query_single_rollup_match_filtered.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_whole_query_single_rollup_match_filtered.snap index 2c74cbbdf13a5..3e339deb7c99f 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_whole_query_single_rollup_match_filtered.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multi_fact_whole_query_single_rollup_match_filtered.snap @@ -1,7 +1,7 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs expression: result --- customers__city | orders__count | orders__total_amount | returns__count | returns__total_refund ----------------+---------------+----------------------+----------------+---------------------- -New York | 4 | 425.00 | 1 | 100.00 +New York | 4 | 425 | 1 | 100 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match.snap index b73e820655f89..2bbbc2d0e3c1c 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match.snap @@ -1,9 +1,9 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs expression: result --- orders__status | customers__count | customers__total_lifetime_value ---------------+------------------+-------------------------------- -completed | 3 | 4500.00 -pending | 2 | 3000.00 -NULL | 1 | 500.00 +NULL | 1 | 500 +completed | 3 | 4500 +pending | 2 | 3000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match_filtered.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match_filtered.snap index 13ed08cdc664e..3364d372bac59 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match_filtered.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match_filtered.snap @@ -1,8 +1,8 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_fact.rs expression: result --- orders__status | customers__count | customers__total_lifetime_value ---------------+------------------+-------------------------------- -completed | 1 | 1000.00 -pending | 1 | 1000.00 +completed | 1 | 1000 +pending | 1 | 1000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_stage__multi_stage_time_shift_pre_agg_with_leaf_measure.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_stage__multi_stage_time_shift_pre_agg_with_leaf_measure.snap index 524b6aeb4c199..484b2242abd51 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_stage__multi_stage_time_shift_pre_agg_with_leaf_measure.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_stage__multi_stage_time_shift_pre_agg_with_leaf_measure.snap @@ -1,9 +1,9 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs expression: result --- returns__created_at_month | customers__total_lifetime_value | customers__total_lifetime_value_prev_month_by_returns --------------------------+---------------------------------+------------------------------------------------------ -2024-01-01 00:00:00 | 1000.00 | NULL -2024-02-01 00:00:00 | 2000.00 | 1000.00 -2024-03-01 00:00:00 | 500.00 | 2000.00 +2024-01-01T00:00:00.000Z | 1000 | NULL +2024-02-01T00:00:00.000Z | 2000 | 1000 +2024-03-01T00:00:00.000Z | 500 | 2000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_stage__multi_stage_time_shift_pre_agg_with_multi_stage_measure.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_stage__multi_stage_time_shift_pre_agg_with_multi_stage_measure.snap index 7ab4e156a8df0..ea41e73b210a3 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_stage__multi_stage_time_shift_pre_agg_with_multi_stage_measure.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_stage__multi_stage_time_shift_pre_agg_with_multi_stage_measure.snap @@ -1,8 +1,8 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs expression: result --- returns__created_at_month | customers__total_lifetime_value_prev_month_by_returns --------------------------+------------------------------------------------------ -2024-02-01 00:00:00 | 1000.00 -2024-03-01 00:00:00 | 2000.00 +2024-02-01T00:00:00.000Z | 1000 +2024-03-01T00:00:00.000Z | 2000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_full_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_full_match_cubestore_result.snap new file mode 100644 index 0000000000000..66e28f32db75d --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_full_match_cubestore_result.snap @@ -0,0 +1,13 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__city | orders__amount_per_count +---------------+--------------+------------------------- +cancelled | Chicago | 25 +completed | Boston | 187.5 +completed | Chicago | 400 +completed | New York | 75 +pending | Boston | 150 +pending | Chicago | 175 +pending | New York | 130 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_full_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_full_match_pg_result.snap deleted file mode 100644 index 1ecd0af27733c..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_full_match_pg_result.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__city | orders__amount_per_count ----------------+--------------+------------------------- -completed | Chicago | 400.0000000000000000 -completed | Boston | 187.5000000000000000 -pending | Chicago | 175.0000000000000000 -pending | Boston | 150.0000000000000000 -pending | New York | 130.0000000000000000 -completed | New York | 75.0000000000000000 -cancelled | Chicago | 25.0000000000000000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_parital_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_parital_match_cubestore_result.snap new file mode 100644 index 0000000000000..1806b76ff4cd7 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_parital_match_cubestore_result.snap @@ -0,0 +1,9 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__amount_per_count +---------------+------------------------- +cancelled | 25 +completed | 185 +pending | 146.25 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_parital_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_parital_match_pg_result.snap deleted file mode 100644 index 184cc788679e6..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__base_and_calculated_measure_parital_match_pg_result.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__amount_per_count ----------------+------------------------- -completed | 185.0000000000000000 -pending | 146.2500000000000000 -cancelled | 25.0000000000000000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_cubestore_result.snap similarity index 52% rename from rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_pg_result.snap rename to rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_cubestore_result.snap index 62db01c059b66..ccca5cbc4aaf9 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_pg_result.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_cubestore_result.snap @@ -1,10 +1,10 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs expression: result --- visitors__source | visitors__count -----------------+---------------- +NULL | 1 google | 5 organic | 2 -twitter | 2 -NULL | 1 +twitter | 2 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_full_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_full_match_cubestore_result.snap new file mode 100644 index 0000000000000..6e9fa46a8064d --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_full_match_cubestore_result.snap @@ -0,0 +1,9 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__created_at_half_year | orders__count +---------------+------------------------------+-------------- +cancelled | 2025-01-01T00:00:00.000Z | 1 +completed | 2025-01-01T00:00:00.000Z | 5 +pending | 2025-01-01T00:00:00.000Z | 4 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_full_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_full_match_pg_result.snap deleted file mode 100644 index 86fc8fc9c98ac..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_full_match_pg_result.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__created_at_half_year | orders__count ----------------+------------------------------+-------------- -completed | 2025-01-01 00:00:00 | 5 -pending | 2025-01-01 00:00:00 | 4 -cancelled | 2025-01-01 00:00:00 | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_additive_full_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_additive_full_match_cubestore_result.snap new file mode 100644 index 0000000000000..f1edf6277e149 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_additive_full_match_cubestore_result.snap @@ -0,0 +1,9 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__created_at_half_year | orders__avg_amount +---------------+------------------------------+------------------- +cancelled | 2025-01-01T00:00:00.000Z | 25 +completed | 2025-01-01T00:00:00.000Z | 185 +pending | 2025-01-01T00:00:00.000Z | 146.25 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_additive_full_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_additive_full_match_pg_result.snap deleted file mode 100644 index d9bcb5f2355f7..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_additive_full_match_pg_result.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__created_at_half_year | orders__avg_amount ----------------+------------------------------+--------------------- -completed | 2025-01-01 00:00:00 | 185.0000000000000000 -pending | 2025-01-01 00:00:00 | 146.2500000000000000 -cancelled | 2025-01-01 00:00:00 | 25.0000000000000000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_strict_self_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_strict_self_match_cubestore_result.snap new file mode 100644 index 0000000000000..d30ead575a646 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_strict_self_match_cubestore_result.snap @@ -0,0 +1,7 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__created_at_half_year | orders__count +-----------------------------+-------------- +2025-01-01T00:00:00.000Z | 10 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_strict_self_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_strict_self_match_pg_result.snap deleted file mode 100644 index 9a8d45b04f2f3..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__custom_granularity_non_strict_self_match_pg_result.snap +++ /dev/null @@ -1,7 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__created_at_half_year | orders__count ------------------------------+-------------- -2025-01-01 00:00:00 | 10 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_coarser_granularity_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_coarser_granularity_cubestore_result.snap new file mode 100644 index 0000000000000..767841f2b4595 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_coarser_granularity_cubestore_result.snap @@ -0,0 +1,12 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__country | orders__created_at_month | orders__count +----------------+--------------------------+-------------- +Boston | 2025-01-01T00:00:00.000Z | 1 +Boston | 2025-02-01T00:00:00.000Z | 2 +Chicago | 2025-02-01T00:00:00.000Z | 1 +Chicago | 2025-03-01T00:00:00.000Z | 2 +New York | 2025-01-01T00:00:00.000Z | 3 +New York | 2025-03-01T00:00:00.000Z | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_coarser_granularity_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_coarser_granularity_pg_result.snap deleted file mode 100644 index 24a6622bfe8ad..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_coarser_granularity_pg_result.snap +++ /dev/null @@ -1,12 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__country | orders__created_at_month | orders__count -----------------+--------------------------+-------------- -Boston | 2025-01-01 00:00:00 | 1 -New York | 2025-01-01 00:00:00 | 3 -Chicago | 2025-02-01 00:00:00 | 1 -Boston | 2025-02-01 00:00:00 | 2 -Chicago | 2025-03-01 00:00:00 | 2 -New York | 2025-03-01 00:00:00 | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_full_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_full_match_cubestore_result.snap new file mode 100644 index 0000000000000..2f76f989775ef --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_full_match_cubestore_result.snap @@ -0,0 +1,13 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__country | orders__created_at_day | orders__count +----------------+--------------------------+-------------- +Boston | 2025-01-31T00:00:00.000Z | 1 +Boston | 2025-02-01T00:00:00.000Z | 2 +Chicago | 2025-02-15T00:00:00.000Z | 1 +Chicago | 2025-03-01T00:00:00.000Z | 2 +New York | 2025-01-10T00:00:00.000Z | 2 +New York | 2025-01-11T00:00:00.000Z | 1 +New York | 2025-03-02T00:00:00.000Z | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_full_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_full_match_pg_result.snap deleted file mode 100644 index 495ea7428b88f..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_full_match_pg_result.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__country | orders__created_at_day | orders__count -----------------+------------------------+-------------- -New York | 2025-01-10 00:00:00 | 2 -New York | 2025-01-11 00:00:00 | 1 -Boston | 2025-01-31 00:00:00 | 1 -Boston | 2025-02-01 00:00:00 | 2 -Chicago | 2025-02-15 00:00:00 | 1 -Chicago | 2025-03-01 00:00:00 | 2 -New York | 2025-03-02 00:00:00 | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_non_additive_full_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_non_additive_full_match_cubestore_result.snap new file mode 100644 index 0000000000000..5b2589f01f3d8 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_non_additive_full_match_cubestore_result.snap @@ -0,0 +1,13 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__country | orders__created_at_day | orders__avg_amount +----------------+--------------------------+------------------- +Boston | 2025-01-31T00:00:00.000Z | 300 +Boston | 2025-02-01T00:00:00.000Z | 112.5 +Chicago | 2025-02-15T00:00:00.000Z | 25 +Chicago | 2025-03-01T00:00:00.000Z | 287.5 +New York | 2025-01-10T00:00:00.000Z | 150 +New York | 2025-01-11T00:00:00.000Z | 50 +New York | 2025-03-02T00:00:00.000Z | 60 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_non_additive_full_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_non_additive_full_match_pg_result.snap deleted file mode 100644 index 5681ddfd29f78..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__daily_rollup_non_additive_full_match_pg_result.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__country | orders__created_at_day | orders__avg_amount -----------------+------------------------+--------------------- -New York | 2025-01-10 00:00:00 | 150.0000000000000000 -New York | 2025-01-11 00:00:00 | 50.0000000000000000 -Boston | 2025-01-31 00:00:00 | 300.0000000000000000 -Boston | 2025-02-01 00:00:00 | 112.5000000000000000 -Chicago | 2025-02-15 00:00:00 | 25.0000000000000000 -Chicago | 2025-03-01 00:00:00 | 287.5000000000000000 -New York | 2025-03-02 00:00:00 | 60.0000000000000000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_main_rollup_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_main_rollup_cubestore_result.snap new file mode 100644 index 0000000000000..320a6656cfbf1 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_main_rollup_cubestore_result.snap @@ -0,0 +1,13 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__city | orders__count | orders__total_amount +---------------+--------------+---------------+--------------------- +cancelled | Chicago | 1 | 25 +completed | Boston | 2 | 375 +completed | Chicago | 1 | 400 +completed | New York | 2 | 150 +pending | Boston | 1 | 150 +pending | Chicago | 1 | 175 +pending | New York | 2 | 260 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_main_rollup_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_main_rollup_pg_result.snap deleted file mode 100644 index 70b793b8628b6..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_main_rollup_pg_result.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__city | orders__count | orders__total_amount ----------------+--------------+---------------+--------------------- -completed | New York | 2 | 150.00 -pending | New York | 2 | 260.00 -completed | Boston | 2 | 375.00 -pending | Boston | 1 | 150.00 -completed | Chicago | 1 | 400.00 -cancelled | Chicago | 1 | 25.00 -pending | Chicago | 1 | 175.00 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_non_additive_measure_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_non_additive_measure_cubestore_result.snap new file mode 100644 index 0000000000000..9f6f95d8c81d6 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_non_additive_measure_cubestore_result.snap @@ -0,0 +1,13 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__city | orders__avg_amount +---------------+--------------+------------------- +cancelled | Chicago | 25 +completed | Boston | 187.5 +completed | Chicago | 400 +completed | New York | 75 +pending | Boston | 150 +pending | Chicago | 175 +pending | New York | 130 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_non_additive_measure_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_non_additive_measure_pg_result.snap deleted file mode 100644 index 666aea64813fd..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__full_match_non_additive_measure_pg_result.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__city | orders__avg_amount ----------------+--------------+--------------------- -completed | Chicago | 400.0000000000000000 -completed | Boston | 187.5000000000000000 -pending | Chicago | 175.0000000000000000 -pending | Boston | 150.0000000000000000 -pending | New York | 130.0000000000000000 -completed | New York | 75.0000000000000000 -cancelled | Chicago | 25.0000000000000000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_full_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_full_match_cubestore_result.snap new file mode 100644 index 0000000000000..6082cb8bb37f2 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_full_match_cubestore_result.snap @@ -0,0 +1,13 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__city | orders__multi_level_measure +---------------+--------------+---------------------------- +cancelled | Chicago | 1 +completed | Boston | 0.625 +completed | Chicago | 1 +completed | New York | 0.75 +pending | Boston | 1 +pending | Chicago | 1 +pending | New York | 0.65 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_full_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_full_match_pg_result.snap deleted file mode 100644 index 752233ef3ce06..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_full_match_pg_result.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__city | orders__multi_level_measure ----------------+--------------+---------------------------- -completed | Chicago | 1.00000000000000000000 -cancelled | Chicago | 1.00000000000000000000 -pending | Chicago | 1.00000000000000000000 -pending | Boston | 1.00000000000000000000 -completed | New York | 0.75000000000000000000 -pending | New York | 0.65000000000000000000 -completed | Boston | 0.62500000000000000000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_partial_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_partial_match_cubestore_result.snap new file mode 100644 index 0000000000000..5cf1af83cc919 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_partial_match_cubestore_result.snap @@ -0,0 +1,9 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__multi_level_measure +---------------+---------------------------- +cancelled | 1 +completed | 0.4625 +pending | 0.73125 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_partial_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_partial_match_pg_result.snap deleted file mode 100644 index 2f107e2ae7a9e..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_all_base_measures_partial_match_pg_result.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__multi_level_measure ----------------+---------------------------- -cancelled | 1.00000000000000000000 -pending | 0.73125000000000000000 -completed | 0.46250000000000000000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_calculated_measure_full_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_calculated_measure_full_match_cubestore_result.snap new file mode 100644 index 0000000000000..6082cb8bb37f2 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_calculated_measure_full_match_cubestore_result.snap @@ -0,0 +1,13 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__city | orders__multi_level_measure +---------------+--------------+---------------------------- +cancelled | Chicago | 1 +completed | Boston | 0.625 +completed | Chicago | 1 +completed | New York | 0.75 +pending | Boston | 1 +pending | Chicago | 1 +pending | New York | 0.65 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_calculated_measure_full_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_calculated_measure_full_match_pg_result.snap deleted file mode 100644 index 752233ef3ce06..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_calculated_measure_full_match_pg_result.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__city | orders__multi_level_measure ----------------+--------------+---------------------------- -completed | Chicago | 1.00000000000000000000 -cancelled | Chicago | 1.00000000000000000000 -pending | Chicago | 1.00000000000000000000 -pending | Boston | 1.00000000000000000000 -completed | New York | 0.75000000000000000000 -pending | New York | 0.65000000000000000000 -completed | Boston | 0.62500000000000000000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_mixed_measure_full_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_mixed_measure_full_match_cubestore_result.snap new file mode 100644 index 0000000000000..6082cb8bb37f2 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_mixed_measure_full_match_cubestore_result.snap @@ -0,0 +1,13 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__city | orders__multi_level_measure +---------------+--------------+---------------------------- +cancelled | Chicago | 1 +completed | Boston | 0.625 +completed | Chicago | 1 +completed | New York | 0.75 +pending | Boston | 1 +pending | Chicago | 1 +pending | New York | 0.65 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_mixed_measure_full_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_mixed_measure_full_match_pg_result.snap deleted file mode 100644 index 752233ef3ce06..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_level_mixed_measure_full_match_pg_result.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__city | orders__multi_level_measure ----------------+--------------+---------------------------- -completed | Chicago | 1.00000000000000000000 -cancelled | Chicago | 1.00000000000000000000 -pending | Chicago | 1.00000000000000000000 -pending | Boston | 1.00000000000000000000 -completed | New York | 0.75000000000000000000 -pending | New York | 0.65000000000000000000 -completed | Boston | 0.62500000000000000000 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_count_distinct_sum_by_quarter_with_pre_agg_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_count_distinct_sum_by_quarter_with_pre_agg_cubestore_result.snap similarity index 52% rename from rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_count_distinct_sum_by_quarter_with_pre_agg_pg_result.snap rename to rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_count_distinct_sum_by_quarter_with_pre_agg_cubestore_result.snap index 44971f61ee38f..ed535e92fb253 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_count_distinct_sum_by_quarter_with_pre_agg_pg_result.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_count_distinct_sum_by_quarter_with_pre_agg_cubestore_result.snap @@ -1,5 +1,5 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs expression: result --- coach__count_distinct__sum_by_quarter diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_cubestore_result.snap similarity index 53% rename from rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_pg_result.snap rename to rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_cubestore_result.snap index 7d4e94b1b4d31..3af58a60990ff 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_pg_result.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_cubestore_result.snap @@ -1,7 +1,7 @@ --- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs expression: result --- orders__count_reduce_status | orders__revenue_reduce_status ----------------------------+------------------------------ -7 | 1750.00 +7 | 1750 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_time_shift_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_time_shift_cubestore_result.snap new file mode 100644 index 0000000000000..ec3a2bd3c528a --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_time_shift_cubestore_result.snap @@ -0,0 +1,9 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__created_at_month | orders__count_prev_month | orders__revenue_reduce_status +-------------------------+--------------------------+------------------------------ +2025-01-01T00:00:00.000Z | 2 | 250 +2025-02-01T00:00:00.000Z | 2 | 750 +2025-03-01T00:00:00.000Z | 3 | 750 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_time_shift_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_time_shift_pg_result.snap deleted file mode 100644 index 186625c7023da..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__multi_stage_separate_pre_aggs_time_shift_pg_result.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__created_at_month | orders__count_prev_month | orders__revenue_reduce_status --------------------------+--------------------------+------------------------------ -2025-01-01 00:00:00 | 2 | 250.00 -2025-02-01 00:00:00 | 2 | 750.00 -2025-03-01 00:00:00 | 3 | 750.00 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__partial_match_main_rollup_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__partial_match_main_rollup_cubestore_result.snap new file mode 100644 index 0000000000000..240667e2e2db5 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__partial_match_main_rollup_cubestore_result.snap @@ -0,0 +1,9 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__count +---------------+-------------- +cancelled | 1 +completed | 5 +pending | 4 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__partial_match_main_rollup_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__partial_match_main_rollup_pg_result.snap deleted file mode 100644 index a3344048f0d0f..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__partial_match_main_rollup_pg_result.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__count ----------------+-------------- -completed | 5 -pending | 4 -cancelled | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_full_match_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_full_match_cubestore_result.snap new file mode 100644 index 0000000000000..590fc6f679fda --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_full_match_cubestore_result.snap @@ -0,0 +1,11 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__created_at_day | orders__count +---------------+--------------------------+-------------- +cancelled | 2025-02-15T00:00:00.000Z | 1 +completed | 2025-01-10T00:00:00.000Z | 1 +completed | 2025-01-31T00:00:00.000Z | 1 +pending | 2025-01-10T00:00:00.000Z | 1 +pending | 2025-03-01T00:00:00.000Z | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_full_match_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_full_match_pg_result.snap deleted file mode 100644 index 6d9340252f6a3..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_full_match_pg_result.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__created_at_day | orders__count ----------------+------------------------+-------------- -pending | 2025-01-10 00:00:00 | 1 -completed | 2025-01-10 00:00:00 | 1 -completed | 2025-01-31 00:00:00 | 1 -cancelled | 2025-02-15 00:00:00 | 1 -pending | 2025-03-01 00:00:00 | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_partial_match_unused_segment_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_partial_match_unused_segment_cubestore_result.snap new file mode 100644 index 0000000000000..891b8a940788b --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_partial_match_unused_segment_cubestore_result.snap @@ -0,0 +1,16 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__created_at_day | orders__count +---------------+--------------------------+-------------- +cancelled | 2025-02-15T00:00:00.000Z | 1 +completed | 2025-01-10T00:00:00.000Z | 1 +completed | 2025-01-11T00:00:00.000Z | 1 +completed | 2025-01-31T00:00:00.000Z | 1 +completed | 2025-02-01T00:00:00.000Z | 1 +completed | 2025-03-01T00:00:00.000Z | 1 +pending | 2025-01-10T00:00:00.000Z | 1 +pending | 2025-02-01T00:00:00.000Z | 1 +pending | 2025-03-01T00:00:00.000Z | 1 +pending | 2025-03-02T00:00:00.000Z | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_partial_match_unused_segment_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_partial_match_unused_segment_pg_result.snap deleted file mode 100644 index 5dad67c109f18..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_partial_match_unused_segment_pg_result.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__created_at_day | orders__count ----------------+------------------------+-------------- -completed | 2025-01-10 00:00:00 | 1 -pending | 2025-01-10 00:00:00 | 1 -completed | 2025-01-11 00:00:00 | 1 -completed | 2025-01-31 00:00:00 | 1 -completed | 2025-02-01 00:00:00 | 1 -pending | 2025-02-01 00:00:00 | 1 -cancelled | 2025-02-15 00:00:00 | 1 -completed | 2025-03-01 00:00:00 | 1 -pending | 2025-03-01 00:00:00 | 1 -pending | 2025-03-02 00:00:00 | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_with_coarser_granularity_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_with_coarser_granularity_cubestore_result.snap new file mode 100644 index 0000000000000..5644afdac7a46 --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_with_coarser_granularity_cubestore_result.snap @@ -0,0 +1,10 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__created_at_month | orders__count +---------------+--------------------------+-------------- +cancelled | 2025-02-01T00:00:00.000Z | 1 +completed | 2025-01-01T00:00:00.000Z | 2 +pending | 2025-01-01T00:00:00.000Z | 1 +pending | 2025-03-01T00:00:00.000Z | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_with_coarser_granularity_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_with_coarser_granularity_pg_result.snap deleted file mode 100644 index b176feca2a0b7..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__segment_with_coarser_granularity_pg_result.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__created_at_month | orders__count ----------------+--------------------------+-------------- -completed | 2025-01-01 00:00:00 | 2 -pending | 2025-01-01 00:00:00 | 1 -cancelled | 2025-02-01 00:00:00 | 1 -pending | 2025-03-01 00:00:00 | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__standard_pre_agg_coarser_custom_query_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__standard_pre_agg_coarser_custom_query_cubestore_result.snap new file mode 100644 index 0000000000000..6e9fa46a8064d --- /dev/null +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__standard_pre_agg_coarser_custom_query_cubestore_result.snap @@ -0,0 +1,9 @@ +--- +source: cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +expression: result +--- +orders__status | orders__created_at_half_year | orders__count +---------------+------------------------------+-------------- +cancelled | 2025-01-01T00:00:00.000Z | 1 +completed | 2025-01-01T00:00:00.000Z | 5 +pending | 2025-01-01T00:00:00.000Z | 4 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__standard_pre_agg_coarser_custom_query_pg_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__standard_pre_agg_coarser_custom_query_pg_result.snap deleted file mode 100644 index 86fc8fc9c98ac..0000000000000 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__standard_pre_agg_coarser_custom_query_pg_result.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs -expression: result ---- -orders__status | orders__created_at_half_year | orders__count ----------------+------------------------------+-------------- -completed | 2025-01-01 00:00:00 | 5 -pending | 2025-01-01 00:00:00 | 4 -cancelled | 2025-01-01 00:00:00 | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs index a19583bb70ad8..92336e4204226 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs @@ -27,10 +27,10 @@ async fn test_basic_pre_agg_sql() { assert_eq!(pre_aggrs[0].name(), "daily_rollup"); if let Some(result) = test_context - .try_execute_pg(query_yaml, "pre_aggregation_tables.sql") + .try_execute(query_yaml, "pre_aggregation_tables.sql") .await { - insta::assert_snapshot!("basic_pre_agg_sql_pg_result", result); + insta::assert_snapshot!("basic_pre_agg_sql_cubestore_result", result); } } @@ -57,10 +57,10 @@ async fn test_full_match_main_rollup() { assert_eq!(pre_aggrs[0].name(), "main_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("full_match_main_rollup_pg_result", result); + insta::assert_snapshot!("full_match_main_rollup_cubestore_result", result); } } @@ -85,10 +85,10 @@ async fn test_partial_match_main_rollup() { assert_eq!(pre_aggrs[0].name(), "main_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("partial_match_main_rollup_pg_result", result); + insta::assert_snapshot!("partial_match_main_rollup_cubestore_result", result); } } @@ -114,10 +114,10 @@ async fn test_full_match_non_additive_measure() { assert_eq!(pre_aggrs[0].name(), "main_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("full_match_non_additive_measure_pg_result", result); + insta::assert_snapshot!("full_match_non_additive_measure_cubestore_result", result); } } @@ -163,10 +163,10 @@ async fn test_daily_rollup_full_match() { assert_eq!(pre_aggrs[0].name(), "daily_countries_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("daily_rollup_full_match_pg_result", result); + insta::assert_snapshot!("daily_rollup_full_match_cubestore_result", result); } } @@ -194,10 +194,10 @@ async fn test_daily_rollup_coarser_granularity() { assert_eq!(pre_aggrs[0].name(), "daily_countries_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("daily_rollup_coarser_granularity_pg_result", result); + insta::assert_snapshot!("daily_rollup_coarser_granularity_cubestore_result", result); } } @@ -246,10 +246,13 @@ async fn test_daily_rollup_non_additive_full_match() { assert_eq!(pre_aggrs[0].name(), "daily_countries_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("daily_rollup_non_additive_full_match_pg_result", result); + insta::assert_snapshot!( + "daily_rollup_non_additive_full_match_cubestore_result", + result + ); } } @@ -298,10 +301,13 @@ async fn test_multi_level_all_base_measures_full_match() { assert_eq!(pre_aggrs[0].name(), "all_base_measures_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("multi_level_all_base_measures_full_match_pg_result", result); + insta::assert_snapshot!( + "multi_level_all_base_measures_full_match_cubestore_result", + result + ); } } @@ -326,11 +332,11 @@ async fn test_multi_level_all_base_measures_partial_match() { assert_eq!(pre_aggrs[0].name(), "all_base_measures_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { insta::assert_snapshot!( - "multi_level_all_base_measures_partial_match_pg_result", + "multi_level_all_base_measures_partial_match_cubestore_result", result ); } @@ -376,11 +382,11 @@ async fn test_multi_level_calculated_measure_full_match() { assert_eq!(pre_aggrs[0].name(), "calculated_measure_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { insta::assert_snapshot!( - "multi_level_calculated_measure_full_match_pg_result", + "multi_level_calculated_measure_full_match_cubestore_result", result ); } @@ -408,10 +414,13 @@ async fn test_multi_level_mixed_measure_full_match() { assert_eq!(pre_aggrs[0].name(), "mixed_measure_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("multi_level_mixed_measure_full_match_pg_result", result); + insta::assert_snapshot!( + "multi_level_mixed_measure_full_match_cubestore_result", + result + ); } } @@ -455,10 +464,13 @@ async fn test_base_and_calculated_measure_full_match() { assert_eq!(pre_aggrs[0].name(), "base_and_calculated_measure_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("base_and_calculated_measure_full_match_pg_result", result); + insta::assert_snapshot!( + "base_and_calculated_measure_full_match_cubestore_result", + result + ); } } @@ -483,11 +495,11 @@ async fn test_base_and_calculated_measure_parital_match() { assert_eq!(pre_aggrs[0].name(), "base_and_calculated_measure_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { insta::assert_snapshot!( - "base_and_calculated_measure_parital_match_pg_result", + "base_and_calculated_measure_parital_match_cubestore_result", result ); } @@ -521,10 +533,10 @@ async fn test_segment_full_match() { assert_eq!(pre_aggrs[0].name(), "segment_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("segment_full_match_pg_result", result); + insta::assert_snapshot!("segment_full_match_cubestore_result", result); } } @@ -552,10 +564,13 @@ async fn test_segment_partial_match_unused_segment() { assert_eq!(pre_aggrs[0].name(), "segment_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("segment_partial_match_unused_segment_pg_result", result); + insta::assert_snapshot!( + "segment_partial_match_unused_segment_cubestore_result", + result + ); } } @@ -606,10 +621,10 @@ async fn test_custom_granularity_full_match() { assert_eq!(pre_aggrs[0].name(), "custom_half_year_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("custom_granularity_full_match_pg_result", result); + insta::assert_snapshot!("custom_granularity_full_match_cubestore_result", result); } } @@ -637,10 +652,13 @@ async fn test_standard_pre_agg_coarser_custom_query() { assert_eq!(pre_aggrs[0].name(), "daily_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("standard_pre_agg_coarser_custom_query_pg_result", result); + insta::assert_snapshot!( + "standard_pre_agg_coarser_custom_query_cubestore_result", + result + ); } } @@ -710,11 +728,11 @@ async fn test_custom_granularity_non_additive_full_match() { assert_eq!(pre_aggrs[0].name(), "custom_half_year_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { insta::assert_snapshot!( - "custom_granularity_non_additive_full_match_pg_result", + "custom_granularity_non_additive_full_match_cubestore_result", result ); } @@ -763,10 +781,13 @@ async fn test_custom_granularity_non_strict_self_match() { assert_eq!(pre_aggrs[0].name(), "custom_half_year_non_strict"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("custom_granularity_non_strict_self_match_pg_result", result); + insta::assert_snapshot!( + "custom_granularity_non_strict_self_match_cubestore_result", + result + ); } } @@ -796,10 +817,10 @@ async fn test_segment_with_coarser_granularity() { assert_eq!(pre_aggrs[0].name(), "segment_rollup"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "pre_aggregation_matching_tables.sql") + .try_execute(query_yaml, "pre_aggregation_matching_tables.sql") .await { - insta::assert_snapshot!("segment_with_coarser_granularity_pg_result", result); + insta::assert_snapshot!("segment_with_coarser_granularity_cubestore_result", result); } } @@ -824,11 +845,11 @@ async fn test_multi_stage_count_distinct_sum_by_quarter_with_pre_aggregation() { assert_eq!(pre_aggrs[0].name(), "main"); if let Some(result) = ctx - .try_execute_pg(query_yaml, "multi_stage_sum_by_quarter_tables.sql") + .try_execute(query_yaml, "multi_stage_sum_by_quarter_tables.sql") .await { insta::assert_snapshot!( - "multi_stage_count_distinct_sum_by_quarter_with_pre_agg_pg_result", + "multi_stage_count_distinct_sum_by_quarter_with_pre_agg_cubestore_result", result ); } @@ -870,10 +891,10 @@ async fn test_multi_stage_separate_pre_aggregations() { ); if let Some(result) = ctx - .try_execute_pg(query_yaml, "multi_stage_separate_pre_aggs_tables.sql") + .try_execute(query_yaml, "multi_stage_separate_pre_aggs_tables.sql") .await { - insta::assert_snapshot!("multi_stage_separate_pre_aggs_pg_result", result); + insta::assert_snapshot!("multi_stage_separate_pre_aggs_cubestore_result", result); } } @@ -934,10 +955,13 @@ async fn test_multi_stage_separate_pre_aggs_with_time_shift() { ); if let Some(result) = ctx - .try_execute_pg(query_yaml, "multi_stage_pre_agg_time_shift_tables.sql") + .try_execute(query_yaml, "multi_stage_pre_agg_time_shift_tables.sql") .await { - insta::assert_snapshot!("multi_stage_separate_pre_aggs_time_shift_pg_result", result); + insta::assert_snapshot!( + "multi_stage_separate_pre_aggs_time_shift_cubestore_result", + result + ); } } From 9d729a0d0818e82ec7bb1600db8600218089946c Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 5 Jun 2026 11:56:19 +0200 Subject: [PATCH 4/8] test(tesseract): default cubestored to downloaded binary, gate local build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve cubestored from the downloaded @cubejs-backend/cubestore release binary by default (suits Tesseract development without a 10-minute CubeStore build). Set CUBESTORED_LOCAL_BUILD=1 to use a local cargo build from rust/cubestore/target (release preferred — debug stack-overflows on deep multi-stage plans). CUBESTORED_BIN_PATH still overrides everything. --- .../test_utils/cubestore_service.rs | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs index 8fc9e37dc1f1a..5f7e4ff066e9a 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs @@ -37,6 +37,12 @@ fn register_atexit_cleanup(pid: u32, data_dir: PathBuf) { static CS_INSTANCE: OnceCell = OnceCell::const_new(); static SCHEMA_COUNTER: AtomicU64 = AtomicU64::new(0); +/// Resolves the cubestored binary. Default is the downloaded release binary +/// (`@cubejs-backend/cubestore` postinstall), which suits Tesseract +/// development. Set `CUBESTORED_LOCAL_BUILD=1` to use a locally built binary +/// from `rust/cubestore/target` (for CubeStore development); the release +/// profile is preferred since a debug build stack-overflows on deep +/// multi-stage plans. `CUBESTORED_BIN_PATH` overrides everything. fn cubestored_bin() -> PathBuf { if let Ok(path) = std::env::var("CUBESTORED_BIN_PATH") { let path = PathBuf::from(path); @@ -47,20 +53,32 @@ fn cubestored_bin() -> PathBuf { } let cubestore_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../cubestore"); - for candidate in [ - cubestore_root.join("target/release/cubestored"), - cubestore_root.join("target/debug/cubestored"), - cubestore_root.join("downloaded/latest/bin/cubestored"), - ] { - if candidate.exists() { - return candidate; + + if std::env::var("CUBESTORED_LOCAL_BUILD").is_ok_and(|v| !v.is_empty()) { + for candidate in [ + cubestore_root.join("target/release/cubestored"), + cubestore_root.join("target/debug/cubestored"), + ] { + if candidate.exists() { + return candidate; + } } + panic!( + "CUBESTORED_LOCAL_BUILD is set but no local binary found. Build it with \ + `cargo build --release -p cubestore --bin cubestored` in rust/cubestore" + ); + } + + let downloaded = cubestore_root.join("downloaded/latest/bin/cubestored"); + if downloaded.exists() { + return downloaded; } panic!( - "cubestored binary not found. Build it with \ - `cargo build -p cubestore --bin cubestored` in rust/cubestore \ - or set CUBESTORED_BIN_PATH" + "cubestored binary not found at {:?}. Install it via the \ + @cubejs-backend/cubestore package, or set CUBESTORED_LOCAL_BUILD=1 \ + to use a local `cargo build` from rust/cubestore, or CUBESTORED_BIN_PATH", + downloaded ); } From 3ab2c2fd045724a5c627eac2922d9dde837e866c Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 5 Jun 2026 12:19:34 +0200 Subject: [PATCH 5/8] test(tesseract): prefer release cubestored, warn on unused debug build Resolve cubestored as: local release build (where CI drops the build-cubestore artifact) -> downloaded @cubejs-backend/cubestore release. A debug build is ignored by default (it stack-overflows on deep multi-stage plans) but warns when present and unused; CUBESTORED_ALLOW_DEBUG=1 opts into it for fast local iteration on features that don't hit the overflow. CUBESTORED_BIN_PATH still overrides everything. --- .../test_utils/cubestore_service.rs | 69 ++++++++++++------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs index 5f7e4ff066e9a..319d9340b8ba8 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/cubestore_service.rs @@ -37,12 +37,14 @@ fn register_atexit_cleanup(pid: u32, data_dir: PathBuf) { static CS_INSTANCE: OnceCell = OnceCell::const_new(); static SCHEMA_COUNTER: AtomicU64 = AtomicU64::new(0); -/// Resolves the cubestored binary. Default is the downloaded release binary -/// (`@cubejs-backend/cubestore` postinstall), which suits Tesseract -/// development. Set `CUBESTORED_LOCAL_BUILD=1` to use a locally built binary -/// from `rust/cubestore/target` (for CubeStore development); the release -/// profile is preferred since a debug build stack-overflows on deep -/// multi-stage plans. `CUBESTORED_BIN_PATH` overrides everything. +/// Resolves the cubestored binary. Prefers a local release build +/// (`rust/cubestore/target/release`, which is where CI drops the +/// `build-cubestore` artifact), falling back to the downloaded +/// `@cubejs-backend/cubestore` release binary. A debug build is ignored by +/// default — it stack-overflows on deep multi-stage plans — but if one is +/// present and unused we warn, and `CUBESTORED_ALLOW_DEBUG=1` opts into it +/// for fast local iteration on features that don't hit the overflow. +/// `CUBESTORED_BIN_PATH` overrides everything. fn cubestored_bin() -> PathBuf { if let Ok(path) = std::env::var("CUBESTORED_BIN_PATH") { let path = PathBuf::from(path); @@ -53,32 +55,49 @@ fn cubestored_bin() -> PathBuf { } let cubestore_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../cubestore"); + let release = cubestore_root.join("target/release/cubestored"); + let debug = cubestore_root.join("target/debug/cubestored"); + let downloaded = cubestore_root.join("downloaded/latest/bin/cubestored"); + let allow_debug = std::env::var("CUBESTORED_ALLOW_DEBUG").is_ok_and(|v| !v.is_empty()); - if std::env::var("CUBESTORED_LOCAL_BUILD").is_ok_and(|v| !v.is_empty()) { - for candidate in [ - cubestore_root.join("target/release/cubestored"), - cubestore_root.join("target/debug/cubestored"), - ] { - if candidate.exists() { - return candidate; - } - } - panic!( - "CUBESTORED_LOCAL_BUILD is set but no local binary found. Build it with \ - `cargo build --release -p cubestore --bin cubestored` in rust/cubestore" + // Explicit opt-in: run against the local debug build (fast iteration). + if allow_debug && debug.exists() { + return debug; + } + + let chosen = if release.exists() { + Some(release.clone()) + } else if downloaded.exists() { + Some(downloaded.clone()) + } else { + None + }; + + if debug.exists() { + eprintln!( + "warning: a debug cubestored build exists at {:?} but is not used — \ + a release build is required (debug stack-overflows on deep multi-stage \ + plans). Set CUBESTORED_ALLOW_DEBUG=1 to run against it anyway.", + debug ); } - let downloaded = cubestore_root.join("downloaded/latest/bin/cubestored"); - if downloaded.exists() { - return downloaded; + if let Some(path) = chosen { + return path; } + let debug_hint = if debug.exists() { + " A debug build exists; set CUBESTORED_ALLOW_DEBUG=1 to use it despite the \ + multi-stage stack overflow." + } else { + "" + }; panic!( - "cubestored binary not found at {:?}. Install it via the \ - @cubejs-backend/cubestore package, or set CUBESTORED_LOCAL_BUILD=1 \ - to use a local `cargo build` from rust/cubestore, or CUBESTORED_BIN_PATH", - downloaded + "cubestored binary not found. Build it with \ + `cargo build --release -p cubestore --bin cubestored` in rust/cubestore, \ + install it via the @cubejs-backend/cubestore package, or set \ + CUBESTORED_BIN_PATH.{}", + debug_hint ); } From 1f10f2c4c7059d1093538abfbff0aa536e95851c Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 5 Jun 2026 12:21:19 +0200 Subject: [PATCH 6/8] ci(tesseract): run cubesqlplanner pre-agg tests against CubeStore unit-core now consumes the build-cubestore release artifact (dropped into rust/cubestore/target/release, where the resolver looks first) and runs the cube workspace with cubesqlplanner/integration-cubestore instead of integration-postgres, so pre-aggregation tests execute on CubeStore. The artifact is the current source tree's CubeStore, so the same job catches both Tesseract and CubeStore regressions. --- .github/workflows/push.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 2198373b8f153..5fca051d48462 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -208,7 +208,7 @@ jobs: unit-core: runs-on: ubuntu-24.04 timeout-minutes: 60 - needs: latest-tag-sha + needs: [latest-tag-sha, build-cubestore] if: (needs['latest-tag-sha'].outputs.sha != github.sha) steps: @@ -219,8 +219,17 @@ jobs: with: toolchain: 1.90.0 components: rustfmt + # cubesqlplanner pre-aggregation tests run against this binary; the + # resolver picks up target/release first (see cubestore_service.rs). + - name: Download cubestored artifact + uses: actions/download-artifact@v4 + with: + path: ./rust/cubestore/target/release/ + name: cubestored-x86_64-unknown-linux-gnu-release + - name: Chmod +x for cubestored + run: chmod +x ./rust/cubestore/target/release/cubestored - name: Cargo test cube workspace - run: cargo test --manifest-path rust/cube/Cargo.toml --workspace --features cubesqlplanner/integration-postgres -j 4 + run: cargo test --manifest-path rust/cube/Cargo.toml --workspace --features cubesqlplanner/integration-cubestore -j 4 build-cubestore: needs: [latest-tag-sha] From db96b4aae0b20f82208663d490ba4e92c05f5f5f Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 5 Jun 2026 13:27:04 +0200 Subject: [PATCH 7/8] test(tesseract): pin row order via query ORDER BY, drop formatter sort Remove the post-execution row sort in the CubeStore result formatter and give every executing pre-agg test a total `order:` over its group-by keys, so row order is exercised by the query (CubeStore's auto ORDER BY is only partial) instead of being silently masked. NULLs now sort last (ASC), as CubeStore orders them. Also make AGGREGATIONS rendering for an aggregate index fail loudly (unimplemented!) on unsupported measure types (e.g. countDistinctApprox/HLL) instead of silently dropping them while still emitting AGGREGATE INDEX. --- .../test_fixtures/test_utils/test_context.rs | 31 ++++++---- ...basic__basic_pre_agg_cubestore_result.snap | 4 +- .../pre_aggregations/multi_stage.rs | 4 ++ ...act_plus_multiplied_separate_pre_aggs.snap | 4 +- ...plied_whole_query_single_rollup_match.snap | 4 +- ...n__basic_pre_agg_sql_cubestore_result.snap | 4 +- .../pre_aggregations/sql_generation.rs | 57 +++++++++++++++++++ 7 files changed, 89 insertions(+), 19 deletions(-) diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs index 1a049c7981420..b710859ea3229 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs @@ -824,7 +824,7 @@ impl TestContext { .collect() }) .unwrap_or_default(); - let mut formatted_rows: Vec> = rows + let formatted_rows: Vec> = rows .iter() .map(|r| { (0..r.columns_ref().len()) @@ -833,11 +833,10 @@ impl TestContext { }) .collect(); - // CubeStore parallel aggregation yields rows in nondeterministic - // order; sort for stable snapshots without requiring ORDER BY in - // every query. - formatted_rows.sort(); - + // Rows are NOT re-sorted here: every test must carry a total `order:` + // so the query itself pins row order (CubeStore aggregates in parallel + // and returns rows unordered otherwise). This keeps ORDER BY rendering + // under test instead of masking it. Some(super::integration_context::format_rows_table( columns, formatted_rows, @@ -1010,16 +1009,26 @@ impl TestContext { let aggregations: Vec = pre_agg .measures() .iter() - .filter_map(|m| { - let func = match m.as_measure().ok()?.measure_type() { + .map(|m| { + let measure = m.as_measure().expect("aggregate-index measure"); + let func = match measure.measure_type() { "count" | "sum" => "sum", "min" => "min", "max" => "max", - _ => return None, + // HLL (countDistinctApprox) needs a `merge(...)` aggregation + // and an HLL-encoded upload, neither of which is wired yet. + other => unimplemented!( + "AGGREGATIONS for measure type '{}' (e.g. countDistinctApprox) \ + is not supported by the CubeStore test harness yet", + other + ), }; let alias = m.alias(); - let column = columns.iter().find(|(n, _)| *n == alias)?; - Some(format!("{}(\"{}\")", func, column.0)) + let column = columns + .iter() + .find(|(n, _)| *n == alias) + .unwrap_or_else(|| panic!("aggregation column {} not found", alias)); + format!("{}(\"{}\")", func, column.0) }) .collect(); if !aggregations.is_empty() { diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap index 671c812914b67..62145a0ccfd3d 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/cubestore/snapshots/cubesqlplanner__tests__integration__cubestore__basic__basic_pre_agg_cubestore_result.snap @@ -4,7 +4,7 @@ expression: result --- visitors__source | visitors__count -----------------+---------------- -NULL | 1 google | 5 organic | 2 -twitter | 2 +twitter | 2 +NULL | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs index bfa383a8a7f2a..a18e51b63a3ae 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/multi_stage.rs @@ -26,6 +26,8 @@ async fn test_multi_stage_time_shift_pre_agg_with_leaf_measure() { dateRange: - "2024-01-01" - "2024-03-31" + order: + - id: returns.created_at "#}; let (_sql, pre_aggrs) = ctx.build_sql_with_used_pre_aggregations(query).unwrap(); @@ -86,6 +88,8 @@ async fn test_multi_stage_time_shift_pre_agg_with_multi_stage_measure() { dateRange: - "2024-01-01" - "2024-03-31" + order: + - id: returns.created_at "#}; let (_sql, pre_aggrs) = ctx.build_sql_with_used_pre_aggregations(query).unwrap(); diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__fact_plus_multiplied_separate_pre_aggs.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__fact_plus_multiplied_separate_pre_aggs.snap index b0dc88dd3eb5c..1de60218c3697 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__fact_plus_multiplied_separate_pre_aggs.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__fact_plus_multiplied_separate_pre_aggs.snap @@ -4,6 +4,6 @@ expression: result --- orders__status | orders__count | customers__count ---------------+---------------+----------------- -NULL | NULL | NULL completed | 5 | 3 -pending | 3 | 2 +pending | 3 | 2 +NULL | NULL | NULL diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match.snap index 2bbbc2d0e3c1c..4b69ce3143bf1 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__multi_fact__multiplied_whole_query_single_rollup_match.snap @@ -4,6 +4,6 @@ expression: result --- orders__status | customers__count | customers__total_lifetime_value ---------------+------------------+-------------------------------- -NULL | 1 | 500 completed | 3 | 4500 -pending | 2 | 3000 +pending | 2 | 3000 +NULL | 1 | 500 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_cubestore_result.snap b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_cubestore_result.snap index ccca5cbc4aaf9..e979bd5668400 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_cubestore_result.snap +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/snapshots/cubesqlplanner__tests__integration__pre_aggregations__sql_generation__basic_pre_agg_sql_cubestore_result.snap @@ -4,7 +4,7 @@ expression: result --- visitors__source | visitors__count -----------------+---------------- -NULL | 1 google | 5 organic | 2 -twitter | 2 +twitter | 2 +NULL | 1 diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs index 92336e4204226..803a17906883b 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs @@ -17,6 +17,8 @@ async fn test_basic_pre_agg_sql() { - visitors.count dimensions: - visitors.source + order: + - id: visitors.source "}; let (_sql, pre_aggrs) = test_context @@ -47,6 +49,9 @@ async fn test_full_match_main_rollup() { dimensions: - orders.status - orders.city + order: + - id: orders.status + - id: orders.city "}; let (_sql, pre_aggrs) = ctx @@ -75,6 +80,8 @@ async fn test_partial_match_main_rollup() { - orders.count dimensions: - orders.status + order: + - id: orders.status "}; let (_sql, pre_aggrs) = ctx @@ -104,6 +111,9 @@ async fn test_full_match_non_additive_measure() { dimensions: - orders.status - orders.city + order: + - id: orders.status + - id: orders.city "}; let (_sql, pre_aggrs) = ctx @@ -153,6 +163,9 @@ async fn test_daily_rollup_full_match() { time_dimensions: - dimension: orders.created_at granularity: day + order: + - id: orders.country + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx @@ -184,6 +197,9 @@ async fn test_daily_rollup_coarser_granularity() { time_dimensions: - dimension: orders.created_at granularity: month + order: + - id: orders.country + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx @@ -236,6 +252,9 @@ async fn test_daily_rollup_non_additive_full_match() { time_dimensions: - dimension: orders.created_at granularity: day + order: + - id: orders.country + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx @@ -291,6 +310,9 @@ async fn test_multi_level_all_base_measures_full_match() { dimensions: - orders.status - orders.city + order: + - id: orders.status + - id: orders.city "}; let (_sql, pre_aggrs) = ctx @@ -322,6 +344,8 @@ async fn test_multi_level_all_base_measures_partial_match() { - orders.multi_level_measure dimensions: - orders.status + order: + - id: orders.status "}; let (_sql, pre_aggrs) = ctx @@ -372,6 +396,9 @@ async fn test_multi_level_calculated_measure_full_match() { dimensions: - orders.status - orders.city + order: + - id: orders.status + - id: orders.city "}; let (_sql, pre_aggrs) = ctx @@ -404,6 +431,9 @@ async fn test_multi_level_mixed_measure_full_match() { dimensions: - orders.status - orders.city + order: + - id: orders.status + - id: orders.city "}; let (_sql, pre_aggrs) = ctx @@ -454,6 +484,9 @@ async fn test_base_and_calculated_measure_full_match() { dimensions: - orders.status - orders.city + order: + - id: orders.status + - id: orders.city "}; let (_sql, pre_aggrs) = ctx @@ -485,6 +518,8 @@ async fn test_base_and_calculated_measure_parital_match() { - orders.amount_per_count dimensions: - orders.status + order: + - id: orders.status "}; let (_sql, pre_aggrs) = ctx @@ -523,6 +558,9 @@ async fn test_segment_full_match() { time_dimensions: - dimension: orders.created_at granularity: day + order: + - id: orders.status + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx @@ -554,6 +592,9 @@ async fn test_segment_partial_match_unused_segment() { time_dimensions: - dimension: orders.created_at granularity: day + order: + - id: orders.status + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx @@ -611,6 +652,9 @@ async fn test_custom_granularity_full_match() { time_dimensions: - dimension: orders.created_at granularity: half_year + order: + - id: orders.status + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx @@ -642,6 +686,9 @@ async fn test_standard_pre_agg_coarser_custom_query() { time_dimensions: - dimension: orders.created_at granularity: half_year + order: + - id: orders.status + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx @@ -718,6 +765,9 @@ async fn test_custom_granularity_non_additive_full_match() { time_dimensions: - dimension: orders.created_at granularity: half_year + order: + - id: orders.status + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx @@ -771,6 +821,8 @@ async fn test_custom_granularity_non_strict_self_match() { time_dimensions: - dimension: orders.created_at granularity: half_year + order: + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx @@ -807,6 +859,9 @@ async fn test_segment_with_coarser_granularity() { time_dimensions: - dimension: orders.created_at granularity: month + order: + - id: orders.status + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx @@ -916,6 +971,8 @@ async fn test_multi_stage_separate_pre_aggs_with_time_shift() { - \"2025-01-01\" - \"2025-03-31\" cubestoreSupportMultistage: true + order: + - id: orders.created_at "}; let (_sql, pre_aggrs) = ctx From 2d38992a7af7821d1e460b24efdd27c040168737 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 5 Jun 2026 13:59:28 +0200 Subject: [PATCH 8/8] test(tesseract): silence dead_code on index mock without cubestore feature MockPreAggregationIndex and the indexes field/accessor are only read by the integration-cubestore-gated CubeStore table builder; allow dead_code only when that feature is off so clippy --all-targets is clean in every config. --- .../cube_bridge/mock_pre_aggregation_description.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_pre_aggregation_description.rs b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_pre_aggregation_description.rs index 73b3df6600493..e6ace18588807 100644 --- a/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_pre_aggregation_description.rs +++ b/rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_pre_aggregation_description.rs @@ -12,6 +12,8 @@ use typed_builder::TypedBuilder; /// Mirrors the JS pre-aggregation description's `createTableIndexes` entries: /// member references resolved to columns at table creation time. +// Only read by the CubeStore table builder, which is integration-cubestore-gated. +#[cfg_attr(not(feature = "integration-cubestore"), allow(dead_code))] #[derive(Debug, Clone)] pub struct MockPreAggregationIndex { pub name: String, @@ -46,10 +48,12 @@ pub struct MockPreAggregationDescription { #[builder(default)] time_dimension_references: Option>>, #[builder(default)] + #[cfg_attr(not(feature = "integration-cubestore"), allow(dead_code))] indexes: Vec, } impl MockPreAggregationDescription { + #[cfg_attr(not(feature = "integration-cubestore"), allow(dead_code))] pub fn indexes(&self) -> &[MockPreAggregationIndex] { &self.indexes }