Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions packages/cubejs-backend-native/js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,8 +450,18 @@ export const sql4sql = async (instance: SqlInterfaceInstance, sqlQuery: string,

export const buildSqlAndParams = (cubeEvaluator: any): String => {
const native = loadNative();

return native.buildSqlAndParams(cubeEvaluator);
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, safeCallFn);
};

export type ResultRow = Record<string, string>;
Expand Down
12 changes: 12 additions & 0 deletions packages/cubejs-backend-native/src/node_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,18 @@ fn build_sql_and_params(cx: FunctionContext) -> JsResult<JsValue> {
.unwrap()?,
));

let safe_call_fn = neon_context_holder
.with_context(|cx| {
if let Ok(func) = cx.argument::<JsFunction>(1) {
Some(func)
} else {
None
}
})
.unwrap();

neon_context_holder.set_safe_call_fn(safe_call_fn).unwrap();

let context_holder = NativeContextHolder::<NeonInnerTypes<FunctionContext<'static>>>::new(
neon_context_holder,
);
Expand Down
108 changes: 107 additions & 1 deletion rust/cubenativeutils/src/wrappers/neon/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,81 @@ impl<'cx> NoenContextLifetimeExpand<'cx> for FunctionContext<'cx> {
}
}

pub struct SafeCallFn<'a> {
safe_fn: &'a Option<Handle<'static, JsFunction>>,
}

impl<'a> SafeCallFn<'a> {
pub fn new(safe_fn: &'a Option<Handle<'static, JsFunction>>) -> Self {
Self { safe_fn }
}

pub fn safe_call<C: Context<'static>, T: Value>(
&self,
cx: &mut C,
func: &Handle<'static, JsFunction>,
this: Handle<'static, T>,
mut args: Vec<Handle<'static, JsValue>>,
) -> Result<Handle<'static, JsValue>, 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::<JsObject, _>(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::<JsUndefined, _>(cx) {
let error_string = err_field.downcast::<JsString, _>(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::<JsUndefined, _>(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<C: Context<'static>> {
cx: C,
safe_call_fn: Option<Handle<'static, JsFunction>>,
}

impl<C: Context<'static>> ContextWrapper<C> {
pub fn new(cx: C) -> Rc<RefCell<Self>> {
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<Handle<'static, JsFunction>>) {
self.safe_call_fn = fn_handle;
}

pub fn with_context<T, F>(&mut self, f: F) -> T
Expand All @@ -45,6 +113,14 @@ impl<C: Context<'static>> ContextWrapper<C> {
f(&mut self.cx)
}

pub fn with_context_and_safe_fn<T, F>(&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
}
Expand Down Expand Up @@ -116,6 +192,36 @@ impl<C: Context<'static>> ContextHolder<C> {
))
}
}

pub fn with_context_and_safe_fn<T, F>(&self, f: F) -> Result<T, CubeError>
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<Handle<'static, JsFunction>>,
) -> 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"
)))
}
}
}

impl<C: Context<'static> + 'static> NativeContext<NeonInnerTypes<C>> for ContextHolder<C> {
Expand Down
13 changes: 12 additions & 1 deletion rust/cubenativeutils/src/wrappers/neon/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use self::{
neon_struct::NeonStruct,
};
use super::inner_types::NeonInnerTypes;
use crate::wrappers::{neon::context::ContextHolder, object::NativeObject};
use crate::wrappers::{
neon::context::{ContextHolder, SafeCallFn},
object::NativeObject,
};
use cubesql::CubeError;
use neon::prelude::*;

Expand Down Expand Up @@ -64,6 +67,14 @@ impl<C: Context<'static> + 'static, V: Value + 'static> NeonTypeHandle<C, V> {
})?
}

pub fn map_neon_object_with_safe_call_fn<T, F>(&self, f: F) -> Result<T, CubeError>
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 is_a<U: Value>(&self) -> Result<bool, CubeError> {
self.context.with_context(|cx| self.object.is_a::<U, _>(cx))
}
Expand Down
12 changes: 6 additions & 6 deletions rust/cubenativeutils/src/wrappers/neon/object/neon_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ impl<C: Context<'static> + 'static> NativeFunction<NeonInnerTypes<C>> for NeonFu
.into_iter()
.map(|arg| -> Result<_, CubeError> { Ok(arg.into_object().get_object()) })
.collect::<Result<Vec<_>, _>>()?;
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("Failed to call function ".to_string()))
})??;
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,
Expand Down
24 changes: 11 additions & 13 deletions rust/cubenativeutils/src/wrappers/neon/object/neon_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,17 @@ impl<C: Context<'static> + 'static> NativeStruct<NeonInnerTypes<C>> for NeonStru
.map(|arg| -> Result<_, CubeError> { Ok(arg.into_object().get_object()) })
.collect::<Result<Vec<_>, _>>()?;

let neon_reuslt = self.object.map_neon_object(|cx, neon_object| {
let neon_method = neon_object
.get::<JsFunction, _, _>(cx, method)
.map_err(|_| CubeError::internal(format!("Method `{}` not found", method)))?;
neon_method
.call(cx, *neon_object, neon_args)
.map_err(|err| {
CubeError::internal(format!(
"Failed to call method `{} {} {:?}",
method, err, err
))
})
})??;
let neon_reuslt =
self.object
.map_neon_object_with_safe_call_fn(|cx, neon_object, safe_call_fn| {
let neon_method =
neon_object
.get::<JsFunction, _, _>(cx, method)
.map_err(|_| {
CubeError::internal(format!("Method `{}` not found", method))
})?;
safe_call_fn.safe_call(cx, &neon_method, *neon_object, neon_args)
})??;
Ok(NativeObjectHandle::new(NeonObject::new(
self.object.context.clone(),
neon_reuslt,
Expand Down
Loading