diff --git a/packages/cubejs-backend-native/Cargo.lock b/packages/cubejs-backend-native/Cargo.lock index 1c143cdc5749b..3aef173e16537 100644 --- a/packages/cubejs-backend-native/Cargo.lock +++ b/packages/cubejs-backend-native/Cargo.lock @@ -176,7 +176,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -187,7 +187,7 @@ checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -395,7 +395,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", "syn_derive", ] @@ -998,7 +998,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -1219,7 +1219,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -1650,7 +1650,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -2076,7 +2076,7 @@ dependencies = [ "itertools 0.10.5", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] @@ -2101,7 +2101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6813fde79b646e47e7ad75f480aa80ef76a5d9599e2717407961531169ee38b" dependencies = [ "quote", - "syn 2.0.90", + "syn 2.0.98", "syn-mid", ] @@ -2335,7 +2335,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -2418,7 +2418,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -2621,7 +2621,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -2633,7 +2633,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -3064,7 +3064,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -3273,7 +3273,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -3312,9 +3312,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -3329,7 +3329,7 @@ checksum = "b5dc35bb08dd1ca3dfb09dce91fd2d13294d6711c88897d9a9d60acf39bce049" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -3341,7 +3341,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -3364,7 +3364,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -3435,7 +3435,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -3446,7 +3446,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -3534,7 +3534,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -3677,7 +3677,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -3963,7 +3963,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -3997,7 +3997,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4289,7 +4289,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", "synstructure", ] @@ -4310,7 +4310,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] @@ -4330,7 +4330,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", "synstructure", ] @@ -4359,7 +4359,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.98", ] [[package]] diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index 047a655757fb2..41cdbf3020517 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -3499,6 +3499,7 @@ export class BaseQuery { in: '{{ column }} IN ({{ values_concat }}){{ is_null_check }}', not_in: '{{ column }} NOT IN ({{ values_concat }}){{ is_null_check }}', time_range_filter: '{{ column }} >= {{ from_timestamp }} AND {{ column }} <= {{ to_timestamp }}', + time_not_in_range_filter: '{{ column }} < {{ from_timestamp }} OR {{ column }} > {{ to_timestamp }}', gt: '{{ column }} > {{ param }}', gte: '{{ column }} >= {{ param }}', lt: '{{ column }} < {{ param }}', diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/cube-views.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/cube-views.test.ts index cb0f60a240dc1..81a70b84baa94 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/cube-views.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/cube-views.test.ts @@ -1,3 +1,4 @@ +import { getEnv } from '@cubejs-backend/shared'; import { BaseQuery, PostgresQuery } from '../../../src/adapter'; import { prepareCompiler } from '../../unit/PrepareCompiler'; import { dbRunner } from './PostgresDBRunner'; @@ -12,9 +13,9 @@ cube(\`Orders\`, { UNION ALL SELECT 2 as id, 2 as product_id, 'completed' as status, '2022-01-02T00:00:00.000Z'::timestamptz as created_at \`, - + shown: false, - + refreshKey: { sql: \`SELECT MAX(created_at) FROM \${Orders.sql()} orders WHERE \${FILTER_PARAMS.Orders.createdAt.filter('created_at')}\` }, @@ -47,7 +48,7 @@ cube(\`Orders\`, { type: \`count\`, //drillMembers: [id, createdAt] }, - + runningTotal: { type: \`count\`, rollingWindow: { @@ -67,28 +68,28 @@ cube(\`Orders\`, { sql: \`status\`, type: \`string\` }, - + statusProduct: { sql: \`\${CUBE}.status || '_' || \${Products.name}\`, type: \`string\` }, - + createdAt: { sql: \`created_at\`, type: \`time\` }, - + productId: { sql: \`product_id\`, type: \`number\`, }, - + productAndCategory: { sql: \`\${Products.name} || '_' || \${Products.ProductCategories.name}\`, type: \`string\` }, }, - + segments: { potatoOnly: { sql: \`\${CUBE}.product_id = 2 AND \${FILTER_PARAMS.Orders.productId.filter(\`\${CUBE.productId}\`)}\`, @@ -104,7 +105,7 @@ cube(\`Products\`, { UNION ALL SELECT 2 as id, 1 as product_category_id, 'Potato' as name \`, - + joins: { ProductCategories: { sql: \`\${CUBE}.product_category_id = \${ProductCategories}.id\`, @@ -129,7 +130,7 @@ cube(\`Products\`, { sql: \`name\`, type: \`string\` }, - + proxyName: { sql: \`\${name}\`, type: \`string\`, @@ -201,7 +202,7 @@ cube(\`ProductCategories\`, { view(\`OrdersView\`, { includes: [Orders], excludes: [Orders.createdAt], - + measures: { productCategoryCount: { sql: \`\${Orders.ProductsAlt.ProductCategories.count}\`, @@ -224,7 +225,7 @@ view(\`OrdersView\`, { sql: \`\${Orders.ProductsAlt.ProductCategories.name}\`, type: \`string\` }, - + productCategory: { sql: \`\${Orders.ProductsAlt.name} || '_' || \${Orders.ProductsAlt.ProductCategories.name} || '_' || \${categoryName}\`, type: \`string\` diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-2.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-2.test.ts index d69dff1689897..27146aed738a3 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-2.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-2.test.ts @@ -1,3 +1,4 @@ +import { getEnv } from '@cubejs-backend/shared'; import { prepareCompiler } from '../../unit/PrepareCompiler'; import { dbRunner } from './PostgresDBRunner'; @@ -143,31 +144,61 @@ cube('D', { }); `); - it('join order', async () => dbRunner.runQueryTest({ - dimensions: [ - 'View.A_id', - 'View.A_name', - 'View.B_id', - 'View.B_name', - 'View.D_id', - 'View.D_name', - 'View.E_id', - 'View.E_name' - ], - timeDimensions: [], - segments: [], - filters: [], - total: true, - renewQuery: false, - limit: 1 - }, [{ - view___a_id: 1, - view___a_name: 'a', - view___b_id: 2, - view___b_name: 'b', - view___d_id: 3, - view___d_name: 'd', - view___e_id: 4, - view___e_name: 'e', - }], { compiler, joinGraph, cubeEvaluator })); + if (getEnv('nativeSqlPlanner')) { + it('join order', async () => dbRunner.runQueryTest({ + dimensions: [ + 'View.A_id', + 'View.A_name', + 'View.B_id', + 'View.B_name', + 'View.D_id', + 'View.D_name', + 'View.E_id', + 'View.E_name' + ], + timeDimensions: [], + segments: [], + filters: [], + total: true, + renewQuery: false, + limit: 1 + }, [{ + view__a_id: 1, + view__a_name: 'a', + view__b_id: 2, + view__b_name: 'b', + view__d_id: 3, + view__d_name: 'd', + view__e_id: 4, + view__e_name: 'e', + }], { compiler, joinGraph, cubeEvaluator })); + } else { + it('join order', async () => dbRunner.runQueryTest({ + dimensions: [ + 'View.A_id', + 'View.A_name', + 'View.B_id', + 'View.B_name', + 'View.D_id', + 'View.D_name', + 'View.E_id', + 'View.E_name' + ], + timeDimensions: [], + segments: [], + filters: [], + total: true, + renewQuery: false, + limit: 1 + }, [{ + view___a_id: 1, + view___a_name: 'a', + view___b_id: 2, + view___b_name: 'b', + view___d_id: 3, + view___d_name: 'd', + view___e_id: 4, + view___e_name: 'e', + }], { compiler, joinGraph, cubeEvaluator })); + } }); diff --git a/rust/cubesqlplanner/Cargo.lock b/rust/cubesqlplanner/Cargo.lock index 79f015520cb47..f5b32cd1e7703 100644 --- a/rust/cubesqlplanner/Cargo.lock +++ b/rust/cubesqlplanner/Cargo.lock @@ -175,7 +175,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -186,7 +186,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -336,7 +336,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", "syn_derive", ] @@ -1070,7 +1070,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -1729,7 +1729,7 @@ dependencies = [ "itertools 0.10.5", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] @@ -1754,7 +1754,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6813fde79b646e47e7ad75f480aa80ef76a5d9599e2717407961531169ee38b" dependencies = [ "quote", - "syn 2.0.95", + "syn 2.0.98", "syn-mid", ] @@ -1985,7 +1985,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -2068,7 +2068,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -2588,7 +2588,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -2764,7 +2764,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -2803,9 +2803,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.95" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -2820,7 +2820,7 @@ checksum = "b5dc35bb08dd1ca3dfb09dce91fd2d13294d6711c88897d9a9d60acf39bce049" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -2832,7 +2832,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -2901,7 +2901,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -2912,7 +2912,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -2980,7 +2980,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -3082,7 +3082,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] @@ -3316,7 +3316,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -3350,7 +3350,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3604,7 +3604,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.98", ] [[package]] diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs index 1e87da9363565..abe3fa8e533a0 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs @@ -49,7 +49,6 @@ impl FilterItem { #[derive(Serialize, Deserialize, Debug)] pub struct BaseQueryOptionsStatic { - pub measures: Option>, #[serde(rename = "timeDimensions")] pub time_dimensions: Option>, pub timezone: Option, @@ -64,18 +63,14 @@ pub struct BaseQueryOptionsStatic { #[nativebridge::native_bridge(BaseQueryOptionsStatic)] pub trait BaseQueryOptions { - #[field] - #[optional] - #[vec] + #[nbridge(field, optional, vec)] fn measures(&self) -> Result>, CubeError>; - #[field] - #[optional] - #[vec] + #[nbridge(field, optional, vec)] fn dimensions(&self) -> Result>, CubeError>; - #[field] + #[nbridge(field)] fn cube_evaluator(&self) -> Result, CubeError>; - #[field] + #[nbridge(field)] fn base_tools(&self) -> Result, CubeError>; - #[field] + #[nbridge(field)] fn join_graph(&self) -> Result, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_definition.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_definition.rs new file mode 100644 index 0000000000000..bb9145c443f14 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_definition.rs @@ -0,0 +1,19 @@ +use super::case_else_item::{CaseElseItem, NativeCaseElseItem}; +use super::case_item::{CaseItem, NativeCaseItem}; +use cubenativeutils::wrappers::serializer::{ + NativeDeserialize, NativeDeserializer, NativeSerialize, +}; +use cubenativeutils::wrappers::NativeArray; +use cubenativeutils::wrappers::NativeContextHolder; +use cubenativeutils::wrappers::NativeObjectHandle; +use cubenativeutils::CubeError; +use std::any::Any; +use std::rc::Rc; + +#[nativebridge::native_bridge] +pub trait CaseDefinition { + #[nbridge(field, vec)] + fn when(&self) -> Result>, CubeError>; + #[nbridge(field, rename = "else")] + fn else_label(&self) -> Result, CubeError>; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_else_item.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_else_item.rs new file mode 100644 index 0000000000000..250dbd135479c --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_else_item.rs @@ -0,0 +1,15 @@ +use super::case_label::CaseLabel; +use cubenativeutils::wrappers::serializer::{ + NativeDeserialize, NativeDeserializer, NativeSerialize, +}; +use cubenativeutils::wrappers::NativeContextHolder; +use cubenativeutils::wrappers::NativeObjectHandle; +use cubenativeutils::CubeError; +use std::any::Any; +use std::rc::Rc; + +#[nativebridge::native_bridge] +pub trait CaseElseItem { + #[nbridge(field)] + fn label(&self) -> Result; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_item.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_item.rs new file mode 100644 index 0000000000000..2d6d4865cace5 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_item.rs @@ -0,0 +1,18 @@ +use super::case_label::CaseLabel; +use super::member_sql::{MemberSql, NativeMemberSql}; +use cubenativeutils::wrappers::serializer::{ + NativeDeserialize, NativeDeserializer, NativeSerialize, +}; +use cubenativeutils::wrappers::NativeContextHolder; +use cubenativeutils::wrappers::NativeObjectHandle; +use cubenativeutils::CubeError; +use std::any::Any; +use std::rc::Rc; + +#[nativebridge::native_bridge] +pub trait CaseItem { + #[nbridge(field)] + fn sql(&self) -> Result, CubeError>; + #[nbridge(field)] + fn label(&self) -> Result; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_label.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_label.rs new file mode 100644 index 0000000000000..6f9d8fafb1ecf --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_label.rs @@ -0,0 +1,25 @@ +use super::struct_with_sql_member::{NativeStructWithSqlMember, StructWithSqlMember}; +use cubenativeutils::wrappers::inner_types::InnerTypes; +use cubenativeutils::wrappers::serializer::NativeDeserialize; +use cubenativeutils::wrappers::NativeObjectHandle; +use cubenativeutils::CubeError; +use std::rc::Rc; + +pub enum CaseLabel { + String(String), + MemberSql(Rc), +} + +impl NativeDeserialize for CaseLabel { + fn from_native(native_object: NativeObjectHandle) -> Result { + match String::from_native(native_object.clone()) { + Ok(label) => Ok(Self::String(label)), + Err(_) => match NativeStructWithSqlMember::from_native(native_object) { + Ok(obj) => Ok(Self::MemberSql(Rc::new(obj))), + Err(_) => Err(CubeError::user(format!( + "String or object with sql property expected as label" + ))), + }, + } + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs index c8d9d9ef76da5..075b0589675f3 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs @@ -18,10 +18,8 @@ pub struct CubeDefinitionStatic { #[nativebridge::native_bridge(CubeDefinitionStatic)] pub trait CubeDefinition { - #[field] - #[optional] + #[nbridge(field, optional)] fn sql_table(&self) -> Result>, CubeError>; - #[field] - #[optional] + #[nbridge(field, optional)] fn sql(&self) -> Result>, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/dimension_definition.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/dimension_definition.rs index 6ca6e193edf42..7604de1551dc1 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/dimension_definition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/dimension_definition.rs @@ -1,3 +1,4 @@ +use super::case_definition::{CaseDefinition, NativeCaseDefinition}; use super::geo_item::{GeoItem, NativeGeoItem}; use super::member_sql::{MemberSql, NativeMemberSql}; use cubenativeutils::wrappers::serializer::{ @@ -26,15 +27,15 @@ pub struct DimenstionDefinitionStatic { #[nativebridge::native_bridge(DimenstionDefinitionStatic)] pub trait DimensionDefinition { - #[optional] - #[field] + #[nbridge(field, optional)] fn sql(&self) -> Result>, CubeError>; - #[optional] - #[field] + #[nbridge(field, optional)] + fn case(&self) -> Result>, CubeError>; + + #[nbridge(field, optional)] fn latitude(&self) -> Result>, CubeError>; - #[optional] - #[field] + #[nbridge(field, optional)] fn longitude(&self) -> Result>, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs index 7c02a6f7a0afd..cf353e07b7aab 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs @@ -19,7 +19,7 @@ pub struct CubeEvaluatorStatic { pub primary_keys: HashMap>, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Hash)] pub struct CallDep { pub name: String, pub parent: Option, @@ -27,7 +27,7 @@ pub struct CallDep { #[nativebridge::native_bridge(CubeEvaluatorStatic)] pub trait CubeEvaluator { - #[field] + #[nbridge(field)] fn primary_keys(&self) -> Result, CubeError>; fn parse_path(&self, path_type: String, path: String) -> Result, CubeError>; fn measure_by_path(&self, measure_path: String) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/geo_item.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/geo_item.rs index 8742686db9555..b13bc4acf7dbc 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/geo_item.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/geo_item.rs @@ -10,6 +10,6 @@ use std::rc::Rc; #[nativebridge::native_bridge] pub trait GeoItem { - #[field] + #[nbridge(field)] fn sql(&self) -> Result, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_definition.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_definition.rs index 020b3dd6b8d3c..2ff82aa8c0eae 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_definition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_definition.rs @@ -20,7 +20,6 @@ pub struct JoinDefinitionStatic { #[nativebridge::native_bridge(JoinDefinitionStatic)] pub trait JoinDefinition { - #[field] - #[vec] + #[nbridge(field, vec)] fn joins(&self) -> Result>, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_item.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_item.rs index c712cd3c0697c..922de7cbde963 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_item.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_item.rs @@ -21,6 +21,6 @@ pub struct JoinItemStatic { #[nativebridge::native_bridge(JoinItemStatic)] pub trait JoinItem { - #[field] + #[nbridge(field)] fn join(&self) -> Result, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_item_definition.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_item_definition.rs index cca8d2cce702f..7d14e64fe3756 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_item_definition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_item_definition.rs @@ -16,6 +16,6 @@ pub struct JoinItemDefinitionStatic { #[nativebridge::native_bridge(JoinItemDefinitionStatic)] pub trait JoinItemDefinition { - #[field] + #[nbridge(field)] fn sql(&self) -> Result, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/measure_definition.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/measure_definition.rs index 54329c773894e..38f7cd6f8f187 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/measure_definition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/measure_definition.rs @@ -1,7 +1,7 @@ use super::cube_definition::{CubeDefinition, NativeCubeDefinition}; -use super::measure_filter::{MeasureFilter, NativeMeasureFilter}; use super::member_order_by::{MemberOrderBy, NativeMemberOrderBy}; use super::member_sql::{MemberSql, NativeMemberSql}; +use super::struct_with_sql_member::{NativeStructWithSqlMember, StructWithSqlMember}; use cubenativeutils::wrappers::serializer::{ NativeDeserialize, NativeDeserializer, NativeSerialize, }; @@ -54,24 +54,17 @@ pub struct MeasureDefinitionStatic { #[nativebridge::native_bridge(MeasureDefinitionStatic)] pub trait MeasureDefinition { - #[optional] - #[field] + #[nbridge(field, optional)] fn sql(&self) -> Result>, CubeError>; fn cube(&self) -> Result, CubeError>; - #[optional] - #[field] - #[vec] - fn filters(&self) -> Result>>, CubeError>; + #[nbridge(field, optional, vec)] + fn filters(&self) -> Result>>, CubeError>; - #[optional] - #[field] - #[vec] - fn drill_filters(&self) -> Result>>, CubeError>; + #[nbridge(field, optional, vec)] + fn drill_filters(&self) -> Result>>, CubeError>; - #[optional] - #[field] - #[vec] + #[nbridge(field, optional, vec)] fn order_by(&self) -> Result>>, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_definition.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_definition.rs index a0b82971482c9..275b6ee4e8178 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_definition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_definition.rs @@ -17,7 +17,6 @@ pub struct MemberDefinitionStatic { #[nativebridge::native_bridge(MemberDefinitionStatic)] pub trait MemberDefinition { - #[optional] - #[field] + #[nbridge(field, optional)] fn sql(&self) -> Result>, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_expression.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_expression.rs index 45d2a26ddeb90..58d149671b447 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_expression.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_expression.rs @@ -19,6 +19,6 @@ pub struct MemberExpressionDefinitionStatic { #[nativebridge::native_bridge(MemberExpressionDefinitionStatic)] pub trait MemberExpressionDefinition { - #[field] + #[nbridge(field)] fn expression(&self) -> Result, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_order_by.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_order_by.rs index 0f56aeafc41f7..de025bd72925b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_order_by.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_order_by.rs @@ -10,8 +10,8 @@ use std::rc::Rc; #[nativebridge::native_bridge] pub trait MemberOrderBy { - #[field] + #[nbridge(field)] fn sql(&self) -> Result, CubeError>; - #[field] + #[nbridge(field)] fn dir(&self) -> Result; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs index 6fa12c63c418c..42763f52cf196 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs @@ -1,5 +1,9 @@ pub mod base_query_options; pub mod base_tools; +pub mod case_definition; +pub mod case_else_item; +pub mod case_item; +pub mod case_label; pub mod cube_definition; pub mod dimension_definition; pub mod evaluator; @@ -12,7 +16,6 @@ pub mod join_hints; pub mod join_item; pub mod join_item_definition; pub mod measure_definition; -pub mod measure_filter; pub mod member_definition; pub mod member_expression; pub mod member_order_by; @@ -21,3 +24,4 @@ pub mod options_member; pub mod security_context; pub mod sql_templates_render; pub mod sql_utils; +pub mod struct_with_sql_member; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/measure_filter.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/struct_with_sql_member.rs similarity index 88% rename from rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/measure_filter.rs rename to rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/struct_with_sql_member.rs index 500774e50b76f..3a42acbddf429 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/measure_filter.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/struct_with_sql_member.rs @@ -9,7 +9,7 @@ use std::any::Any; use std::rc::Rc; #[nativebridge::native_bridge] -pub trait MeasureFilter { - #[field] +pub trait StructWithSqlMember { + #[nbridge(field)] fn sql(&self) -> Result, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs index 5eb3e24c57285..c6895eec4b81d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs @@ -118,6 +118,11 @@ impl BaseFilter { FilterOperator::Equal => self.equals_where(&member_sql)?, FilterOperator::NotEqual => self.not_equals_where(&member_sql)?, FilterOperator::InDateRange => self.in_date_range(&member_sql)?, + FilterOperator::BeforeDate => self.before_date(&member_sql)?, + FilterOperator::BeforeOrOnDate => self.before_or_on_date(&member_sql)?, + FilterOperator::AfterDate => self.after_date(&member_sql)?, + FilterOperator::AfterOrOnDate => self.after_or_on_date(&member_sql)?, + FilterOperator::NotInDateRange => self.not_in_date_range(&member_sql)?, FilterOperator::RegularRollingWindowDateRange => { self.regular_rolling_window_date_range(&member_sql)? } @@ -225,6 +230,36 @@ impl BaseFilter { .time_range_filter(member_sql.to_string(), from, to) } + fn not_in_date_range(&self, member_sql: &str) -> Result { + let (from, to) = self.allocate_date_params(true)?; + self.templates + .time_not_in_range_filter(member_sql.to_string(), from, to) + } + + fn before_date(&self, member_sql: &str) -> Result { + let value = self.first_timestamp_param(true)?; + + self.templates.lt(member_sql.to_string(), value) + } + + fn before_or_on_date(&self, member_sql: &str) -> Result { + let value = self.first_timestamp_param(true)?; + + self.templates.lte(member_sql.to_string(), value) + } + + fn after_date(&self, member_sql: &str) -> Result { + let value = self.first_timestamp_param(true)?; + + self.templates.gt(member_sql.to_string(), value) + } + + fn after_or_on_date(&self, member_sql: &str) -> Result { + let value = self.first_timestamp_param(true)?; + + self.templates.gte(member_sql.to_string(), value) + } + fn extend_date_range_bound( &self, date: String, @@ -392,16 +427,40 @@ impl BaseFilter { )) } + fn from_date_in_db_time_zone( + &self, + value: &String, + use_db_time_zone: bool, + ) -> Result { + let from = self.format_from_date(value)?; + + let res = if use_db_time_zone && &from != FROM_PARTITION_RANGE { + self.query_tools.base_tools().in_db_time_zone(from)? + } else { + from + }; + Ok(res) + } + + fn to_date_in_db_time_zone( + &self, + value: &String, + use_db_time_zone: bool, + ) -> Result { + let from = self.format_to_date(value)?; + + let res = if use_db_time_zone && &from != TO_PARTITION_RANGE { + self.query_tools.base_tools().in_db_time_zone(from)? + } else { + from + }; + Ok(res) + } + fn allocate_date_params(&self, use_db_time_zone: bool) -> Result<(String, String), CubeError> { if self.values.len() >= 2 { let from = if let Some(from_str) = &self.values[0] { - let from = self.format_from_date(&from_str)?; - - if use_db_time_zone && &from != FROM_PARTITION_RANGE { - self.query_tools.base_tools().in_db_time_zone(from)? - } else { - from - } + self.from_date_in_db_time_zone(from_str, use_db_time_zone)? } else { return Err(CubeError::user(format!( "Arguments for date range is not valid" @@ -409,13 +468,7 @@ impl BaseFilter { }; let to = if let Some(to_str) = &self.values[1] { - let to = self.format_to_date(&to_str)?; - - if use_db_time_zone && &to != TO_PARTITION_RANGE { - self.query_tools.base_tools().in_db_time_zone(to)? - } else { - to - } + self.to_date_in_db_time_zone(to_str, use_db_time_zone)? } else { return Err(CubeError::user(format!( "Arguments for date range is not valid" @@ -517,6 +570,25 @@ impl BaseFilter { } } + fn first_timestamp_param(&self, use_db_time_zone: bool) -> Result { + if self.values.is_empty() { + Err(CubeError::user(format!( + "Expected at least one parameter but nothing found" + ))) + } else { + if let Some(value) = &self.values[0] { + Ok(self.allocate_timestamp_param( + &self.from_date_in_db_time_zone(value, use_db_time_zone)?, + )) + } else { + return Err(CubeError::user(format!( + "Arguments for timestamp parameter for operator {} is not valid", + self.filter_operator().to_string() + ))); + } + } + } + fn is_need_null_chek(&self, is_not: bool) -> bool { let contains_null = self.is_values_contains_null(); if is_not { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/filter_operator.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/filter_operator.rs index a713e1dee58e9..5c6ca3a669d63 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/filter_operator.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/filter_operator.rs @@ -6,6 +6,11 @@ pub enum FilterOperator { Equal, NotEqual, InDateRange, + NotInDateRange, + BeforeDate, + BeforeOrOnDate, + AfterDate, + AfterOrOnDate, RegularRollingWindowDateRange, ToDateRollingWindowDateRange, In, @@ -33,6 +38,12 @@ impl FromStr for FilterOperator { "equals" => Ok(Self::Equal), "notequals" => Ok(Self::NotEqual), "indaterange" => Ok(Self::InDateRange), + "onthedate" => Ok(Self::InDateRange), + "beforedate" => Ok(Self::BeforeDate), + "beforeorondate" => Ok(Self::BeforeOrOnDate), + "afterdate" => Ok(Self::AfterDate), + "afterorondate" => Ok(Self::AfterOrOnDate), + "notindaterange" => Ok(Self::NotInDateRange), "in" => Ok(Self::In), "notin" => Ok(Self::NotIn), "set" => Ok(Self::Set), @@ -60,6 +71,11 @@ impl ToString for FilterOperator { FilterOperator::Equal => "equals", FilterOperator::NotEqual => "notEquals", FilterOperator::InDateRange => "inDateRange", + FilterOperator::NotInDateRange => "notInDateRange", + FilterOperator::BeforeDate => "beforeDate", + FilterOperator::BeforeOrOnDate => "beforeOrOnDate", + FilterOperator::AfterDate => "afterDate", + FilterOperator::AfterOrOnDate => "afterOrOnDate", FilterOperator::RegularRollingWindowDateRange => "inDateRange", FilterOperator::ToDateRollingWindowDateRange => "inDateRange", FilterOperator::In => "in", diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs index 1c1c340ecfc57..4b31708df4f73 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs @@ -1,7 +1,7 @@ use super::{CommonUtils, DimensionSubqueryPlanner}; use crate::cube_bridge::join_definition::JoinDefinition; use crate::cube_bridge::join_hints::JoinHintItem; -use crate::cube_bridge::member_sql::MemberSql; +use crate::cube_bridge::join_item::JoinItem; use crate::plan::{From, JoinBuilder, JoinCondition}; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::SqlCall; @@ -49,9 +49,7 @@ impl JoinPlanner { ); dimension_subquery_planner.add_joins_for_cube(&mut join_builder, root.name())?; for join in joins.iter() { - let definition = join.join()?; - let sql_call = self - .compile_join_condition(&join.static_data().original_from, definition.sql()?)?; + let sql_call = self.compile_join_condition(join.clone())?; let on = JoinCondition::new_base_join(SqlJoinCondition::try_new( self.query_tools.clone(), sql_call, @@ -71,13 +69,14 @@ impl JoinPlanner { } } - fn compile_join_condition( + pub fn compile_join_condition( &self, - cube_name: &String, - sql: Rc, + join_item: Rc, ) -> Result, CubeError> { + let definition = join_item.join()?; let evaluator_compiler_cell = self.query_tools.evaluator_compiler().clone(); let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut(); - evaluator_compiler.compile_sql_call(&cube_name, sql) + evaluator_compiler + .compile_sql_call(&join_item.static_data().original_from, definition.sql()?) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs index c673c91fab748..4cb249075d27e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs @@ -387,6 +387,7 @@ impl MultiStageMemberQueryPlanner { if self.description.member().has_aggregates_on_top() { node_factory.set_count_approx_as_state(true); } + node_factory.set_ungrouped_measure(self.description.member().is_ungrupped()); if cte_query_properties .full_key_aggregate_measures()? diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs index 79529e600c4a6..771f61cf0de54 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs @@ -189,6 +189,7 @@ impl MultiStageQueryPlanner { &self, member: Rc, state: Rc, + ungrouped: bool, descriptions: &mut Vec>, ) -> Result, CubeError> { let alias = format!("cte_{}", descriptions.len()); @@ -196,7 +197,7 @@ impl MultiStageQueryPlanner { MultiStageMember::new( MultiStageMemberType::Leaf(MultiStageLeafMemberType::Measure), member, - self.query_properties.ungrouped(), + self.query_properties.ungrouped() || ungrouped, true, ), state, @@ -301,10 +302,20 @@ impl MultiStageQueryPlanner { granularity: None, } }; + let ungrouped = match member.as_ref() { + MemberSymbol::Measure(measure_symbol) => { + measure_symbol.is_rolling_window() && !measure_symbol.is_addictive() + } + _ => false, + }; let time_dimensions = self.query_properties.time_dimensions(); if time_dimensions.len() == 0 { - let rolling_base = - self.add_rolling_window_base(member.clone(), state.clone(), descriptions)?; + let rolling_base = self.add_rolling_window_base( + member.clone(), + state.clone(), + ungrouped, + descriptions, + )?; return Ok(Some(rolling_base)); } if time_dimensions.len() != 1 { @@ -323,6 +334,7 @@ impl MultiStageQueryPlanner { &rolling_window, state.clone(), )?, + ungrouped, descriptions, )?, ]; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs index a4508220dff50..19e252fe75cde 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs @@ -111,6 +111,8 @@ impl MultipliedMeasuresQueryPlanner { ) -> Result, CubeError> { let subquery_dimensions = collect_sub_query_dimensions_from_members( &BaseMemberHelper::iter_as_base_member(measures).collect_vec(), + &self.join_planner, + &key_join, self.query_tools.clone(), )?; @@ -312,6 +314,8 @@ impl MultipliedMeasuresQueryPlanner { ) -> Result, CubeError> { let subquery_dimensions = collect_sub_query_dimensions_from_symbols( &self.query_properties.all_member_symbols(false), + &self.join_planner, + &join, self.query_tools.clone(), )?; @@ -378,6 +382,8 @@ impl MultipliedMeasuresQueryPlanner { let subquery_dimensions = collect_sub_query_dimensions_from_symbols( &symbols_for_subquery_dimensions, + &self.join_planner, + &key_join, self.query_tools.clone(), )?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs index 9bb7aa3cee1c2..55ea723ecb739 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs @@ -30,8 +30,11 @@ impl SimpleQueryPlanner { } pub fn plan(&self) -> Result, CubeError> { + let join = self.query_properties.simple_query_join()?; let subquery_dimensions = collect_sub_query_dimensions_from_symbols( &self.query_properties.all_member_symbols(false), + &self.join_planner, + &join, self.query_tools.clone(), )?; let dimension_subquery_planner = DimensionSubqueryPlanner::try_new( @@ -49,11 +52,9 @@ impl SimpleQueryPlanner { }) }; let mut context_factory = self.context_factory.clone(); - let from = self.join_planner.make_join_node_impl( - &None, - self.query_properties.simple_query_join()?, - &dimension_subquery_planner, - )?; + let from = + self.join_planner + .make_join_node_impl(&None, join, &dimension_subquery_planner)?; let mut select_builder = SelectBuilder::new(from.clone()); for member in self diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs index cbc821b6f27f6..81de25bd2706f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs @@ -135,14 +135,47 @@ impl QueryProperties { Vec::new() }; - let measures = if let Some(measures) = &options.static_data().measures { + let measures = if let Some(measures) = &options.measures()? { measures .iter() - .map(|m| { - let evaluator = evaluator_compiler.add_measure_evaluator(m.clone())?; - BaseMeasure::try_new_required(evaluator, query_tools.clone()) + .map(|d| match d { + OptionsMember::MemberName(member_name) => { + let evaluator = + evaluator_compiler.add_measure_evaluator(member_name.clone())?; + BaseMeasure::try_new_required(evaluator, query_tools.clone()) + } + OptionsMember::MemberExpression(member_expression) => { + let cube_name = + if let Some(name) = &member_expression.static_data().cube_name { + name.clone() + } else { + "".to_string() + }; + let name = + if let Some(name) = &member_expression.static_data().expression_name { + name.clone() + } else { + "".to_string() + }; + let expression_evaluator = evaluator_compiler + .compile_sql_call(&cube_name, member_expression.expression()?)?; + BaseMeasure::try_new_from_expression( + expression_evaluator, + cube_name, + name, + member_expression.static_data().definition.clone(), + query_tools.clone(), + ) + } }) .collect::, _>>()? + /* measures + .iter() + .map(|m| { + let evaluator = evaluator_compiler.add_measure_evaluator(m.clone())?; + BaseMeasure::try_new_required(evaluator, query_tools.clone()) + }) + .collect::, _>>()? */ } else { Vec::new() }; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs index da597cbd85bcb..eec68a482bc51 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs @@ -11,7 +11,6 @@ use crate::plan::FilterItem; use crate::planner::sql_evaluator::collectors::collect_join_hints; use crate::planner::sql_templates::PlanSqlTemplates; use chrono_tz::Tz; -use convert_case::{Boundary, Case, Casing}; use cubenativeutils::CubeError; use itertools::Itertools; use lazy_static::lazy_static; @@ -187,9 +186,7 @@ impl QueryTools { } pub fn alias_name(&self, name: &str) -> String { - name.without_boundaries(&[Boundary::LOWER_DIGIT, Boundary::UPPER_DIGIT]) - .to_case(Case::Snake) - .replace(".", "__") + PlanSqlTemplates::alias_name(name) } pub fn escaped_alias_name(&self, name: &str) -> String { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs index e5e16d4cd53a7..36c7a364fc55b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs @@ -1,3 +1,5 @@ +use crate::cube_bridge::join_definition::JoinDefinition; +use crate::planner::planners::JoinPlanner; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::{DimensionSymbol, MemberSymbol, TraversalVisitor}; use crate::planner::{BaseDimension, BaseMember}; @@ -60,20 +62,30 @@ impl TraversalVisitor for SubQueryDimensionsCollector { pub fn collect_sub_query_dimensions_from_members( members: &Vec>, + join_planner: &JoinPlanner, + join: &Rc, query_tools: Rc, ) -> Result>, CubeError> { let symbols = members.iter().map(|m| m.member_evaluator()).collect_vec(); - collect_sub_query_dimensions_from_symbols(&symbols, query_tools) + collect_sub_query_dimensions_from_symbols(&symbols, join_planner, join, query_tools) } pub fn collect_sub_query_dimensions_from_symbols( members: &Vec>, + join_planner: &JoinPlanner, + join: &Rc, query_tools: Rc, ) -> Result>, CubeError> { let mut visitor = SubQueryDimensionsCollector::new(); for member in members.iter() { visitor.apply(&member, &())?; } + for join_item in join.joins()? { + let condition = join_planner.compile_join_condition(join_item.clone())?; + for dep in condition.get_dependencies() { + visitor.apply(&dep, &())?; + } + } visitor .extract_result() .into_iter() diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/dependecy.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/dependecy.rs index b1c69f57230b1..59512ba64fda7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/dependecy.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/dependecy.rs @@ -6,13 +6,13 @@ use cubenativeutils::CubeError; use std::collections::HashMap; use std::rc::Rc; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum CubeDepProperty { CubeDependency(CubeDependency), SymbolDependency(Rc), } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CubeDependency { pub cube_symbol: Rc, pub sql_fn: Option>, @@ -36,7 +36,7 @@ impl CubeDependency { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum ContextSymbolDep { SecurityContext, FilterParams, @@ -44,7 +44,7 @@ pub enum ContextSymbolDep { SqlUtils, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Dependency { SymbolDependency(Rc), CubeDependency(CubeDependency), @@ -76,14 +76,7 @@ impl<'a> DependenciesBuilder<'a> { vec![] }; - let mut childs = Vec::new(); - for (i, dep) in call_deps.iter().enumerate() { - childs.push(vec![]); - if let Some(parent) = dep.parent { - childs[parent].push(i); - } - } - + let childs = self.deduplicate_deps_and_make_childs_tree(&call_deps)?; let mut result = Vec::new(); for (i, dep) in call_deps.iter().enumerate() { @@ -107,6 +100,34 @@ impl<'a> DependenciesBuilder<'a> { Ok(result) } + fn deduplicate_deps_and_make_childs_tree( + &self, + call_deps: &Vec, + ) -> Result>, CubeError> { + let mut childs_tree = Vec::new(); + let mut deduplicate_index_map = HashMap::::new(); + let mut deduplicate_map = HashMap::::new(); + for (i, dep) in call_deps.iter().enumerate() { + //If subcube is used twice in function, then call_deps can hold duplicated dependencies + //(for exampls in function ${Orders.ProductsAlt.name} || '_' || ${Orders.ProductsAlt.ProductCategories.name} ProductsAlt appeared twice in call_deps)) + let self_index = if let Some(exists_index) = deduplicate_map.get(&dep) { + deduplicate_index_map.insert(i, *exists_index); + *exists_index + } else { + deduplicate_map.insert(dep.clone(), i); + i + }; + + childs_tree.push(vec![]); + if let Some(parent) = dep.parent { + let deduplecated_parent = deduplicate_index_map.get(&parent).unwrap_or(&parent); + childs_tree[*deduplecated_parent].push(self_index); + } + } + + Ok(childs_tree) + } + fn build_cube_dependency( &mut self, cube_name: &String, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/mod.rs index 83cdce4b7d78c..8aeaaafa89cd2 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/mod.rs @@ -15,7 +15,8 @@ pub use sql_call::SqlCall; pub use sql_visitor::SqlEvaluatorVisitor; pub use symbols::{ CubeNameSymbol, CubeNameSymbolFactory, CubeTableSymbol, CubeTableSymbolFactory, - DimensionSymbol, DimensionSymbolFactory, MeasureSymbol, MeasureSymbolFactory, - MemberExpressionSymbol, MemberSymbol, SymbolFactory, TimeDimensionSymbol, + DimensionCaseDefinition, DimensionCaseWhenItem, DimensionSymbol, DimensionSymbolFactory, + DimenstionCaseLabel, MeasureSymbol, MeasureSymbolFactory, MemberExpressionSymbol, MemberSymbol, + SymbolFactory, TimeDimensionSymbol, }; pub use visitor::TraversalVisitor; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/case_dimension.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/case_dimension.rs new file mode 100644 index 0000000000000..64088650984b5 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/case_dimension.rs @@ -0,0 +1,92 @@ +use super::SqlNode; +use crate::planner::query_tools::QueryTools; +use crate::planner::sql_evaluator::DimenstionCaseLabel; +use crate::planner::sql_evaluator::MemberSymbol; +use crate::planner::sql_evaluator::SqlEvaluatorVisitor; +use crate::planner::sql_templates::PlanSqlTemplates; +use cubenativeutils::CubeError; +use std::any::Any; +use std::rc::Rc; + +pub struct CaseDimensionSqlNode { + input: Rc, +} + +impl CaseDimensionSqlNode { + pub fn new(input: Rc) -> Rc { + Rc::new(Self { input }) + } + + pub fn input(&self) -> &Rc { + &self.input + } +} + +impl SqlNode for CaseDimensionSqlNode { + fn to_sql( + &self, + visitor: &SqlEvaluatorVisitor, + node: &Rc, + query_tools: Rc, + node_processor: Rc, + templates: &PlanSqlTemplates, + ) -> Result { + let res = match node.as_ref() { + MemberSymbol::Dimension(ev) => { + if let Some(case) = ev.case() { + let mut when_then = Vec::new(); + for itm in case.items.iter() { + let when = itm.sql.eval( + visitor, + node_processor.clone(), + query_tools.clone(), + templates, + )?; + let then = match &itm.label { + DimenstionCaseLabel::String(s) => templates.quote_string(&s)?, + DimenstionCaseLabel::Sql(sql) => sql.eval( + visitor, + node_processor.clone(), + query_tools.clone(), + templates, + )?, + }; + when_then.push((when, then)); + } + let else_label = match &case.else_label { + DimenstionCaseLabel::String(s) => templates.quote_string(&s)?, + DimenstionCaseLabel::Sql(sql) => sql.eval( + visitor, + node_processor.clone(), + query_tools.clone(), + templates, + )?, + }; + templates.case(None, when_then, Some(else_label))? + } else { + self.input.to_sql( + visitor, + node, + query_tools.clone(), + node_processor.clone(), + templates, + )? + } + } + _ => { + return Err(CubeError::internal(format!( + "CaseDimension node processor called for wrong node", + ))); + } + }; + Ok(res) + } + + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn childs(&self) -> Vec> { + vec![self.input.clone()] + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/factory.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/factory.rs index 0c3d4b727f7c0..eb1ab1f7fe6f1 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/factory.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/factory.rs @@ -1,8 +1,8 @@ use super::{ - AutoPrefixSqlNode, EvaluateSqlNode, FinalMeasureSqlNode, GeoDimensionSqlNode, - MeasureFilterSqlNode, MultiStageRankNode, MultiStageWindowNode, RenderReferencesSqlNode, - RollingWindowNode, RootSqlNode, SqlNode, TimeShiftSqlNode, UngroupedMeasureSqlNode, - UngroupedQueryFinalMeasureSqlNode, + AutoPrefixSqlNode, CaseDimensionSqlNode, EvaluateSqlNode, FinalMeasureSqlNode, + GeoDimensionSqlNode, MeasureFilterSqlNode, MultiStageRankNode, MultiStageWindowNode, + RenderReferencesSqlNode, RollingWindowNode, RootSqlNode, SqlNode, TimeShiftSqlNode, + UngroupedMeasureSqlNode, UngroupedQueryFinalMeasureSqlNode, }; use crate::plan::schema::QualifiedColumnName; use std::collections::{HashMap, HashSet}; @@ -164,19 +164,23 @@ impl SqlNodesFactory { UngroupedMeasureSqlNode::new(input) } else if self.ungrouped { UngroupedQueryFinalMeasureSqlNode::new(input) - } else if self.rolling_window { - RollingWindowNode::new(input) } else { - FinalMeasureSqlNode::new( - input, + let final_processor = FinalMeasureSqlNode::new( + input.clone(), self.rendered_as_multiplied_measures.clone(), self.count_approx_as_state, - ) + ); + if self.rolling_window { + RollingWindowNode::new(input, final_processor) + } else { + final_processor + } } } fn dimension_processor(&self, input: Rc) -> Rc { let input: Rc = GeoDimensionSqlNode::new(input); + let input: Rc = CaseDimensionSqlNode::new(input); let input: Rc = AutoPrefixSqlNode::new(input, self.cube_name_references.clone()); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/mod.rs index 8b1e16cdf471b..c1a791c631adf 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/mod.rs @@ -1,4 +1,5 @@ pub mod auto_prefix; +pub mod case_dimension; pub mod evaluate_sql; pub mod factory; pub mod final_measure; @@ -15,6 +16,7 @@ pub mod ungroupped_measure; pub mod ungroupped_query_final_measure; pub use auto_prefix::AutoPrefixSqlNode; +pub use case_dimension::CaseDimensionSqlNode; pub use evaluate_sql::EvaluateSqlNode; pub use factory::SqlNodesFactory; pub use final_measure::FinalMeasureSqlNode; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/rolling_window.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/rolling_window.rs index 18e14c93dcb2c..ea1821540635f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/rolling_window.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/rolling_window.rs @@ -8,11 +8,15 @@ use std::rc::Rc; pub struct RollingWindowNode { input: Rc, + default_processor: Rc, } impl RollingWindowNode { - pub fn new(input: Rc) -> Rc { - Rc::new(Self { input }) + pub fn new(input: Rc, default_processor: Rc) -> Rc { + Rc::new(Self { + input, + default_processor, + }) } pub fn input(&self) -> &Rc { @@ -31,30 +35,42 @@ impl SqlNode for RollingWindowNode { ) -> Result { let res = match node.as_ref() { MemberSymbol::Measure(m) => { - let input = self.input.to_sql( - visitor, - node, - query_tools.clone(), - node_processor, - templates, - )?; if m.is_cumulative() { + let input = self.input.to_sql( + visitor, + node, + query_tools.clone(), + node_processor.clone(), + templates, + )?; if m.measure_type() == "countDistinctApprox" { query_tools.base_tools().hll_cardinality_merge(input)? } else { - let aggregate_function = if m.measure_type() == "sum" + if m.measure_type() == "sum" || m.measure_type() == "count" || m.measure_type() == "runningTotal" { - "sum" + format!("sum({})", input) + } else if m.measure_type() == "min" || m.measure_type() == "max" { + format!("{}({})", m.measure_type(), input) } else { - m.measure_type() - }; - - format!("{}({})", aggregate_function, input) + self.default_processor.to_sql( + visitor, + node, + query_tools.clone(), + node_processor, + templates, + )? + } } } else { - input + self.default_processor.to_sql( + visitor, + node, + query_tools.clone(), + node_processor, + templates, + )? } } _ => { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs index c574afd0e0a58..38021370ae550 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs @@ -1,4 +1,5 @@ use super::{MemberSymbol, SymbolFactory}; +use crate::cube_bridge::case_label::CaseLabel; use crate::cube_bridge::dimension_definition::DimensionDefinition; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::member_sql::MemberSql; @@ -8,12 +9,28 @@ use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::CubeError; use std::rc::Rc; +pub enum DimenstionCaseLabel { + String(String), + Sql(Rc), +} + +pub struct DimensionCaseWhenItem { + pub sql: Rc, + pub label: DimenstionCaseLabel, +} + +pub struct DimensionCaseDefinition { + pub items: Vec, + pub else_label: DimenstionCaseLabel, +} + pub struct DimensionSymbol { cube_name: String, name: String, member_sql: Option>, latitude: Option>, longitude: Option>, + case: Option, #[allow(dead_code)] definition: Rc, } @@ -25,6 +42,7 @@ impl DimensionSymbol { member_sql: Option>, latitude: Option>, longitude: Option>, + case: Option, definition: Rc, ) -> Self { Self { @@ -34,6 +52,7 @@ impl DimensionSymbol { latitude, longitude, definition, + case, } } @@ -63,6 +82,10 @@ impl DimensionSymbol { self.longitude.clone() } + pub fn case(&self) -> &Option { + &self.case + } + pub fn member_sql(&self) -> &Option> { &self.member_sql } @@ -98,6 +121,17 @@ impl DimensionSymbol { if let Some(member_sql) = &self.longitude { member_sql.extract_symbol_deps(&mut deps); } + if let Some(case) = &self.case { + for itm in case.items.iter() { + itm.sql.extract_symbol_deps(&mut deps); + if let DimenstionCaseLabel::Sql(sql) = &itm.label { + sql.extract_symbol_deps(&mut deps); + } + } + if let DimenstionCaseLabel::Sql(sql) = &case.else_label { + sql.extract_symbol_deps(&mut deps); + } + } deps } @@ -112,6 +146,17 @@ impl DimensionSymbol { if let Some(member_sql) = &self.longitude { member_sql.extract_symbol_deps_with_path(&mut deps); } + if let Some(case) = &self.case { + for itm in case.items.iter() { + itm.sql.extract_symbol_deps_with_path(&mut deps); + if let DimenstionCaseLabel::Sql(sql) = &itm.label { + sql.extract_symbol_deps_with_path(&mut deps); + } + } + if let DimenstionCaseLabel::Sql(sql) = &case.else_label { + sql.extract_symbol_deps_with_path(&mut deps); + } + } deps } @@ -212,8 +257,37 @@ impl SymbolFactory for DimensionSymbolFactory { } else { (None, None) }; + + let case = if let Some(native_case) = definition.case()? { + let items = native_case + .when()? + .iter() + .map(|item| -> Result<_, CubeError> { + let sql = compiler.compile_sql_call(&cube_name, item.sql()?)?; + let label = match item.label()? { + CaseLabel::String(s) => DimenstionCaseLabel::String(s.clone()), + CaseLabel::MemberSql(sql_struct) => { + let sql = compiler.compile_sql_call(&cube_name, sql_struct.sql()?)?; + DimenstionCaseLabel::Sql(sql) + } + }; + Ok(DimensionCaseWhenItem { sql, label }) + }) + .collect::, _>>()?; + + let else_label = match native_case.else_label()?.label()? { + CaseLabel::String(s) => DimenstionCaseLabel::String(s.clone()), + CaseLabel::MemberSql(sql_struct) => { + let sql = compiler.compile_sql_call(&cube_name, sql_struct.sql()?)?; + DimenstionCaseLabel::Sql(sql) + } + }; + Some(DimensionCaseDefinition { items, else_label }) + } else { + None + }; Ok(MemberSymbol::new_dimension(DimensionSymbol::new( - cube_name, name, sql, latitude, longitude, definition, + cube_name, name, sql, latitude, longitude, case, definition, ))) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs index e827878aee0bb..0b4818df43f01 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs @@ -89,6 +89,17 @@ impl MeasureSymbol { } } + pub fn is_addictive(&self) -> bool { + if self.is_multi_stage() { + false + } else { + match self.measure_type().as_str() { + "sum" | "count" | "countDistinctApprox" | "min" | "max" => true, + _ => false, + } + } + } + pub fn evaluate_sql( &self, visitor: &SqlEvaluatorVisitor, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs index c8eefc7e22ea3..7395515fb445e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs @@ -2,6 +2,7 @@ use super::{ CubeNameSymbol, CubeTableSymbol, DimensionSymbol, MeasureSymbol, MemberExpressionSymbol, TimeDimensionSymbol, }; +use std::fmt::Debug; use std::rc::Rc; pub enum MemberSymbol { @@ -13,6 +14,25 @@ pub enum MemberSymbol { MemberExpression(MemberExpressionSymbol), } +impl Debug for MemberSymbol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Dimension(_) => f.debug_tuple("Dimension").field(&self.full_name()).finish(), + Self::TimeDimension(_) => f + .debug_tuple("TimeDimension") + .field(&self.full_name()) + .finish(), + Self::Measure(_) => f.debug_tuple("Measure").field(&self.full_name()).finish(), + Self::CubeName(_) => f.debug_tuple("CubeName").field(&self.full_name()).finish(), + Self::CubeTable(_) => f.debug_tuple("CubeTable").field(&self.full_name()).finish(), + Self::MemberExpression(_) => f + .debug_tuple("MemberExpression") + .field(&self.full_name()) + .finish(), + } + } +} + impl MemberSymbol { pub fn new_measure(symbol: MeasureSymbol) -> Rc { Rc::new(Self::Measure(symbol)) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/mod.rs index 9a47779d4f47c..c05e9d160b911 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/mod.rs @@ -9,7 +9,10 @@ mod time_dimension_symbol; pub use cube_symbol::{ CubeNameSymbol, CubeNameSymbolFactory, CubeTableSymbol, CubeTableSymbolFactory, }; -pub use dimension_symbol::{DimensionSymbol, DimensionSymbolFactory}; +pub use dimension_symbol::{ + DimensionCaseDefinition, DimensionCaseWhenItem, DimensionSymbol, DimensionSymbolFactory, + DimenstionCaseLabel, +}; pub use measure_symbol::{MeasureSymbol, MeasureSymbolFactory}; pub use member_expression_symbol::MemberExpressionSymbol; pub use member_symbol::MemberSymbol; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/filter.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/filter.rs index 7b12a74ef25ca..3ab58c46e2815 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/filter.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/filter.rs @@ -61,6 +61,22 @@ impl FilterTemplates { ) } + pub fn time_not_in_range_filter( + &self, + column: String, + from_timestamp: String, + to_timestamp: String, + ) -> Result { + self.render.render_template( + &"filters/time_not_in_range_filter", + context! { + column => column, + from_timestamp => from_timestamp, + to_timestamp => to_timestamp, + }, + ) + } + pub fn in_where( &self, column: String, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs index ba4e7c01c1220..edd4e4d927b16 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs @@ -10,6 +10,18 @@ use std::rc::Rc; pub struct PlanSqlTemplates { render: Rc, } +pub const UNDERSCORE_UPPER_BOUND: Boundary = Boundary { + name: "LowerUpper", + condition: |s, _| { + s.get(0) == Some(&"_") + && s.get(1) + .map(|c| c.to_uppercase() != c.to_lowercase() && *c == c.to_uppercase()) + == Some(true) + }, + arg: None, + start: 0, + len: 0, +}; impl PlanSqlTemplates { pub fn new(render: Rc) -> Self { @@ -18,8 +30,12 @@ impl PlanSqlTemplates { pub fn alias_name(name: &str) -> String { let res = name - .from_case(Case::Camel) - .without_boundaries(&[Boundary::LOWER_DIGIT, Boundary::UPPER_DIGIT]) + .with_boundaries(&[ + UNDERSCORE_UPPER_BOUND, + Boundary::LOWER_UPPER, + Boundary::DIGIT_LOWER, + Boundary::ACRONYM, + ]) .to_case(Case::Snake) .replace(".", "__"); res @@ -39,6 +55,10 @@ impl PlanSqlTemplates { ) } + pub fn quote_string(&self, string: &str) -> Result { + Ok(format!("'{}'", string)) + } + pub fn quote_identifier(&self, column_name: &str) -> Result { let quote = self.render.get_template("quotes/identifiers")?; let escape = self.render.get_template("quotes/escape")?; @@ -280,6 +300,18 @@ impl PlanSqlTemplates { .render_template("params/param", context! { param_index => param_index }) } + pub fn case( + &self, + expr: Option, + when_then: Vec<(String, String)>, + else_expr: Option, + ) -> Result { + self.render.render_template( + "expressions/case", + context! { expr => expr, when_then => when_then, else_expr => else_expr }, + ) + } + pub fn scalar_function( &self, scalar_function: String, diff --git a/rust/cubesqlplanner/nativebridge/Cargo.toml b/rust/cubesqlplanner/nativebridge/Cargo.toml index 2111236eb58da..f641657712a28 100644 --- a/rust/cubesqlplanner/nativebridge/Cargo.toml +++ b/rust/cubesqlplanner/nativebridge/Cargo.toml @@ -20,7 +20,7 @@ version = "1.0" version = "1.0" [dependencies.syn] -version = "1.0" +version = "2.0.98" features = ["full"] [dependencies.async-trait] diff --git a/rust/cubesqlplanner/nativebridge/src/lib.rs b/rust/cubesqlplanner/nativebridge/src/lib.rs index fd2dff5ac18d2..cdba883071094 100644 --- a/rust/cubesqlplanner/nativebridge/src/lib.rs +++ b/rust/cubesqlplanner/nativebridge/src/lib.rs @@ -4,10 +4,11 @@ use proc_macro2::Ident; use quote::{format_ident, quote, ToTokens}; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; -use syn::token::Colon2; +use syn::token::PathSep; +use syn::LitStr; use syn::{ parse_macro_input, punctuated::Punctuated, FnArg, Item, Meta, Pat, Path, PathArguments, - PathSegment, ReturnType, TraitItem, TraitItemMethod, Type, + PathSegment, ReturnType, TraitItem, TraitItemFn, Type, }; #[proc_macro_attribute] pub fn native_bridge(args: TokenStream, input: TokenStream) -> proc_macro::TokenStream { @@ -39,6 +40,7 @@ struct NativeMethodParams { pub method_type: NativeMethodType, pub is_optional: bool, pub is_vec: bool, + pub custom_name: Option, } impl Default for NativeMethodParams { @@ -47,6 +49,7 @@ impl Default for NativeMethodParams { method_type: NativeMethodType::Call, is_optional: false, is_vec: false, + custom_name: None, } } } @@ -74,6 +77,7 @@ struct NativeArgumentTyped { struct NativeMethod { ident: Ident, + //custom_js_name: Option args: Vec, typed_args: Vec, output: ReturnType, @@ -90,8 +94,8 @@ impl Parse for NativeService { .items .iter() .filter_map(|item| match item { - TraitItem::Method(method_item) => { - let method_params = Self::parse_method_params(method_item); + TraitItem::Fn(method_item) => { + let method_params = Self::parse_method_params(method_item).unwrap(); let args = method_item.sig.inputs.iter().cloned().collect::>(); let typed_args = Self::parse_method_typed_args(&args).unwrap(); Some(NativeMethod { @@ -150,27 +154,36 @@ impl NativeService { .collect::, _>>() } - fn parse_method_params(method_item: &TraitItemMethod) -> NativeMethodParams { + fn parse_method_params(method_item: &TraitItemFn) -> syn::Result { let mut method_params = NativeMethodParams::default(); if method_item.attrs.len() > 0 { for attr in method_item.attrs.iter() { - let seg = attr.path.segments.last().unwrap(); - match seg.ident.to_string().as_str() { - "optional" => { - method_params.is_optional = true; - } - "field" => { - method_params.method_type = NativeMethodType::Getter; - } - "vec" => { - method_params.is_vec = true; - } - _ => {} + if attr.path().is_ident("nbridge") { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("optional") { + method_params.is_optional = true; + return Ok(()); + } + if meta.path.is_ident("field") { + method_params.method_type = NativeMethodType::Getter; + return Ok(()); + } + if meta.path.is_ident("vec") { + method_params.is_vec = true; + return Ok(()); + } + if meta.path.is_ident("rename") { + method_params.custom_name = + Some(meta.value()?.parse::()?.value()); + } + + Ok(()) + })?; } } } - method_params + Ok(method_params) } fn get_output_for_deserializer( tp: &ReturnType, @@ -204,7 +217,7 @@ impl NativeService { } fn get_deserializer_output_for_result( - segs: &Punctuated, + segs: &Punctuated, optional: bool, vec: bool, expected_type: &str, @@ -529,7 +542,10 @@ impl NativeMethod { .iter() .map(|arg| Self::js_agr_set(&arg.ident, &arg.downcast_type_path)) .collect::>(); - let js_method_name = self.camel_case_name(); + let js_method_name = method_params + .custom_name + .clone() + .unwrap_or_else(|| self.camel_case_name()); let deseralization = Self::deserialization_impl(&output_params, method_params.is_vec);