diff --git a/packages/cubejs-backend-native/js/index.ts b/packages/cubejs-backend-native/js/index.ts index f6c54477f463f..2b865dd59272b 100644 --- a/packages/cubejs-backend-native/js/index.ts +++ b/packages/cubejs-backend-native/js/index.ts @@ -358,8 +358,19 @@ export const execSql = async (instance: SqlInterfaceInstance, sqlQuery: string, export const buildSqlAndParams = (cubeEvaluator: any): String => { const native = loadNative(); + const safeCallFn = (fn: Function, thisArg: any, ...args: any[]) => { + try { + return { + result: fn.apply(thisArg, args), + }; + } catch (e: any) { + return { + error: e.toString(), + }; + } + }; - return native.buildSqlAndParams(cubeEvaluator); + return native.buildSqlAndParams(cubeEvaluator, safeCallFn); }; export type ResultRow = Record; @@ -405,6 +416,12 @@ export const getFinalQueryResultMulti = (transformDataArr: Object[], rows: any[] return native.getFinalQueryResultMulti(transformDataArr, rows, responseData); }; +export const nativeProxy = (nativeResolver: any, resolveFunc: any) => new Proxy({}, { + get(_, prop) { + return resolveFunc(nativeResolver, prop); + } +}); + export interface PyConfiguration { repositoryFactory?: (ctx: unknown) => Promise, logger?: (msg: string, params: Record) => void, diff --git a/packages/cubejs-backend-native/src/node_export.rs b/packages/cubejs-backend-native/src/node_export.rs index 802076279c47d..23308e26a03d9 100644 --- a/packages/cubejs-backend-native/src/node_export.rs +++ b/packages/cubejs-backend-native/src/node_export.rs @@ -471,6 +471,18 @@ fn build_sql_and_params(cx: FunctionContext) -> JsResult { .unwrap()?, )); + let safe_call_fn = neon_context_holder + .with_context(|cx| { + if let Ok(func) = cx.argument::(1) { + Some(func) + } else { + None + } + }) + .unwrap(); + + neon_context_holder.set_safe_call_fn(safe_call_fn).unwrap(); + let context_holder = NativeContextHolder::>>::new( neon_context_holder, ); diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index f452d904655ed..4a95d0c27abbf 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -670,6 +670,14 @@ export class BaseQuery { return res; } + nativeProxy(nativeResolver, resolveFunc) { + return new Proxy({}, { + get(_, prop) { + return resolveFunc(nativeResolver, prop.valueOf()); + } + }); + } + allCubeMembers(path) { const fromPath = this.cubeEvaluator.cubeFromPath(path); diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeSymbols.js b/packages/cubejs-schema-compiler/src/compiler/CubeSymbols.js index 0efdc4dcf45a2..0b1de279091f1 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeSymbols.js +++ b/packages/cubejs-schema-compiler/src/compiler/CubeSymbols.js @@ -645,6 +645,25 @@ export class CubeSymbols { }); } + isNameOfCube(name) { + if (this.symbols[name]) { + return true; + } else { + return false; + } + } + + isNameOfSymbolInCube(cubeName, name) { + const cube = this.symbols[cubeName]; + if (!cube) { + return false; + } + if (cube[name]) { + return true; + } + return false; + } + filterGroupFunctionDep() { return (...filterParamArgs) => ''; } diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts index 86184d058e311..d21ba456b0b06 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts @@ -549,7 +549,7 @@ describe('SQL Generation', () => { }); `); - it('simple join', async () => { + it('simple join 1', async () => { await compiler.compile(); console.log(joinGraph.buildJoin(['visitor_checkins', 'visitors'])); diff --git a/rust/cubenativeutils/Cargo.toml b/rust/cubenativeutils/Cargo.toml index 1e4e18574a8e2..7b581a5a8fba0 100644 --- a/rust/cubenativeutils/Cargo.toml +++ b/rust/cubenativeutils/Cargo.toml @@ -22,4 +22,4 @@ convert_case = "0.6.0" [dependencies.neon] version = "=1" default-features = false -features = ["napi-1", "napi-4", "napi-6", "futures"] +features = ["napi-1", "napi-4", "napi-5", "napi-6", "futures"] diff --git a/rust/cubenativeutils/src/wrappers/context.rs b/rust/cubenativeutils/src/wrappers/context.rs index 9695b30717ec3..5fae6cd1f6603 100644 --- a/rust/cubenativeutils/src/wrappers/context.rs +++ b/rust/cubenativeutils/src/wrappers/context.rs @@ -1,5 +1,6 @@ -use super::{inner_types::InnerTypes, object_handle::NativeObjectHandle}; +use super::{inner_types::InnerTypes, object_handle::NativeObjectHandle, NativeBox}; use cubesql::CubeError; +use std::{any::Any, rc::Rc}; pub trait NativeContext: Clone { fn boolean(&self, v: bool) -> Result; @@ -8,8 +9,30 @@ pub trait NativeContext: Clone { fn undefined(&self) -> Result, CubeError>; fn empty_array(&self) -> Result; fn empty_struct(&self) -> Result; - //fn boxed(&self, value: T) -> impl NativeBox; fn to_string_fn(&self, result: String) -> Result; + + //IMPORTANT NOTE: Using of any native args in callback (as well as any native objects created + //with NativeContextHolder passed to callback) outside of callback will cause error from + //runtime lifetime check + fn function(&self, function: F) -> Result + where + F: Fn( + Rc>, + Vec>, + ) -> Result, CubeError> + + 'static; + + fn boxed( + &self, + object: T, + ) -> Result, CubeError>; + fn global(&self, name: &str) -> Result, CubeError>; +} + +//Top level reference to ContextHolder for using in top level interfaces. Should be downcaster to +//specific context for use +pub trait NativeContextHolderRef { + fn as_any(self: Rc) -> Rc; } #[derive(Clone)] @@ -18,10 +41,10 @@ pub struct NativeContextHolder { } impl NativeContextHolder { - pub fn new(context: IT::Context) -> Self { - Self { context } + pub fn new(context: IT::Context) -> Rc { + Rc::new(Self { context }) } - pub fn context(&self) -> &impl NativeContext { + pub fn context(&self) -> &IT::Context { &self.context } pub fn boolean(&self, v: bool) -> Result { @@ -42,8 +65,54 @@ impl NativeContextHolder { pub fn empty_struct(&self) -> Result { self.context.empty_struct() } - #[allow(dead_code)] pub fn to_string_fn(&self, result: String) -> Result { self.context.to_string_fn(result) } + pub fn function(&self, function: F) -> Result + where + F: Fn( + Rc>, + Vec>, + ) -> Result, CubeError> + + 'static, + { + self.context.function(function) + } + pub fn boxed( + &self, + object: T, + ) -> Result + '_, CubeError> { + self.context.boxed(object) + } + pub fn global(&self, name: &str) -> Result, CubeError> { + self.context.global(name) + } + pub fn as_context_ref(self: &Rc) -> Rc { + self.clone() + } +} + +impl NativeContextHolderRef for NativeContextHolder { + fn as_any(self: Rc) -> Rc { + self.clone() + } +} + +//FIXME For now we don't allow js calls on finalize, so it's only to clean rust resources +pub trait NativeFinalize: Sized { + fn finalize(self) {} +} + +impl NativeFinalize for std::rc::Rc { + fn finalize(self) { + if let Ok(v) = std::rc::Rc::try_unwrap(self) { + v.finalize(); + } + } +} + +impl NativeFinalize for std::cell::RefCell { + fn finalize(self) { + self.into_inner().finalize(); + } } diff --git a/rust/cubenativeutils/src/wrappers/inner_types.rs b/rust/cubenativeutils/src/wrappers/inner_types.rs index 66bfbf136375c..f5697f31a0f7e 100644 --- a/rust/cubenativeutils/src/wrappers/inner_types.rs +++ b/rust/cubenativeutils/src/wrappers/inner_types.rs @@ -1,8 +1,8 @@ use super::{ context::NativeContext, object::{ - NativeArray, NativeBoolean, NativeFunction, NativeNumber, NativeObject, NativeString, - NativeStruct, + NativeArray, NativeBoolean, NativeFunction, NativeNumber, NativeObject, NativeRoot, + NativeString, NativeStruct, }, }; pub trait InnerTypes: Clone + 'static { @@ -14,4 +14,6 @@ pub trait InnerTypes: Clone + 'static { type Function: NativeFunction; type Number: NativeNumber; type Context: NativeContext; + type FunctionIT: InnerTypes; + type Root: NativeRoot; } diff --git a/rust/cubenativeutils/src/wrappers/mod.rs b/rust/cubenativeutils/src/wrappers/mod.rs index 0f983de467005..9755faded4f31 100644 --- a/rust/cubenativeutils/src/wrappers/mod.rs +++ b/rust/cubenativeutils/src/wrappers/mod.rs @@ -3,11 +3,13 @@ pub mod inner_types; pub mod neon; pub mod object; pub mod object_handle; +pub mod root_holder; pub mod serializer; -pub use context::NativeContextHolder; +pub use context::{NativeContextHolder, NativeContextHolderRef}; pub use object::{ - NativeArray, NativeBoolean, NativeFunction, NativeNumber, NativeString, NativeStruct, - NativeType, + NativeArray, NativeBoolean, NativeBox, NativeFunction, NativeNumber, NativeRoot, NativeString, + NativeStruct, NativeType, }; pub use object_handle::NativeObjectHandle; +pub use root_holder::{RootHolder, Rootable}; diff --git a/rust/cubenativeutils/src/wrappers/neon/context.rs b/rust/cubenativeutils/src/wrappers/neon/context.rs index cc5139110f393..45120ba630e33 100644 --- a/rust/cubenativeutils/src/wrappers/neon/context.rs +++ b/rust/cubenativeutils/src/wrappers/neon/context.rs @@ -1,12 +1,15 @@ +//use super::object::NeonObject; use super::{ inner_types::NeonInnerTypes, object::{ base_types::*, neon_array::NeonArray, neon_function::NeonFunction, neon_struct::NeonStruct, - NeonObject, + NeonObject, NeonTypeHandle, }, }; use crate::wrappers::{ - context::NativeContext, object::NativeObject, object_handle::NativeObjectHandle, + context::{NativeContext, NativeContextHolder, NativeFinalize}, + object::{NativeBox, NativeObject}, + object_handle::NativeObjectHandle, }; use cubesql::CubeError; use neon::prelude::*; @@ -29,13 +32,81 @@ impl<'cx> NoenContextLifetimeExpand<'cx> for FunctionContext<'cx> { } } +pub struct SafeCallFn<'a> { + safe_fn: &'a Option>, +} + +impl<'a> SafeCallFn<'a> { + pub fn new(safe_fn: &'a Option>) -> Self { + Self { safe_fn } + } + + pub fn safe_call, T: Value>( + &self, + cx: &mut C, + func: &Handle<'static, JsFunction>, + this: Handle<'static, T>, + mut args: Vec>, + ) -> Result, CubeError> { + if let Some(safe_fn) = self.safe_fn { + args.insert(0, this.upcast()); + + args.insert(0, func.upcast()); + + let res = safe_fn + .call(cx, this, args) + .map_err(|_| CubeError::internal(format!("Failed to call safe function")))?; + let res = res.downcast::(cx).map_err(|_| { + CubeError::internal(format!("Result of safe function call should be object")) + })?; + let result_field = res.get_value(cx, "result").map_err(|_| { + CubeError::internal(format!( + "Failed wile get `result` field of safe call function result" + )) + })?; + let err_field = res.get_value(cx, "error").map_err(|_| { + CubeError::internal(format!( + "Failed wile get `error` field of safe call function result" + )) + })?; + if !err_field.is_a::(cx) { + let error_string = err_field.downcast::(cx).map_err(|_| { + CubeError::internal(format!( + "Error in safe call function result should be string" + )) + })?; + Err(CubeError::internal(error_string.value(cx))) + } else if !result_field.is_a::(cx) { + Ok(result_field) + } else { + Err(CubeError::internal(format!( + "Safe call function should return object with result or error field" + ))) + } + } else { + let res = func + .call(cx, this, args) + .map_err(|_| CubeError::internal(format!("Failed to call function")))?; + Ok(res) + } + } +} + pub struct ContextWrapper> { cx: C, + safe_call_fn: Option>, } impl> ContextWrapper { pub fn new(cx: C) -> Rc> { - Rc::new(RefCell::new(Self { cx })) + Rc::new(RefCell::new(Self { + cx, + safe_call_fn: None, + })) + } + + pub fn set_safe_call_fn(&mut self, fn_handle: Option>) { + self.safe_call_fn = fn_handle; } pub fn with_context(&mut self, f: F) -> T @@ -45,6 +116,14 @@ impl> ContextWrapper { f(&mut self.cx) } + pub fn with_context_and_safe_fn(&mut self, f: F) -> T + where + F: FnOnce(&mut C, SafeCallFn) -> T, + { + let safe_call_fn = SafeCallFn::new(&self.safe_call_fn); + f(&mut self.cx, safe_call_fn) + } + pub fn get_context(&mut self) -> &mut C { &mut self.cx } @@ -116,6 +195,50 @@ impl> ContextHolder { ))) } } + + pub fn with_context_and_safe_fn(&self, f: F) -> Result + where + F: FnOnce(&mut C, SafeCallFn) -> T, + { + if let Some(context) = self.context.upgrade() { + let mut cx = context.borrow_mut(); + let res = cx.with_context_and_safe_fn(f); + Ok(res) + } else { + Err(CubeError::internal(format!( + "Call to neon context outside of its lifetime" + ))) + } + } + + pub fn set_safe_call_fn( + &self, + f: Option>, + ) -> Result<(), CubeError> { + if let Some(context) = self.context.upgrade() { + let mut cx = context.borrow_mut(); + cx.set_safe_call_fn(f); + Ok(()) + } else { + Err(CubeError::internal(format!( + "Call to neon context outside of its lifetime" + ))) + } + } + + pub fn throw_if_cube_error(&self, e: Result) -> NeonResult { + match e { + Ok(r) => Ok(r), + Err(e) => self + .with_context(|cx| cx.throw_error(&e.to_string())) + .unwrap(), //We panic here becouse of this is situation then we try to throw error + //on context that is not longer alive + } + } + + pub fn is_alive(&self) -> bool { + self.context.upgrade().is_some() + } } impl + 'static> NativeContext> for ContextHolder { @@ -170,6 +293,84 @@ impl + 'static> NativeContext> for Context ); obj.into_function() } + + fn function(&self, func: F) -> Result, CubeError> + where + F: Fn( + Rc>>>, + Vec>>>, + ) + -> Result>>, CubeError> + + 'static, + { + let obj = NeonObject::new( + self.clone(), + self.with_context(|cx| { + JsFunction::new(cx, move |fun_cx| { + neon_run_with_guarded_lifetime(fun_cx, |neon_context_holder| { + let neon_args = neon_context_holder.with_context(|fn_cx| { + let mut res = Vec::new(); + + for i in 0..fn_cx.len() { + if let Ok(arg) = fn_cx.argument::(i) { + res.push(arg); + } else { + res.push(fn_cx.null().upcast()); + } + } + res + }); + let neon_args = neon_context_holder.throw_if_cube_error(neon_args)?; + + let args = neon_args + .into_iter() + .map(|arg| { + let res = NativeObjectHandle::new(NeonObject::new( + neon_context_holder.clone(), + arg, + )); + res + }) + .collect::>(); + + let context_holder = NativeContextHolder::new(neon_context_holder.clone()); + + let res = + neon_context_holder.throw_if_cube_error(func(context_holder, args))?; + + let result: NeonObject> = res.into_object(); + let result = result.into_object(); + Ok(result) + }) + }) + .unwrap() + .upcast() + })?, + ); + obj.into_function() + } + + fn boxed( + &self, + object: T, + ) -> Result, T>, CubeError> { + let obj = NeonBoxWrapper::new(object); + let obj = self.with_context(|cx| cx.boxed(obj))?; + let result = NeonBox::new(NeonTypeHandle::new(self.clone(), obj)); + Ok(result) + } + + fn global(&self, name: &str) -> Result>, CubeError> { + let obj = NeonObject::new( + self.clone(), + self.with_context(|cx| { + cx.global(name).map_err(|_| { + CubeError::internal(format!("Global JS object {} not found", name)) + }) + })??, + ); + Ok(NativeObjectHandle::new(obj)) + } } impl> Clone for ContextHolder { diff --git a/rust/cubenativeutils/src/wrappers/neon/inner_types.rs b/rust/cubenativeutils/src/wrappers/neon/inner_types.rs index 5da5dca397de4..74c5b9c09f774 100644 --- a/rust/cubenativeutils/src/wrappers/neon/inner_types.rs +++ b/rust/cubenativeutils/src/wrappers/neon/inner_types.rs @@ -30,4 +30,6 @@ impl + 'static> InnerTypes for NeonInnerTypes { type Boolean = NeonBoolean; type Function = NeonFunction; type Number = NeonNumber; + type FunctionIT = NeonInnerTypes>; + type Root = NeonRoot; } diff --git a/rust/cubenativeutils/src/wrappers/neon/object/base_types.rs b/rust/cubenativeutils/src/wrappers/neon/object/base_types.rs index 54d1a7de02285..0d876dac91d1d 100644 --- a/rust/cubenativeutils/src/wrappers/neon/object/base_types.rs +++ b/rust/cubenativeutils/src/wrappers/neon/object/base_types.rs @@ -1,11 +1,19 @@ use super::{NeonObject, NeonTypeHandle}; -use crate::wrappers::neon::inner_types::NeonInnerTypes; +use crate::wrappers::NativeObjectHandle; +use crate::wrappers::{ + context::NativeFinalize, + inner_types::InnerTypes, + neon::{context::ContextHolder, inner_types::NeonInnerTypes}, +}; +use std::cell::RefCell; use std::marker::PhantomData; +use std::ops::DerefMut; -use crate::wrappers::object::{NativeBoolean, NativeBox, NativeNumber, NativeString, NativeType}; +use crate::wrappers::object::{ + NativeBoolean, NativeBox, NativeNumber, NativeRoot, NativeString, NativeType, +}; use cubesql::CubeError; use neon::prelude::*; -use std::ops::Deref; pub struct NeonString> { object: NeonTypeHandle, @@ -76,13 +84,29 @@ impl + 'static> NativeBoolean> for NeonBoo } } -pub struct NeonBox, T: 'static> { - object: NeonTypeHandle>, +pub struct NeonBoxWrapper { + pub obj: T, +} + +impl NeonBoxWrapper { + pub fn new(obj: T) -> Self { + Self { obj } + } +} + +impl Finalize for NeonBoxWrapper { + fn finalize<'a, C: Context<'a>>(self, _: &mut C) { + self.obj.finalize(); + } +} + +pub struct NeonBox, T: 'static + NativeFinalize> { + object: NeonTypeHandle>>, _marker: PhantomData, } -impl, T: 'static> NeonBox { - pub fn new(object: NeonTypeHandle>) -> Self { +impl, T: 'static + NativeFinalize> NeonBox { + pub fn new(object: NeonTypeHandle>>) -> Self { Self { object, _marker: PhantomData::default(), @@ -90,14 +114,59 @@ impl, T: 'static> NeonBox { } } -impl + 'static, T: 'static> NativeType> for NeonBox { +impl + 'static, T: 'static + NativeFinalize> NativeType> + for NeonBox +{ fn into_object(self) -> NeonObject { self.object.upcast() } } -impl + 'static, T: 'static> NativeBox, T> for NeonBox { +impl + 'static, T: 'static + NativeFinalize> NativeBox, T> + for NeonBox +{ fn deref_value(&self) -> &T { - self.object.get_object_ref().deref() + &self.object.get_object_ref().obj + } +} + +pub struct NeonRoot { + object: RefCell>>, +} + +impl NeonRoot { + pub fn try_new + 'static>( + context_holder: ContextHolder, + object: NeonTypeHandle, + ) -> Result { + let obj = context_holder.with_context(|cx| object.get_object().root(cx))?; + Ok(Self { + object: RefCell::new(Some(obj)), + }) + } +} + +impl + 'static> NativeRoot> for NeonRoot { + fn to_inner( + &self, + cx: &ContextHolder, + ) -> Result>, CubeError> { + if let Some(object) = &self.object.borrow().as_ref() { + let res = cx.with_context(|cx| object.to_inner(cx))?; + let res = NeonObject::new(cx.clone(), res.upcast()); + Ok(NativeObjectHandle::new(res)) + } else { + Err(CubeError::internal("Root object is dropped".to_string())) + } + } + + fn drop_root(&self, cx: &ContextHolder) -> Result<(), CubeError> { + let mut object = self.object.borrow_mut(); + let object = std::mem::take(object.deref_mut()); + if let Some(object) = object { + cx.with_context(|cx| object.drop(cx)) + } else { + Ok(()) + } } } diff --git a/rust/cubenativeutils/src/wrappers/neon/object/mod.rs b/rust/cubenativeutils/src/wrappers/neon/object/mod.rs index 54b28d10684b5..e12864afbd29d 100644 --- a/rust/cubenativeutils/src/wrappers/neon/object/mod.rs +++ b/rust/cubenativeutils/src/wrappers/neon/object/mod.rs @@ -4,13 +4,17 @@ pub mod neon_function; pub mod neon_struct; use self::{ - base_types::{NeonBoolean, NeonNumber, NeonString}, + base_types::{NeonBoolean, NeonBox, NeonBoxWrapper, NeonNumber, NeonRoot, NeonString}, neon_array::NeonArray, neon_function::NeonFunction, neon_struct::NeonStruct, }; use super::inner_types::NeonInnerTypes; -use crate::wrappers::{neon::context::ContextHolder, object::NativeObject}; +use crate::wrappers::{ + context::NativeFinalize, + neon::context::{ContextHolder, SafeCallFn}, + object::{NativeBox, NativeObject}, +}; use cubesql::CubeError; use neon::prelude::*; @@ -64,6 +68,30 @@ impl + 'static, V: Value + 'static> NeonTypeHandle { })? } + pub fn map_neon_object_with_safe_call_fn(&self, f: F) -> Result + where + F: FnOnce(&mut C, &Handle<'static, V>, SafeCallFn) -> T, + { + self.context + .with_context_and_safe_fn(|cx, safe_call_fn| f(cx, &self.object, safe_call_fn)) + } + + pub fn map_downcast_neon_object_with_safe_call_fn( + &self, + f: F, + ) -> Result + where + F: FnOnce(&mut C, &Handle<'static, JT>, SafeCallFn) -> Result, + { + self.context.with_context_and_safe_fn(|cx, safe_call_fn| { + let obj = self + .object + .downcast::(cx) + .map_err(|_| CubeError::internal("Downcast error".to_string()))?; + f(cx, &obj, safe_call_fn) + })? + } + pub fn is_a(&self) -> Result { self.context.with_context(|cx| self.object.is_a::(cx)) } @@ -118,9 +146,9 @@ impl + 'static> NeonObject { msg: &str, ) -> Result, CubeError> { let obj = self.context.with_context(|cx| { - self.object - .downcast::(cx) - .map_err(|_| CubeError::internal(msg.to_string())) + self.object.downcast::(cx).map_err(|e| { + CubeError::internal(format!("{:?} {} {:?}", self.object, msg.to_string(), e)) + }) })??; Ok(NeonTypeHandle::new(self.context.clone(), obj)) } @@ -136,16 +164,22 @@ impl + 'static> NativeObject> for NeonObje Ok(NeonStruct::new(obj)) } fn into_function(self) -> Result, CubeError> { - let obj = self.downcast_with_err_msg::("NeonObject is not the JsArray")?; + let obj = self.downcast_with_err_msg::("NeonObject is not the JsFunction")?; Ok(NeonFunction::new(obj)) } + fn into_root(self) -> Result { + let obj = self.downcast_with_err_msg::( + "Only JsObject and its child types can be converted to root", + )?; + NeonRoot::try_new(self.context.clone(), obj) + } fn into_array(self) -> Result, CubeError> { let obj = self.downcast_with_err_msg::("NeonObject is not the JsArray")?; Ok(NeonArray::new(obj)) } fn into_string(self) -> Result, CubeError> { - let obj = self.downcast_with_err_msg::("NeonObject is not the JsString")?; - Ok(NeonString::new(obj)) + let obj = self.downcast_with_err_msg::("NeonObject is not the JsString"); + Ok(NeonString::new(obj?)) } fn into_number(self) -> Result, CubeError> { let obj = self.downcast_with_err_msg::("NeonObject is not the JsNumber")?; @@ -155,6 +189,13 @@ impl + 'static> NativeObject> for NeonObje let obj = self.downcast_with_err_msg::("NeonObject is not the JsBoolean")?; Ok(NeonBoolean::new(obj)) } + fn into_boxed( + self, + ) -> Result, T>, CubeError> { + let obj = + self.downcast_with_err_msg::>>("NeonObject is not the JsBox")?; + Ok(NeonBox::new(obj)) + } fn is_null(&self) -> Result { self.is_a::() diff --git a/rust/cubenativeutils/src/wrappers/neon/object/neon_function.rs b/rust/cubenativeutils/src/wrappers/neon/object/neon_function.rs index 192db172e1c8a..2310c48f96aae 100644 --- a/rust/cubenativeutils/src/wrappers/neon/object/neon_function.rs +++ b/rust/cubenativeutils/src/wrappers/neon/object/neon_function.rs @@ -1,4 +1,4 @@ -use super::{NeonObject, NeonTypeHandle}; +use super::{NeonObject, NeonStruct, NeonTypeHandle}; use crate::wrappers::{ neon::inner_types::NeonInnerTypes, object::{NativeFunction, NativeType}, @@ -27,20 +27,37 @@ impl + 'static> NativeType> for NeonFuncti } impl + 'static> NativeFunction> for NeonFunction { - fn call( + fn call_as_construct( &self, args: Vec>>, - ) -> Result>, CubeError> { + ) -> Result, CubeError> { let neon_args = args .into_iter() .map(|arg| -> Result<_, CubeError> { Ok(arg.into_object().get_object()) }) .collect::, _>>()?; let neon_reuslt = self.object.map_neon_object(|cx, neon_object| { - let null = cx.null(); neon_object - .call(cx, null, neon_args) - .map_err(|_| CubeError::internal(format!("Failed to call function "))) + .construct(cx, neon_args) + .map_err(|_| CubeError::internal(format!("Failed to call function as construct"))) })??; + let obj = NeonTypeHandle::new(self.object.context.clone(), neon_reuslt); + Ok(NeonStruct::new(obj)) + } + + fn call( + &self, + args: Vec>>, + ) -> Result>, CubeError> { + let neon_args = args + .into_iter() + .map(|arg| -> Result<_, CubeError> { Ok(arg.into_object().get_object()) }) + .collect::, _>>()?; + let neon_reuslt = + self.object + .map_neon_object_with_safe_call_fn(|cx, neon_object, safe_call_fn| { + let null = cx.null(); + safe_call_fn.safe_call(cx, neon_object, null, neon_args) + })??; Ok(NativeObjectHandle::new(NeonObject::new( self.object.context.clone(), neon_reuslt, diff --git a/rust/cubenativeutils/src/wrappers/object.rs b/rust/cubenativeutils/src/wrappers/object.rs index 401a0b35c2651..44f9e3c373a8a 100644 --- a/rust/cubenativeutils/src/wrappers/object.rs +++ b/rust/cubenativeutils/src/wrappers/object.rs @@ -1,4 +1,8 @@ -use super::{inner_types::InnerTypes, object_handle::NativeObjectHandle}; +use super::{ + context::{NativeContext, NativeFinalize}, + inner_types::InnerTypes, + object_handle::NativeObjectHandle, +}; use cubesql::CubeError; pub trait NativeObject: Clone { @@ -10,6 +14,8 @@ pub trait NativeObject: Clone { fn into_number(self) -> Result; fn into_boolean(self) -> Result; fn into_function(self) -> Result; + fn into_root(self) -> Result; + fn into_boxed(self) -> Result, CubeError>; fn is_null(&self) -> Result; fn is_undefined(&self) -> Result; } @@ -40,6 +46,8 @@ pub trait NativeStruct: NativeType { } pub trait NativeFunction: NativeType { + fn call_as_construct(&self, args: Vec>) + -> Result; fn call(&self, args: Vec>) -> Result, CubeError>; fn definition(&self) -> Result; fn args_names(&self) -> Result, CubeError>; @@ -60,3 +68,8 @@ pub trait NativeBoolean: NativeType { pub trait NativeBox: NativeType { fn deref_value(&self) -> &T; } + +pub trait NativeRoot { + fn to_inner(&self, cx: &IT::Context) -> Result, CubeError>; //FIXME we allow to get use root only in the same context type in which it was created. It can lead to problems if we will try to use it in different type of context (In neon case if we crate it in FunctionContext but tryed to use in TaskContext) + fn drop_root(&self, cx: &IT::Context) -> Result<(), CubeError>; +} diff --git a/rust/cubenativeutils/src/wrappers/object_handle.rs b/rust/cubenativeutils/src/wrappers/object_handle.rs index 93429cacbc117..1614436701518 100644 --- a/rust/cubenativeutils/src/wrappers/object_handle.rs +++ b/rust/cubenativeutils/src/wrappers/object_handle.rs @@ -1,4 +1,8 @@ -use super::{inner_types::InnerTypes, object::NativeObject}; +use super::{ + context::NativeFinalize, + inner_types::InnerTypes, + object::{NativeBox, NativeObject, NativeType}, +}; use cubesql::CubeError; #[derive(Clone)] @@ -11,6 +15,10 @@ impl NativeObjectHandle { Self { object } } + pub fn new_from_type>(object: T) -> Self { + Self::new(object.into_object()) + } + pub fn object_ref(&self) -> &IT::Object { &self.object } @@ -37,6 +45,14 @@ impl NativeObjectHandle { pub fn into_boolean(self) -> Result { self.object.into_boolean() } + pub fn into_root(self) -> Result { + self.object.into_root() + } + pub fn into_boxed( + self, + ) -> Result, CubeError> { + self.object.into_boxed() + } pub fn to_struct(&self) -> Result { self.object.clone().into_struct() } @@ -55,6 +71,14 @@ impl NativeObjectHandle { pub fn to_boolean(&self) -> Result { self.object.clone().into_boolean() } + pub fn to_root(&self) -> Result { + self.object.clone().into_root() + } + pub fn to_boxed( + &self, + ) -> Result, CubeError> { + self.object.clone().into_boxed() + } pub fn is_null(&self) -> Result { self.object.is_null() } diff --git a/rust/cubenativeutils/src/wrappers/object_handler.rs b/rust/cubenativeutils/src/wrappers/object_handler.rs deleted file mode 100644 index 65298226ef2d4..0000000000000 --- a/rust/cubenativeutils/src/wrappers/object_handler.rs +++ /dev/null @@ -1,83 +0,0 @@ -use super::context::NativeContextHolder; -use super::object::{ - NativeArray, NativeBoolean, NativeNumber, NativeObject, NativeString, NativeStruct, -}; -use cubesql::CubeError; - -pub struct NativeObjectHandler { - object: Box, -} - -impl NativeObjectHandler { - pub fn new(object: Box) -> Self { - Self { object } - } - - pub fn object_ref(&self) -> &Box { - &self.object - } - - pub fn downcast_object_ref(&self) -> Option<&T> { - self.object.as_any().downcast_ref() - } - - pub fn into_object(self) -> Box { - self.object - } - - pub fn downcast_object(self) -> Result, CubeError> { - self.object - .into_any() - .downcast::() - .map_err(|_| CubeError::internal("Unable to downcast object".to_string())) - } - - pub fn into_struct(self) -> Result, CubeError> { - self.object.into_struct() - } - pub fn into_array(self) -> Result, CubeError> { - self.object.into_array() - } - pub fn into_string(self) -> Result, CubeError> { - self.object.into_string() - } - pub fn into_number(self) -> Result, CubeError> { - self.object.into_number() - } - pub fn into_boolean(self) -> Result, CubeError> { - self.object.into_boolean() - } - pub fn to_struct(&self) -> Result, CubeError> { - self.object.boxed_clone().into_struct() - } - pub fn to_array(&self) -> Result, CubeError> { - self.object.boxed_clone().into_array() - } - pub fn to_string(&self) -> Result, CubeError> { - self.object.boxed_clone().into_string() - } - pub fn to_number(&self) -> Result, CubeError> { - self.object.boxed_clone().into_number() - } - pub fn to_boolean(&self) -> Result, CubeError> { - self.object.boxed_clone().into_boolean() - } - pub fn is_null(&self) -> bool { - self.object.is_null() - } - pub fn is_undefined(&self) -> bool { - self.object.is_undefined() - } - - pub fn get_context(&self) -> Result { - self.object.get_context() - } -} - -impl Clone for NativeObjectHandler { - fn clone(&self) -> Self { - Self { - object: self.object.boxed_clone(), - } - } -} diff --git a/rust/cubenativeutils/src/wrappers/root_holder.rs b/rust/cubenativeutils/src/wrappers/root_holder.rs new file mode 100644 index 0000000000000..f9dfe3a947a7d --- /dev/null +++ b/rust/cubenativeutils/src/wrappers/root_holder.rs @@ -0,0 +1,20 @@ +use super::NativeContextHolderRef; +use crate::CubeError; +use std::any::Any; +use std::rc::Rc; + +pub trait RootHolder { + fn as_any(self: Rc) -> Rc; + fn drop( + self: Rc, + context_holder_ref: Rc, + ) -> Result<(), CubeError>; + fn to_inner( + self: Rc, + context_holder_ref: Rc, + ) -> Result, CubeError>; +} + +pub trait Rootable { + fn to_root(self: Rc) -> Result>, CubeError>; +} diff --git a/rust/cubenativeutils/src/wrappers/serializer/serialize.rs b/rust/cubenativeutils/src/wrappers/serializer/serialize.rs index e08ef8284053f..2a6b7e04a5019 100644 --- a/rust/cubenativeutils/src/wrappers/serializer/serialize.rs +++ b/rust/cubenativeutils/src/wrappers/serializer/serialize.rs @@ -4,18 +4,19 @@ use crate::{ CubeError, }; use serde::Serialize; +use std::rc::Rc; pub trait NativeSerialize { fn to_native( &self, - context: NativeContextHolder, + context: Rc>, ) -> Result, CubeError>; } impl NativeSerialize for T { fn to_native( &self, - context: NativeContextHolder, + context: Rc>, ) -> Result, CubeError> { NativeSerdeSerializer::serialize(self, context) .map_err(|e| CubeError::internal(format!("Serialize error: {}", e))) @@ -25,7 +26,7 @@ impl NativeSerialize for T { impl NativeSerialize for NativeObjectHandle { fn to_native( &self, - _context: NativeContextHolder, + _context: Rc>, ) -> Result, CubeError> { Ok(self.clone()) } diff --git a/rust/cubenativeutils/src/wrappers/serializer/serializer.rs b/rust/cubenativeutils/src/wrappers/serializer/serializer.rs index 5750bc656a364..999855fb0d1cf 100644 --- a/rust/cubenativeutils/src/wrappers/serializer/serializer.rs +++ b/rust/cubenativeutils/src/wrappers/serializer/serializer.rs @@ -4,35 +4,36 @@ use crate::wrappers::{ NativeStruct, NativeType, }; use serde::{ser, Serialize}; +use std::rc::Rc; pub struct NativeSerdeSerializer { - context: NativeContextHolder, + context: Rc>, } pub struct NativeSeqSerializer { - context: NativeContextHolder, + context: Rc>, last_id: u32, seq: IT::Array, } pub struct NativeMapSerializer { - context: NativeContextHolder, + context: Rc>, obj: IT::Struct, } pub struct NativeTupleSerializer { - _context: NativeContextHolder, + _context: Rc>, tuple: IT::Array, } impl NativeSerdeSerializer { - pub fn new(context: NativeContextHolder) -> Self { + pub fn new(context: Rc>) -> Self { Self { context } } pub fn serialize( value: &T, - context: NativeContextHolder, + context: Rc>, ) -> Result, NativeObjSerializerError> where T: Serialize, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs index 0ccc1567be747..e0409105bc230 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs @@ -1,6 +1,10 @@ use super::filter_group::{FilterGroup, NativeFilterGroup}; use super::filter_params::{FilterParams, NativeFilterParams}; use super::member_sql::{MemberSql, NativeMemberSql}; +use super::proxy::{ + NativeProxy, NativeProxyHandler, NativeProxyHandlerFunction, Proxy, ProxyHandler, + ProxyHandlerFunction, +}; use super::security_context::{NativeSecurityContext, SecurityContext}; use super::sql_templates_render::{NativeSqlTemplatesRender, SqlTemplatesRender}; use super::sql_utils::{NativeSqlUtils, SqlUtils}; @@ -47,6 +51,11 @@ pub trait BaseTools { ) -> Result>, CubeError>; fn get_allocated_params(&self) -> Result, CubeError>; fn all_cube_members(&self, path: String) -> Result, CubeError>; + fn native_proxy( + &self, + handler: Rc, + resolve_funcion: Rc, + ) -> Result, CubeError>; //===== TODO Move to templates fn hll_init(&self, sql: String) -> Result; fn hll_merge(&self, sql: String) -> Result; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs index 7c02a6f7a0afd..cf3ff7f8c0b7d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs @@ -45,4 +45,7 @@ pub trait CubeEvaluator { cube_name: String, sql: Rc, ) -> Result, CubeError>; + fn is_name_of_cube(&self, name: String) -> Result; + fn is_name_of_symbol_in_cube(&self, cube_name: String, name: String) + -> Result; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_sql.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_sql.rs index 993fd76dc81d3..392e61fe57724 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_sql.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_sql.rs @@ -1,28 +1,32 @@ use super::{ filter_group::{FilterGroup, NativeFilterGroup}, filter_params::{FilterParams, NativeFilterParams}, + proxy::{CubeDepsCollectorProxyHandler, FilterParamsCollectorProxyHandler}, + return_string_fn::ReturnStringFn, security_context::{NativeSecurityContext, SecurityContext}, sql_utils::{NativeSqlUtils, SqlUtils}, }; use cubenativeutils::wrappers::inner_types::InnerTypes; -use cubenativeutils::wrappers::object::{NativeFunction, NativeStruct, NativeType}; +use cubenativeutils::wrappers::object::{NativeFunction, NativeRoot, NativeStruct, NativeType}; use cubenativeutils::wrappers::serializer::{ NativeDeserialize, NativeDeserializer, NativeSerialize, }; -use cubenativeutils::wrappers::NativeContextHolder; use cubenativeutils::wrappers::NativeObjectHandle; +use cubenativeutils::wrappers::{NativeContextHolder, NativeContextHolderRef}; +use cubenativeutils::wrappers::{RootHolder, Rootable}; use cubenativeutils::CubeError; use std::any::Any; use std::collections::hash_map::HashMap; use std::rc::Rc; -#[derive(Default)] +#[derive(Default, Clone)] pub struct MemberSqlStruct { pub sql_fn: Option, pub to_string_fn: Option, pub properties: HashMap, } +#[derive(Clone)] pub enum ContextSymbolArg { SecurityContext(Rc), SqlUtils(Rc), @@ -30,48 +34,30 @@ pub enum ContextSymbolArg { FilterGroup(Rc), } +#[derive(Clone)] pub enum MemberSqlArg { String(String), Struct(MemberSqlStruct), ContextSymbol(ContextSymbolArg), } +#[derive(Clone)] +pub enum MemberSqlArgForResolve { + String(String), + CubeProxy(Rc), + FilterParamsProxy(Rc), + FilterGroupMock(), + ContextSymbol(ContextSymbolArg), +} + pub trait MemberSql { fn call(&self, args: Vec) -> Result; + fn deps_resolve(&self, args: Vec) -> Result; fn args_names(&self) -> &Vec; fn need_deps_resolve(&self) -> bool; fn as_any(self: Rc) -> Rc; } -pub struct CustomMemberSql { - sql: String, - args_names: Vec, -} - -impl CustomMemberSql { - pub fn new(sql: String) -> Rc { - Rc::new(Self { - sql, - args_names: vec![], - }) - } -} - -impl MemberSql for CustomMemberSql { - fn call(&self, _args: Vec) -> Result { - Ok(self.sql.clone()) - } - fn args_names(&self) -> &Vec { - &self.args_names - } - fn need_deps_resolve(&self) -> bool { - false - } - fn as_any(self: Rc) -> Rc { - self.clone() - } -} - pub struct NativeMemberSql { native_object: NativeObjectHandle, args_names: Vec, @@ -80,7 +66,7 @@ pub struct NativeMemberSql { impl NativeSerialize for MemberSqlStruct { fn to_native( &self, - context: NativeContextHolder, + context: Rc>, ) -> Result, CubeError> { let res = context.empty_struct()?; for (k, v) in self.properties.iter() { @@ -102,40 +88,66 @@ impl NativeSerialize for MemberSqlStruct { } } +impl NativeSerialize for ContextSymbolArg { + fn to_native( + &self, + context_holder: Rc>, + ) -> Result, CubeError> { + match self { + Self::SecurityContext(context) => context + .clone() + .as_any() + .downcast::>() + .unwrap() + .to_native(context_holder.clone()), + Self::SqlUtils(context) => context + .clone() + .as_any() + .downcast::>() + .unwrap() + .to_native(context_holder.clone()), + Self::FilterParams(params) => params + .clone() + .as_any() + .downcast::>() + .unwrap() + .to_native(context_holder.clone()), + Self::FilterGroup(group) => group + .clone() + .as_any() + .downcast::>() + .unwrap() + .to_native(context_holder.clone()), + } + } +} impl NativeSerialize for MemberSqlArg { fn to_native( &self, - context_holder: NativeContextHolder, + context_holder: Rc>, ) -> Result, CubeError> { let res = match self { - MemberSqlArg::String(s) => s.to_native(context_holder.clone()), - MemberSqlArg::Struct(s) => s.to_native(context_holder.clone()), - MemberSqlArg::ContextSymbol(symbol) => match symbol { - ContextSymbolArg::SecurityContext(context) => context - .clone() - .as_any() - .downcast::>() - .unwrap() - .to_native(context_holder.clone()), - ContextSymbolArg::SqlUtils(context) => context - .clone() - .as_any() - .downcast::>() - .unwrap() - .to_native(context_holder.clone()), - ContextSymbolArg::FilterParams(params) => params - .clone() - .as_any() - .downcast::>() - .unwrap() - .to_native(context_holder.clone()), - ContextSymbolArg::FilterGroup(group) => group - .clone() - .as_any() - .downcast::>() - .unwrap() - .to_native(context_holder.clone()), - }, + Self::String(s) => s.to_native(context_holder.clone()), + Self::Struct(s) => s.to_native(context_holder.clone()), + Self::ContextSymbol(s) => s.to_native(context_holder.clone()), + }?; + Ok(NativeObjectHandle::new(res.into_object())) + } +} + +impl NativeSerialize for MemberSqlArgForResolve { + fn to_native( + &self, + context_holder: Rc>, + ) -> Result, CubeError> { + let res = match self { + Self::String(s) => s.to_native(context_holder.clone()), + Self::CubeProxy(proxy) => proxy.to_native(context_holder.clone()), + Self::FilterParamsProxy(proxy) => proxy.to_native(context_holder.clone()), + Self::FilterGroupMock() => { + ReturnStringFn::new(format!("")).to_native(context_holder.clone()) + } + Self::ContextSymbol(s) => s.to_native(context_holder.clone()), }?; Ok(NativeObjectHandle::new(res.into_object())) } @@ -149,13 +161,8 @@ impl NativeMemberSql { args_names, }) } -} -impl MemberSql for NativeMemberSql { - fn as_any(self: Rc) -> Rc { - self.clone() - } - fn call(&self, args: Vec) -> Result { + fn call_impl>(&self, args: Vec) -> Result { if args.len() != self.args_names.len() { return Err(CubeError::internal(format!( "Invalid arguments count for MemberSql call: expected {}, got {}", @@ -172,6 +179,20 @@ impl MemberSql for NativeMemberSql { let res = self.native_object.to_function()?.call(native_args)?; NativeDeserializer::deserialize::(res) } +} + +impl MemberSql for NativeMemberSql { + fn as_any(self: Rc) -> Rc { + self.clone() + } + fn call(&self, args: Vec) -> Result { + self.call_impl(args) + } + + fn deps_resolve(&self, args: Vec) -> Result { + self.call_impl(args) + } + fn args_names(&self) -> &Vec { &self.args_names } @@ -183,7 +204,7 @@ impl MemberSql for NativeMemberSql { impl NativeSerialize for NativeMemberSql { fn to_native( &self, - _context: NativeContextHolder, + _context: Rc>, ) -> Result, CubeError> { Ok(self.native_object.clone()) } @@ -193,3 +214,58 @@ impl NativeDeserialize for NativeMemberSql { Self::try_new(native_object) } } + +pub struct NativeRootMemberSql { + root: IT::Root, +} + +impl NativeRootMemberSql { + pub fn new(root: IT::Root) -> Rc { + Rc::new(Self { root }) + } +} + +impl RootHolder for NativeRootMemberSql { + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn drop( + self: Rc, + context_holder_ref: Rc, + ) -> Result<(), CubeError> { + let context_holder = context_holder_ref + .as_any() + .downcast::>() + .map_err(|_| { + CubeError::internal(format!( + "Wrong context holder type for obtain NativeMemberSql from root holder" + )) + })?; + self.root.drop_root(context_holder.context()) + } + + fn to_inner( + self: Rc, + context_holder_ref: Rc, + ) -> Result, CubeError> { + let context_holder = context_holder_ref + .as_any() + .downcast::>() + .map_err(|_| { + CubeError::internal(format!( + "Wrong context holder type for obtain NativeMemberSql from root holder" + )) + })?; + let result = self.root.to_inner(context_holder.context())?; + let result = Rc::new(NativeMemberSql::from_native(result)?); + Ok(result) + } +} + +impl Rootable for NativeMemberSql { + fn to_root(self: Rc) -> Result>, CubeError> { + let native_root = self.native_object.to_root()?; + Ok(NativeRootMemberSql::::new(native_root)) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs index 6fa12c63c418c..83b9729df88b5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs @@ -18,6 +18,8 @@ pub mod member_expression; pub mod member_order_by; pub mod member_sql; pub mod options_member; +pub mod proxy; +pub mod return_string_fn; pub mod security_context; pub mod sql_templates_render; pub mod sql_utils; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/cube_deps_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/cube_deps_collector.rs new file mode 100644 index 0000000000000..ce22c55f96136 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/cube_deps_collector.rs @@ -0,0 +1,184 @@ +use crate::cube_bridge::base_tools::BaseTools; +use crate::cube_bridge::evaluator::CubeEvaluator; + +use super::{ProxyCollector, ProxyHandlerImpl}; +use cubenativeutils::wrappers::context::NativeFinalize; +use cubenativeutils::wrappers::inner_types::InnerTypes; +use cubenativeutils::wrappers::serializer::NativeSerialize; +use cubenativeutils::wrappers::{ + NativeContextHolder, NativeContextHolderRef, NativeObjectHandle, RootHolder, Rootable, +}; +use cubenativeutils::CubeError; +use std::cell::RefCell; +use std::rc::Rc; + +pub type CubeDepsCollectorProxyHandler = ProxyHandlerImpl; + +pub enum CubeDepsCollectorProp { + Symbol(String), + Cube(Rc), +} + +pub enum CubeDepsProxyRes { + String(String), + ToStringFn(String), + Proxy(Rc), +} + +impl NativeSerialize for CubeDepsProxyRes { + fn to_native( + &self, + context: Rc>, + ) -> Result, CubeError> { + match self { + CubeDepsProxyRes::String(s) => s.to_native(context), + CubeDepsProxyRes::ToStringFn(s) => Ok(NativeObjectHandle::new_from_type( + context.to_string_fn(s.clone())?, + )), + CubeDepsProxyRes::Proxy(proxy) => proxy.to_native(context), + } + } +} + +pub struct CubeDepsCollector { + cube_name: String, + has_sql_fn: bool, + has_to_string_fn: bool, + deps: Vec, + evaluator_root: Rc>, + base_tools_root: Rc>, + context_holder_ref: Rc, +} + +impl CubeDepsCollector { + pub fn try_new( + cube_name: String, + evaluator: Rc, + base_tools: Rc, + context_holder_ref: Rc, + ) -> Result { + let evaluator_root = evaluator.to_root()?; + let base_tools_root = base_tools.to_root()?; + Ok(Self { + cube_name, + has_sql_fn: false, + has_to_string_fn: false, + deps: vec![], + evaluator_root, + base_tools_root, + context_holder_ref, + }) + } + + fn new_with_roots( + cube_name: String, + evaluator_root: Rc>, + base_tools_root: Rc>, + context_holder_ref: Rc, + ) -> Self { + Self { + cube_name, + has_sql_fn: false, + has_to_string_fn: false, + deps: vec![], + evaluator_root, + base_tools_root, + context_holder_ref, + } + } + + pub fn set_has_sql_fn(&mut self) { + self.has_sql_fn = true; + } + + pub fn set_has_to_string_fn(&mut self) { + self.has_to_string_fn = true; + } + + pub fn add_dep(&mut self, dep: CubeDepsCollectorProp) { + self.deps.push(dep); + } + + pub fn has_sql_fn(&self) -> bool { + self.has_sql_fn + } + + pub fn has_to_string_fn(&self) -> bool { + self.has_to_string_fn + } + + pub fn cube_name(&self) -> &String { + &self.cube_name + } + + pub fn deps(&self) -> &Vec { + &self.deps + } +} + +impl NativeFinalize for CubeDepsCollector {} + +impl ProxyCollector for CubeDepsCollector { + type ResultType = CubeDepsProxyRes; + + fn on_get( + &mut self, + property_name: String, + context_holder_ref: Rc, + ) -> Result, CubeError> { + let evaluator = self + .evaluator_root + .clone() + .to_inner(context_holder_ref.clone())?; + let base_tools = self + .base_tools_root + .clone() + .to_inner(context_holder_ref.clone())?; + if property_name == "toString" { + self.has_to_string_fn = true; + return Ok(Some(CubeDepsProxyRes::ToStringFn(format!("")))); + } + if property_name == "valueOf" { + return Ok(None); + } + if property_name == "sql" { + self.has_sql_fn = true; + return Ok(Some(CubeDepsProxyRes::ToStringFn(format!("")))); + } + if evaluator.is_name_of_symbol_in_cube(self.cube_name.clone(), property_name.clone())? { + self.deps + .push(CubeDepsCollectorProp::Symbol(property_name.clone())); + return Ok(Some(CubeDepsProxyRes::String(format!("")))); + } + + if evaluator.is_name_of_cube(property_name.clone())? { + let collector = CubeDepsCollector::new_with_roots( + property_name.clone(), + self.evaluator_root.clone(), + self.base_tools_root.clone(), + self.context_holder_ref.clone(), + ); + let proxy = CubeDepsCollectorProxyHandler::new(collector, base_tools.clone()); + self.deps.push(CubeDepsCollectorProp::Cube(proxy.clone())); + return Ok(Some(CubeDepsProxyRes::Proxy(proxy))); + } + Err(CubeError::user(format!( + "{}.{} cannot be resolved. There's no such member or cube.", + self.cube_name, property_name + ))) + } +} + +impl Drop for CubeDepsCollector { + fn drop(&mut self) { + let _ = self + .evaluator_root + .clone() + .drop(self.context_holder_ref.clone()); + + let _ = self + .base_tools_root + .clone() + .drop(self.context_holder_ref.clone()); + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/filter_params_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/filter_params_collector.rs new file mode 100644 index 0000000000000..ab0bb0dad1206 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/filter_params_collector.rs @@ -0,0 +1,138 @@ +use crate::cube_bridge::base_tools::BaseTools; +use crate::cube_bridge::return_string_fn::ReturnStringFn; + +use super::{ProxyCollector, ProxyHandlerImpl}; +use cubenativeutils::wrappers::context::NativeFinalize; +use cubenativeutils::wrappers::inner_types::InnerTypes; +use cubenativeutils::wrappers::serializer::NativeSerialize; +use cubenativeutils::wrappers::{ + NativeContextHolder, NativeContextHolderRef, NativeObjectHandle, NativeStruct, RootHolder, + Rootable, +}; +use cubenativeutils::CubeError; +use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; +use std::rc::Rc; + +pub type FilterParamsCollectorProxyHandler = ProxyHandlerImpl; +pub type FilterParamsCubeFiltersCollectorProxyHandler = + ProxyHandlerImpl; + +pub struct FilterParamsCollector { + cube_filter_proxies: Vec>, + base_tools_root: Rc>, +} + +pub enum FilterParamsCollectorRes { + CupeFilterProxy(Rc), +} + +impl NativeSerialize for FilterParamsCollectorRes { + fn to_native( + &self, + context: Rc>, + ) -> Result, CubeError> { + match self { + FilterParamsCollectorRes::CupeFilterProxy(proxy) => proxy.to_native(context), + } + } +} + +impl FilterParamsCollector { + pub fn try_new(base_tools: Rc) -> Result { + let base_tools_root = base_tools.to_root()?; + Ok(Self { + cube_filter_proxies: vec![], + base_tools_root, + }) + } + + pub fn collected_result(&self) -> HashMap> { + let mut result = HashMap::new(); + for cb in self.cube_filter_proxies.iter() { + let collector = cb.get_collector(); + let entry: &mut HashSet = + result.entry(collector.cube_name.clone()).or_default(); + for member in collector.members.iter() { + entry.insert(member.clone()); + } + } + println!("!!!! {:?}", result); + result + } +} + +impl NativeFinalize for FilterParamsCollector {} + +impl ProxyCollector for FilterParamsCollector { + type ResultType = FilterParamsCollectorRes; + + fn on_get( + &mut self, + property_name: String, + context_holder_ref: Rc, + ) -> Result, CubeError> { + let base_tools = self + .base_tools_root + .clone() + .to_inner(context_holder_ref.clone())?; + let cube_filters_collector = FilterParamsCubeFiltersCollector::new(property_name.clone()); + let cube_filters_proxy = + FilterParamsCubeFiltersCollectorProxyHandler::new(cube_filters_collector, base_tools); + self.cube_filter_proxies.push(cube_filters_proxy.clone()); + let res = FilterParamsCollectorRes::CupeFilterProxy(cube_filters_proxy); + Ok(Some(res)) + } +} + +pub struct FilterParamsCubeFiltersCollector { + cube_name: String, + members: Vec, +} + +impl FilterParamsCubeFiltersCollector { + pub fn new(cube_name: String) -> Self { + Self { + cube_name, + members: vec![], + } + } +} + +impl NativeFinalize for FilterParamsCubeFiltersCollector {} + +impl ProxyCollector for FilterParamsCubeFiltersCollector { + type ResultType = FilterParamsCubeFiltersCollectorRes; + + fn on_get( + &mut self, + property_name: String, + _context_holder_ref: Rc, + ) -> Result, CubeError> { + println!("!!!! prop name: {}", property_name); + self.members.push(property_name); + Ok(Some(FilterParamsCubeFiltersCollectorRes::new())) + } +} + +pub struct FilterParamsCubeFiltersCollectorRes {} + +impl FilterParamsCubeFiltersCollectorRes { + pub fn new() -> Self { + Self {} + } +} + +impl NativeSerialize for FilterParamsCubeFiltersCollectorRes { + fn to_native( + &self, + context: Rc>, + ) -> Result, CubeError> { + let res = context.empty_struct()?; + res.set_field( + "filter", + NativeObjectHandle::new_from_type(context.to_string_fn("".to_string())?), + )?; + Ok(NativeObjectHandle::new_from_type(res)) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/handler.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/handler.rs new file mode 100644 index 0000000000000..3df24b001c47c --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/handler.rs @@ -0,0 +1,81 @@ +use crate::cube_bridge::base_tools::BaseTools; + +use super::Proxy; +use crate::cube_bridge::evaluator::CubeEvaluator; +use cubenativeutils::wrappers::context::NativeFinalize; +use cubenativeutils::wrappers::inner_types::InnerTypes; +use cubenativeutils::wrappers::serializer::{ + NativeDeserialize, NativeDeserializer, NativeSerialize, +}; +use cubenativeutils::wrappers::{ + object::NativeBox, NativeContextHolder, NativeFunction, NativeObjectHandle, NativeString, + NativeStruct, NativeType, +}; +use cubenativeutils::CubeError; +use serde::Deserialize; +use std::any::Any; +use std::cell::{RefCell, RefMut}; +use std::marker::PhantomData; +use std::rc::Rc; + +pub trait ProxyHandler { + fn as_any(self: Rc) -> Rc; +} + +pub struct NativeProxyHandler { + collector_box: NativeObjectHandle, +} + +impl ProxyHandler for NativeProxyHandler { + fn as_any(self: Rc) -> Rc { + self + } +} + +impl NativeProxyHandler { + pub fn new(collector_box: impl NativeBox) -> Rc { + Rc::new(Self { + collector_box: NativeObjectHandle::new_from_type(collector_box), + }) + } +} + +impl NativeSerialize for NativeProxyHandler { + fn to_native( + &self, + _context: Rc>, + ) -> Result, CubeError> { + Ok(self.collector_box.clone()) + } +} + +pub trait ProxyHandlerFunction { + fn as_any(self: Rc) -> Rc; +} + +pub struct NativeProxyHandlerFunction { + function: NativeObjectHandle, +} + +impl ProxyHandlerFunction for NativeProxyHandlerFunction { + fn as_any(self: Rc) -> Rc { + self + } +} + +impl NativeProxyHandlerFunction { + pub fn new(function: IT::Function) -> Rc { + Rc::new(Self { + function: NativeObjectHandle::new_from_type(function), + }) + } +} + +impl NativeSerialize for NativeProxyHandlerFunction { + fn to_native( + &self, + _context: Rc>, + ) -> Result, CubeError> { + Ok(self.function.clone()) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/handler_impl.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/handler_impl.rs new file mode 100644 index 0000000000000..8841e85e9cdab --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/handler_impl.rs @@ -0,0 +1,97 @@ +use crate::cube_bridge::base_tools::BaseTools; + +use super::{NativeProxy, Proxy}; +use super::{NativeProxyHandler, NativeProxyHandlerFunction, ProxyHandler, ProxyHandlerFunction}; +use crate::cube_bridge::evaluator::CubeEvaluator; +use cubenativeutils::wrappers::context::NativeFinalize; +use cubenativeutils::wrappers::inner_types::InnerTypes; +use cubenativeutils::wrappers::serializer::{ + NativeDeserialize, NativeDeserializer, NativeSerialize, +}; +use cubenativeutils::wrappers::{ + object::NativeBox, NativeContextHolder, NativeContextHolderRef, NativeFunction, + NativeObjectHandle, NativeString, NativeStruct, NativeType, +}; +use cubenativeutils::CubeError; +use serde::Deserialize; +use std::any::Any; +use std::cell::{Ref, RefCell}; +use std::marker::PhantomData; +use std::rc::Rc; + +pub trait ProxyCollector: NativeFinalize { + type ResultType; + fn on_get( + &mut self, + property_name: String, + context_holder_ref: Rc, + ) -> Result, CubeError>; +} + +#[derive(Clone)] +pub struct ProxyHandlerImpl { + collector: Rc>, + base_tools: Rc, +} + +impl ProxyHandlerImpl { + pub fn new(collector: T /*Rc>*/, base_tools: Rc) -> Rc { + Rc::new(Self { + collector: Rc::new(RefCell::new(collector)), + base_tools, + }) + } + + pub fn get_collector(&self) -> Ref { + self.collector.borrow() + } +} + +impl< + IT: InnerTypes, + RT: NativeSerialize, + T: ProxyCollector + 'static, + > NativeSerialize for ProxyHandlerImpl +{ + fn to_native( + &self, + context: Rc>, + ) -> Result, CubeError> { + let boxed_collector = context.boxed(self.collector.clone())?; + + let on_get_fn = context.function(move |fn_context, args| { + if args.len() >= 2 { + let collector_box = args[0].to_boxed::>>()?; + let collector = collector_box.deref_value(); + let mut collector_mut = collector.borrow_mut(); + if let Ok(property_name) = args[1].to_string() { + if let Some(result) = + collector_mut.on_get(property_name.value()?, fn_context.as_context_ref())? + { + let result = result.to_native(fn_context.clone())?; + Ok(result) + } else { + let r = fn_context.empty_struct()?; + let r = NativeObjectHandle::new_from_type(r); + Ok(r) + } + } else { + fn_context.undefined() + } + } else { + Err(CubeError::internal(format!( + "Collector for cubeDepsProxy is not alive" + ))) + } + })?; + + let proxy = self.base_tools.native_proxy( + NativeProxyHandler::new(boxed_collector), + NativeProxyHandlerFunction::::new(on_get_fn), + )?; + + let proxy = proxy.as_any().downcast::>().unwrap(); + + Ok(proxy.to_native(context)?) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/mod.rs new file mode 100644 index 0000000000000..06ab84f778706 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/mod.rs @@ -0,0 +1,17 @@ +mod cube_deps_collector; +mod filter_params_collector; +mod handler; +mod handler_impl; +mod proxy; + +pub use cube_deps_collector::{ + CubeDepsCollector, CubeDepsCollectorProp, CubeDepsCollectorProxyHandler, +}; + +pub use filter_params_collector::{FilterParamsCollector, FilterParamsCollectorProxyHandler}; + +pub use handler::{ + NativeProxyHandler, NativeProxyHandlerFunction, ProxyHandler, ProxyHandlerFunction, +}; +pub use handler_impl::{ProxyCollector, ProxyHandlerImpl}; +pub use proxy::{NativeProxy, Proxy}; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/proxy.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/proxy.rs new file mode 100644 index 0000000000000..1823ba8125bb1 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/proxy/proxy.rs @@ -0,0 +1,9 @@ +use cubenativeutils::wrappers::serializer::{NativeDeserialize, 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 Proxy {} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/return_string_fn.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/return_string_fn.rs new file mode 100644 index 0000000000000..5c4a1fd108e0c --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/return_string_fn.rs @@ -0,0 +1,27 @@ +use cubenativeutils::wrappers::inner_types::InnerTypes; +use cubenativeutils::wrappers::serializer::NativeSerialize; +use cubenativeutils::wrappers::NativeContextHolder; +use cubenativeutils::wrappers::NativeObjectHandle; +use cubenativeutils::CubeError; +use std::rc::Rc; + +pub struct ReturnStringFn { + value: String, +} + +impl ReturnStringFn { + pub fn new(value: String) -> Self { + ReturnStringFn { value } + } +} + +impl NativeSerialize for ReturnStringFn { + fn to_native( + &self, + context: Rc>, + ) -> Result, CubeError> { + Ok(NativeObjectHandle::new_from_type( + context.to_string_fn(self.value.clone())?, + )) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs index 0631a868cec13..07301e3852ed7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs @@ -182,7 +182,7 @@ impl SelectBuilder { schema } - pub fn build(self, mut nodes_factory: SqlNodesFactory) -> Select { + pub fn build(self, mut nodes_factory: SqlNodesFactory, all_filters: Option) -> Select { let cube_references = self.make_cube_references(); nodes_factory.set_cube_name_references(cube_references); let schema = if self.projection_columns.is_empty() { @@ -197,7 +197,7 @@ impl SelectBuilder { group_by: self.group_by, having: self.having, order_by: self.order_by, - context: Rc::new(VisitorContext::new(&nodes_factory)), + context: Rc::new(VisitorContext::new(&nodes_factory, all_filters)), ctes: self.ctes, is_distinct: self.is_distinct, limit: self.limit, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs index 21de374816760..fce50d91eceb5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs @@ -12,17 +12,18 @@ use cubenativeutils::{CubeError, CubeErrorCauseType}; use std::rc::Rc; pub struct BaseQuery { - context: NativeContextHolder, + context: Rc>, query_tools: Rc, request: Rc, } impl BaseQuery { pub fn try_new( - context: NativeContextHolder, + context: Rc>, options: Rc, ) -> Result { let query_tools = QueryTools::try_new( + context.as_context_ref(), options.cube_evaluator()?, options.base_tools()?, options.join_graph()?, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/full_key_query_aggregate_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/full_key_query_aggregate_planner.rs index a89db3fe234ea..a9cc60f48d1c0 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/full_key_query_aggregate_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/full_key_query_aggregate_planner.rs @@ -161,6 +161,9 @@ impl FullKeyAggregateQueryPlanner { let mut context_factory = self.context_factory.clone(); context_factory.set_render_references(render_references); - Ok(Rc::new(select_builder.build(context_factory))) + Ok(Rc::new(select_builder.build( + context_factory, + self.query_properties.all_filters(), + ))) } } 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..27d9bc361fa06 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 @@ -19,7 +19,7 @@ use std::rc::Rc; pub struct MultiStageMemberQueryPlanner { query_tools: Rc, - _query_properties: Rc, + query_properties: Rc, description: Rc, } @@ -31,7 +31,7 @@ impl MultiStageMemberQueryPlanner { ) -> Self { Self { query_tools, - _query_properties: query_properties, + query_properties: query_properties, description, } } @@ -185,7 +185,7 @@ impl MultiStageMemberQueryPlanner { select_builder.add_projection_member(&query_member, None); select_builder.set_group_by(group_by); select_builder.set_order_by(self.query_order()?); - let select = select_builder.build(context_factory); + let select = select_builder.build(context_factory, self.query_properties.all_filters()); Ok(Rc::new(Cte::new_from_select( Rc::new(select), @@ -266,7 +266,7 @@ impl MultiStageMemberQueryPlanner { if self.description.member().is_ungrupped() { context_factory.set_ungrouped(true); } - let select = select_builder.build(context_factory); + let select = select_builder.build(context_factory, None); Ok(Rc::new(Cte::new_from_select( Rc::new(select), @@ -352,7 +352,7 @@ impl MultiStageMemberQueryPlanner { let mut node_factory = SqlNodesFactory::new(); node_factory.set_render_references(render_references); Ok(QueryPlan::Select(Rc::new( - select_builder.build(node_factory), + select_builder.build(node_factory, None), ))) } 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 0a9218832cc63..8728bb20afede 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 @@ -100,7 +100,7 @@ impl MultiStageQueryPlanner { let select_builder = SelectBuilder::new(From::new_from_table_reference(alias.clone(), schema, None)); - Rc::new(select_builder.build(SqlNodesFactory::new())) + Rc::new(select_builder.build(SqlNodesFactory::new(), self.query_properties.all_filters())) } fn create_multi_stage_inode_member( 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..6d62526f99d21 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 @@ -242,7 +242,10 @@ impl MultipliedMeasuresQueryPlanner { .rendered_as_multiplied_measures .clone(), ); - Ok(Rc::new(select_builder.build(context_factory))) + Ok(Rc::new(select_builder.build( + context_factory, + self.query_properties.all_filters(), + ))) } fn check_should_build_join_for_measure_select( @@ -301,7 +304,10 @@ impl MultipliedMeasuresQueryPlanner { for meas in measures.iter() { select_builder.add_projection_member(&meas.clone().as_base_member(), None); } - Ok(Rc::new(select_builder.build(context_factory))) + Ok(Rc::new(select_builder.build( + context_factory, + self.query_properties.all_filters(), + ))) } fn regular_measures_subquery( @@ -348,7 +354,10 @@ impl MultipliedMeasuresQueryPlanner { .clone(), ); - Ok(Rc::new(select_builder.build(context_factory))) + Ok(Rc::new(select_builder.build( + context_factory, + self.query_properties.all_filters(), + ))) } fn key_query( @@ -407,6 +416,9 @@ impl MultipliedMeasuresQueryPlanner { select_builder.set_distinct(); select_builder.set_filter(self.query_properties.all_filters()); - Ok(Rc::new(select_builder.build(context_factory))) + Ok(Rc::new(select_builder.build( + context_factory, + self.query_properties.all_filters(), + ))) } } 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 cf7d79475bf07..ec1bfeaf0f20c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs @@ -71,7 +71,8 @@ impl SimpleQueryPlanner { select_builder.set_having(having); select_builder.set_limit(self.query_properties.row_limit()); select_builder.set_offset(self.query_properties.offset()); - let res = Rc::new(select_builder.build(context_factory)); + let res = + Rc::new(select_builder.build(context_factory, self.query_properties.all_filters())); Ok(res) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs index e75372e7c4896..19292b4d8b215 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs @@ -12,6 +12,7 @@ use crate::planner::sql_evaluator::collectors::collect_join_hints; use crate::planner::sql_templates::PlanSqlTemplates; use chrono_tz::Tz; use convert_case::{Case, Casing}; +use cubenativeutils::wrappers::NativeContextHolderRef; use cubenativeutils::CubeError; use itertools::Itertools; use lazy_static::lazy_static; @@ -117,6 +118,7 @@ impl QueryToolsCachedData { } pub struct QueryTools { + context_holder_ref: Rc, cube_evaluator: Rc, base_tools: Rc, join_graph: Rc, @@ -129,13 +131,18 @@ pub struct QueryTools { impl QueryTools { pub fn try_new( + context_holder_ref: Rc, cube_evaluator: Rc, base_tools: Rc, join_graph: Rc, timezone_name: Option, ) -> Result, CubeError> { let templates_render = base_tools.sql_templates()?; - let evaluator_compiler = Rc::new(RefCell::new(Compiler::new(cube_evaluator.clone()))); + let evaluator_compiler = Rc::new(RefCell::new(Compiler::new( + cube_evaluator.clone(), + base_tools.clone(), + context_holder_ref.clone(), + ))); let timezone = if let Some(timezone) = timezone_name { Some( timezone @@ -147,6 +154,7 @@ impl QueryTools { }; let sql_templates = PlanSqlTemplates::new(templates_render.clone()); Ok(Rc::new(Self { + context_holder_ref, cube_evaluator, base_tools, join_graph, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs index fe20a91af738a..b9093572cd76f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs @@ -5,22 +5,32 @@ use super::{ CubeNameSymbolFactory, CubeTableSymbolFactory, DimensionSymbolFactory, MeasureSymbolFactory, SqlCall, SymbolFactory, TraversalVisitor, }; +use crate::cube_bridge::base_tools::BaseTools; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::join_hints::JoinHintItem; use crate::cube_bridge::member_sql::MemberSql; +use cubenativeutils::wrappers::NativeContextHolderRef; use cubenativeutils::CubeError; use std::collections::HashMap; use std::rc::Rc; pub struct Compiler { cube_evaluator: Rc, + base_tools: Rc, + context_holder_ref: Rc, /* (type, name) */ members: HashMap<(String, String), Rc>, } impl Compiler { - pub fn new(cube_evaluator: Rc) -> Self { + pub fn new( + cube_evaluator: Rc, + base_tools: Rc, + context_holder_ref: Rc, + ) -> Self { Self { + base_tools, cube_evaluator, + context_holder_ref, members: HashMap::new(), } } @@ -94,7 +104,12 @@ impl Compiler { cube_name: &String, member_sql: Rc, ) -> Result, CubeError> { - let dep_builder = DependenciesBuilder::new(self, self.cube_evaluator.clone()); + let dep_builder = DependenciesBuilder::new( + self, + self.cube_evaluator.clone(), + self.base_tools.clone(), + self.context_holder_ref.clone(), + ); let deps = dep_builder.build(cube_name.clone(), member_sql.clone())?; let sql_call = SqlCall::new(member_sql, deps); Ok(Rc::new(sql_call)) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/dependecy.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/dependecy.rs index b1c69f57230b1..8955c89d6c752 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/dependecy.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/dependecy.rs @@ -1,9 +1,15 @@ use super::symbols::MemberSymbol; use super::Compiler; +use crate::cube_bridge::base_tools::BaseTools; use crate::cube_bridge::evaluator::{CallDep, CubeEvaluator}; -use crate::cube_bridge::member_sql::MemberSql; +use crate::cube_bridge::member_sql::{ContextSymbolArg, MemberSql, MemberSqlArgForResolve}; +use crate::cube_bridge::proxy::{ + CubeDepsCollector, CubeDepsCollectorProp, CubeDepsCollectorProxyHandler, FilterParamsCollector, + FilterParamsCollectorProxyHandler, +}; +use cubenativeutils::wrappers::NativeContextHolderRef; use cubenativeutils::CubeError; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::rc::Rc; #[derive(Clone)] @@ -48,19 +54,30 @@ pub enum ContextSymbolDep { pub enum Dependency { SymbolDependency(Rc), CubeDependency(CubeDependency), + FilterParamsDependency(HashMap>), + FilterGroupDependency, ContextDependency(ContextSymbolDep), } pub struct DependenciesBuilder<'a> { compiler: &'a mut Compiler, cube_evaluator: Rc, + base_tools: Rc, + context_holder_ref: Rc, } impl<'a> DependenciesBuilder<'a> { - pub fn new(compiler: &'a mut Compiler, cube_evaluator: Rc) -> Self { + pub fn new( + compiler: &'a mut Compiler, + cube_evaluator: Rc, + base_tools: Rc, + context_holder_ref: Rc, + ) -> Self { DependenciesBuilder { compiler, cube_evaluator, + base_tools, + context_holder_ref, } } @@ -69,39 +86,89 @@ impl<'a> DependenciesBuilder<'a> { cube_name: String, member_sql: Rc, ) -> Result, CubeError> { - let call_deps = if member_sql.need_deps_resolve() { - self.cube_evaluator - .resolve_symbols_call_deps(cube_name.clone(), member_sql)? - } else { - 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 arg_names = member_sql.args_names(); + let mut deps_to_resolve = Vec::new(); + for arg_name in arg_names { + if arg_name == "FILTER_PARAMS" { + let collector = FilterParamsCollector::try_new(self.base_tools.clone())?; + let proxy = + FilterParamsCollectorProxyHandler::new(collector, self.base_tools.clone()); + deps_to_resolve.push(MemberSqlArgForResolve::FilterParamsProxy(proxy)); + } else if arg_name == "FILTER_GROUP" { + deps_to_resolve.push(MemberSqlArgForResolve::FilterGroupMock()); + } else if let Some(context_arg) = self.build_context_resolve_arg_tmp(arg_name)? { + deps_to_resolve.push(MemberSqlArgForResolve::ContextSymbol(context_arg)); + } else if self + .cube_evaluator + .is_name_of_symbol_in_cube(cube_name.clone(), arg_name.clone())? + { + deps_to_resolve.push(MemberSqlArgForResolve::String("".to_string())); + } else if self.is_current_cube(&arg_name) + || self.cube_evaluator.is_name_of_cube(arg_name.clone())? + { + let new_cube_name = if self.is_current_cube(&arg_name) { + cube_name.clone() + } else { + arg_name.clone() + }; + let collector = CubeDepsCollector::try_new( + new_cube_name, + self.cube_evaluator.clone(), + self.base_tools.clone(), + self.context_holder_ref.clone(), + )?; + let proxy = CubeDepsCollectorProxyHandler::new(collector, self.base_tools.clone()); + deps_to_resolve.push(MemberSqlArgForResolve::CubeProxy(proxy)); + } else { + return Err(CubeError::internal(format!( + "Undefinded dependency {}", + arg_name + ))); } } + member_sql.deps_resolve(deps_to_resolve.clone())?; let mut result = Vec::new(); - for (i, dep) in call_deps.iter().enumerate() { - if dep.parent.is_some() { - continue; - } - if let Some(context_dep) = self.build_context_dep(&dep.name) { - result.push(context_dep); - continue; - } - if childs[i].is_empty() { - result.push(Dependency::SymbolDependency( - self.build_evaluator(&cube_name, &dep.name)?, - )); - } else { - let dep = self.build_cube_dependency(&cube_name, i, &call_deps, &childs)?; - result.push(Dependency::CubeDependency(dep)); - } + for (dep, arg_name) in deps_to_resolve.into_iter().zip(arg_names.iter()) { + match dep { + MemberSqlArgForResolve::String(_) => { + result.push(Dependency::SymbolDependency( + self.build_evaluator(&cube_name, &arg_name)?, + )); + } + MemberSqlArgForResolve::CubeProxy(proxy_handler) => { + result.push(Dependency::CubeDependency( + self.build_cube_dependency(proxy_handler.clone())?, + )); + } + MemberSqlArgForResolve::FilterParamsProxy(proxy_handler) => { + proxy_handler.get_collector().collected_result(); + let dep = Dependency::ContextDependency(ContextSymbolDep::FilterParams); + result.push(dep); + } + MemberSqlArgForResolve::FilterGroupMock() => { + let dep = Dependency::ContextDependency(ContextSymbolDep::FilterGroup); + result.push(dep); + } + MemberSqlArgForResolve::ContextSymbol(context_symbol_arg) => { + let dep = match context_symbol_arg { + ContextSymbolArg::SecurityContext(_rc) => { + Dependency::ContextDependency(ContextSymbolDep::SecurityContext) + } + ContextSymbolArg::SqlUtils(_rc) => { + Dependency::ContextDependency(ContextSymbolDep::SqlUtils) + } + ContextSymbolArg::FilterParams(_rc) => { + Dependency::ContextDependency(ContextSymbolDep::FilterParams) + } + ContextSymbolArg::FilterGroup(_rc) => { + Dependency::ContextDependency(ContextSymbolDep::FilterGroup) + } + }; + result.push(dep); + } + }; } Ok(result) @@ -109,12 +176,48 @@ impl<'a> DependenciesBuilder<'a> { fn build_cube_dependency( &mut self, - cube_name: &String, - dep_index: usize, - call_deps: &Vec, - call_childs: &Vec>, + proxy_handler: Rc, ) -> Result { - let dep = &call_deps[dep_index]; + let collector = proxy_handler.get_collector(); + let cube_symbol = self + .compiler + .add_cube_table_evaluator(collector.cube_name().clone())?; + let sql_fn = if collector.has_sql_fn() { + Some( + self.compiler + .add_cube_table_evaluator(collector.cube_name().clone())?, + ) + } else { + None + }; + + let to_string_fn = if collector.has_to_string_fn() { + Some( + self.compiler + .add_cube_name_evaluator(collector.cube_name().clone())?, + ) + } else { + None + }; + + let mut properties = HashMap::new(); + for dep in collector.deps().iter() { + match dep { + CubeDepsCollectorProp::Symbol(name) => { + let prop = CubeDepProperty::SymbolDependency( + self.build_evaluator(collector.cube_name(), name)?, + ); + properties.insert(name.clone(), prop); + } + CubeDepsCollectorProp::Cube(rc) => { + let prop = + CubeDepProperty::CubeDependency(self.build_cube_dependency(rc.clone())?); + let prop_name = rc.get_collector().cube_name().clone(); + properties.insert(prop_name, prop); + } + }; + } + /* let dep = &call_deps[dep_index]; let new_cube_name = if self.is_current_cube(&dep.name) { cube_name.clone() } else { @@ -151,7 +254,7 @@ impl<'a> DependenciesBuilder<'a> { }; properties.insert(name.clone(), child_dep); } - } + } */ Ok(CubeDependency::new( cube_symbol, sql_fn, @@ -174,6 +277,28 @@ impl<'a> DependenciesBuilder<'a> { } } + fn build_context_resolve_arg_tmp( + &self, + name: &str, + ) -> Result, CubeError> { + let res = match name { + "USER_CONTEXT" | "SECURITY_CONTEXT" => Some(ContextSymbolArg::SecurityContext( + self.base_tools.security_context_for_rust()?, + )), + "FILTER_PARAMS" => Some(ContextSymbolArg::FilterParams( + self.base_tools.filters_proxy()?, + )), + "FILTER_GROUP" => Some(ContextSymbolArg::FilterGroup( + self.base_tools.filter_group_function()?, + )), + "SQL_UTILS" => Some(ContextSymbolArg::SqlUtils( + self.base_tools.sql_utils_for_rust()?, + )), + _ => None, + }; + Ok(res) + } + //FIXME may be should be moved to BaseTools fn is_current_cube(&self, name: &str) -> bool { match name { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs index 63a9a906f4805..a0acf79b8e942 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs @@ -59,7 +59,7 @@ impl SqlCall { Dependency::CubeDependency(cube_dep) => { self.extract_symbol_deps_from_cube_dep(cube_dep, result) } - Dependency::ContextDependency(_) => {} + _ => {} } } } @@ -71,7 +71,7 @@ impl SqlCall { Dependency::CubeDependency(cube_dep) => { self.extract_symbol_deps_with_path_from_cube_dep(cube_dep, vec![], result) } - Dependency::ContextDependency(_) => {} + _ => {} } } } @@ -89,7 +89,7 @@ impl SqlCall { Dependency::CubeDependency(cube_dep) => { self.extract_cube_deps_from_cube_dep(cube_dep, result) } - Dependency::ContextDependency(_) => {} + _ => {} } } } @@ -169,6 +169,8 @@ impl SqlCall { Dependency::ContextDependency(contex_symbol) => { self.apply_context_symbol(contex_symbol, query_tools.clone()) } + Dependency::FilterParamsDependency(hash_map) => todo!(), + Dependency::FilterGroupDependency => todo!(), } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs index 0eba5e2f4592a..9f05346184f53 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs @@ -1,6 +1,7 @@ use super::query_tools::QueryTools; use super::sql_evaluator::sql_nodes::{SqlNode, SqlNodesFactory}; use super::sql_evaluator::{MemberSymbol, SqlCall}; +use crate::plan::Filter; use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::CubeError; @@ -8,12 +9,14 @@ use std::rc::Rc; pub struct VisitorContext { node_processor: Rc, + all_filters: Option, //This is the most convenient way to deliver filters to sql_call to generate FILTER_PARAMS and FILTER_GROUP } impl VisitorContext { - pub fn new(nodes_factory: &SqlNodesFactory) -> Self { + pub fn new(nodes_factory: &SqlNodesFactory, all_filters: Option) -> Self { Self { node_processor: nodes_factory.default_node_processor(), + all_filters, } } diff --git a/rust/cubesqlplanner/nativebridge/src/lib.rs b/rust/cubesqlplanner/nativebridge/src/lib.rs index fd2dff5ac18d2..f69cfb34a25c3 100644 --- a/rust/cubesqlplanner/nativebridge/src/lib.rs +++ b/rust/cubesqlplanner/nativebridge/src/lib.rs @@ -362,7 +362,9 @@ impl NativeService { fn imports(&self) -> proc_macro2::TokenStream { quote! { use cubenativeutils::wrappers::inner_types::InnerTypes; - use cubenativeutils::wrappers::object::NativeStruct; + use cubenativeutils::wrappers::object::{NativeStruct, NativeRoot}; + use cubenativeutils::wrappers::context::NativeContextHolderRef; + use cubenativeutils::wrappers::{RootHolder, Rootable}; } } @@ -379,11 +381,69 @@ impl NativeService { pub trait #service_ident { #( #methods )* fn as_any(self: Rc) -> Rc; + fn to_root(self: Rc) -> Result>, CubeError>; #static_data_method } } } + fn root_struct(&self) -> proc_macro2::TokenStream { + let root_struct_ident = format_ident!("NativeRoot{}", self.ident); + let service_ident = &self.ident; + let struct_ident = self.struct_ident(); + quote! { + pub struct #root_struct_ident { + root: IT::Root, + } + impl #root_struct_ident { + pub fn new(root: IT::Root) -> Rc { + Rc::new(Self { root }) + } + } + impl RootHolder for #root_struct_ident { + fn as_any(self: Rc) -> Rc { + self.clone() + } + fn drop( + self: Rc, + context_holder_ref: Rc, + ) -> Result<(), CubeError> { + let context_holder = context_holder_ref + .as_any() + .downcast::>() + .map_err(|_| { + CubeError::internal(format!( + "Wrong context holder type for obtain valur from root holder" + )) + })?; + self.root.drop_root(context_holder.context()) + } + fn to_inner( + self: Rc, + context_holder_ref: Rc, + ) -> Result, CubeError> { + let context_holder = context_holder_ref + .as_any() + .downcast::>() + .map_err(|_| { + CubeError::internal(format!( + "Wrong context holder type for obtain value from root holder" + )) + })?; + let result = self.root.to_inner(context_holder.context())?; + let result = Rc::new(#struct_ident::from_native(result)?); + Ok(result) + } + } + impl Rootable for #struct_ident { + fn to_root(self: Rc) -> Result>, CubeError> { + let native_root = self.native_object.to_root()?; + Ok(#root_struct_ident::::new(native_root)) + } + } + } + } + fn static_data_method_def(&self) -> proc_macro2::TokenStream { if let Some(static_data_type) = &self.static_data_type { quote! { @@ -467,6 +527,9 @@ impl NativeService { fn as_any(self: Rc) -> Rc { self.clone() } + fn to_root(self: Rc) -> Result>, CubeError> { + Rootable::to_root(self) + } #static_data_method } } @@ -477,7 +540,7 @@ impl NativeService { quote! { impl NativeSerialize for #struct_ident { - fn to_native(&self, _context: NativeContextHolder) -> Result, CubeError> { + fn to_native(&self, _context: Rc>) -> Result, CubeError> { Ok(self.native_object.clone()) } } @@ -691,6 +754,7 @@ impl ToTokens for NativeService { self.struct_impl(), self.struct_bridge_impl(), self.serialization_impl(), + self.root_struct(), ]); } }