Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tesseract): Subqueries support #9108

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
44 changes: 18 additions & 26 deletions packages/cubejs-backend-native/src/node_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::stream::OnDrainHandler;
use crate::tokio_runtime_node;
use crate::transport::NodeBridgeTransport;
use crate::utils::batch_to_rows;
use cubenativeutils::wrappers::neon::context::ContextHolder;
use cubenativeutils::wrappers::neon::context::neon_run_with_guarded_lifetime;
use cubenativeutils::wrappers::neon::inner_types::NeonInnerTypes;
use cubenativeutils::wrappers::neon::object::NeonObject;
use cubenativeutils::wrappers::object_handle::NativeObjectHandle;
Expand Down Expand Up @@ -463,37 +463,29 @@ pub fn setup_logger(mut cx: FunctionContext) -> JsResult<JsUndefined> {
//============ sql planner ===================

fn build_sql_and_params(cx: FunctionContext) -> JsResult<JsValue> {
//IMPORTANT It seems to be safe here, because context lifetime is bound to function, but this
//context should be used only inside function
let mut cx = extend_function_context_lifetime(cx);
let options = cx.argument::<JsValue>(0)?;

let neon_context_holder = ContextHolder::new(cx);

let options = NativeObjectHandle::<NeonInnerTypes<'static, FunctionContext<'static>>>::new(
NeonObject::new(neon_context_holder.clone(), options),
);

let context_holder =
NativeContextHolder::<NeonInnerTypes<'static, FunctionContext<'static>>>::new(
neon_run_with_guarded_lifetime(cx, |neon_context_holder| {
let options =
NativeObjectHandle::<NeonInnerTypes<FunctionContext<'static>>>::new(NeonObject::new(
neon_context_holder.clone(),
neon_context_holder
.with_context(|cx| cx.argument::<JsValue>(0))
.unwrap()?,
));

let context_holder = NativeContextHolder::<NeonInnerTypes<FunctionContext<'static>>>::new(
neon_context_holder,
);

let base_query_options = Rc::new(NativeBaseQueryOptions::from_native(options).unwrap());
let base_query_options = Rc::new(NativeBaseQueryOptions::from_native(options).unwrap());

let base_query = BaseQuery::try_new(context_holder.clone(), base_query_options).unwrap();
let base_query = BaseQuery::try_new(context_holder.clone(), base_query_options).unwrap();

//arg_clrep.into_js(&mut cx)
let res = base_query.build_sql_and_params().unwrap();

let result: NeonObject<'static, FunctionContext<'static>> = res.into_object();
let result = result.into_object();

Ok(result)
}
let res = base_query.build_sql_and_params();

fn extend_function_context_lifetime<'a>(cx: FunctionContext<'a>) -> FunctionContext<'static> {
unsafe { std::mem::transmute::<FunctionContext<'a>, FunctionContext<'static>>(cx) }
let result: NeonObject<FunctionContext<'static>> = res.into_object();
let result = result.into_object();
Ok(result)
})
}

fn debug_js_to_clrepr_to_js(mut cx: FunctionContext) -> JsResult<JsValue> {
Expand Down
20 changes: 19 additions & 1 deletion packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,17 @@ export class BaseQuery {
ungrouped: this.options.ungrouped

};
const res = nativeBuildSqlAndParams(queryParams);
const buildResult = nativeBuildSqlAndParams(queryParams);

if (buildResult.error) {
if (buildResult.error.cause && buildResult.error.cause === 'User') {
throw new UserError(buildResult.error.message);
} else {
throw new Error(buildResult.error.message);
}
}

const res = buildResult.result;
// FIXME
res[1] = [...res[1]];
return res;
Expand Down Expand Up @@ -3336,6 +3346,7 @@ export class BaseQuery {
sort: '{{ expr }} {% if asc %}ASC{% else %}DESC{% endif %} NULLS {% if nulls_first %}FIRST{% else %}LAST{% endif %}',
order_by: '{% if index %} {{ index }} {% else %} {{ expr }} {% endif %} {% if asc %}ASC{% else %}DESC{% endif %}{% if nulls_first %} NULLS FIRST{% endif %}',
cast: 'CAST({{ expr }} AS {{ data_type }})',
cast_to_string: 'CAST({{ expr }} AS TEXT)',
window_function: '{{ fun_call }} OVER ({% if partition_by_concat %}PARTITION BY {{ partition_by_concat }}{% if order_by_concat or window_frame %} {% endif %}{% endif %}{% if order_by_concat %}ORDER BY {{ order_by_concat }}{% if window_frame %} {% endif %}{% endif %}{% if window_frame %}{{ window_frame }}{% endif %})',
window_frame_bounds: '{{ frame_type }} BETWEEN {{ frame_start }} AND {{ frame_end }}',
in_list: '{{ expr }} {% if negated %}NOT {% endif %}IN ({{ in_exprs_concat }})',
Expand All @@ -3352,6 +3363,7 @@ export class BaseQuery {
like: '{{ expr }} {% if negated %}NOT {% endif %}LIKE {{ pattern }}',
ilike: '{{ expr }} {% if negated %}NOT {% endif %}ILIKE {{ pattern }}',
like_escape: '{{ like_expr }} ESCAPE {{ escape_char }}',
concat_strings: '{{ strings | join(\' || \' ) }}',
},
filters: {
equals: '{{ column }} = {{ value }}{{ is_null_check }}',
Expand Down Expand Up @@ -3807,6 +3819,12 @@ export class BaseQuery {
return this.contextSymbolsProxy(this.contextSymbols.securityContext);
}

sqlUtilsForRust() {
return {
convertTz: this.convertTz.bind(this)
};
}

contextSymbolsProxy(symbols) {
return BaseQuery.contextSymbolsProxyFrom(symbols, this.paramAllocator.allocateParam.bind(this.paramAllocator));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,11 @@ export class CubeSymbols {
return Object.assign({
filterParams: this.filtersProxyDep(),
filterGroup: this.filterGroupFunctionDep(),
securityContext: BaseQuery.contextSymbolsProxyFrom({}, (param) => param)
securityContext: BaseQuery.contextSymbolsProxyFrom({}, (param) => param),
sqlUtils: {
convertTz: (f) => f

},
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ describe('SQL Generation', () => {
}
});

it('having filter with operator OR 1', async () => {
it('having filter with operator OR', async () => {
await compiler.compile();

const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, {
Expand Down Expand Up @@ -648,7 +648,7 @@ describe('SQL Generation', () => {
});
});

it('where filter with operators OR & AND 1', async () => {
it('where filter with operators OR & AND', async () => {
await compiler.compile();

const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -945,8 +945,6 @@ describe('SQL Generation', () => {
timezone: 'America/Los_Angeles'
});

console.log(query.buildSqlAndParams());

expect(query.buildSqlAndParams()[0]).toMatch(/HLL_COUNT\.MERGE/);
expect(query.buildSqlAndParams()[0]).toMatch(/HLL_COUNT\.INIT/);
});
Expand All @@ -971,7 +969,7 @@ describe('SQL Generation', () => {

console.log(query.buildSqlAndParams());

expect(query.buildSqlAndParams()[0]).toMatch(/OFFSET (\d) LIMIT (\d)/);
expect(query.buildSqlAndParams()[0]).toMatch(/OFFSET (\d)\s+LIMIT (\d)/);
});

it('calculated join', async () => {
Expand Down Expand Up @@ -2535,6 +2533,9 @@ describe('SQL Generation', () => {

const sqlBuild = query.buildSqlAndParams();

console.log(sqlBuild[0]);
console.log(sqlBuild[1]);

expect(sqlBuild[0].includes('America/Los_Angeles')).toEqual(true);
expect(sqlBuild[1][0]).toEqual(granularityTest.from);
expect(sqlBuild[1][1]).toEqual(granularityTest.to);
Expand Down
2 changes: 1 addition & 1 deletion rust/cubenativeutils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod wrappers;
pub use cubesql::CubeError;
pub use cubesql::{CubeError, CubeErrorCauseType};
31 changes: 17 additions & 14 deletions rust/cubenativeutils/src/wrappers/context.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use super::inner_types::InnerTypes;
use super::object::NativeBox;
use super::object_handle::NativeObjectHandle;
use cubesql::CubeError;

pub trait NativeContext<IT: InnerTypes>: Clone {
fn boolean(&self, v: bool) -> IT::Boolean;
fn string(&self, v: String) -> IT::String;
fn number(&self, v: f64) -> IT::Number;
fn undefined(&self) -> NativeObjectHandle<IT>;
fn empty_array(&self) -> IT::Array;
fn empty_struct(&self) -> IT::Struct;
fn to_string_fn(&self, result: String) -> IT::Function;
fn boolean(&self, v: bool) -> Result<IT::Boolean, CubeError>;
fn string(&self, v: String) -> Result<IT::String, CubeError>;
fn number(&self, v: f64) -> Result<IT::Number, CubeError>;
fn undefined(&self) -> Result<NativeObjectHandle<IT>, CubeError>;
fn empty_array(&self) -> Result<IT::Array, CubeError>;
fn empty_struct(&self) -> Result<IT::Struct, CubeError>;
//fn boxed<T: 'static>(&self, value: T) -> impl NativeBox<IT, T>;
fn to_string_fn(&self, result: String) -> Result<IT::Function, CubeError>;
}

#[derive(Clone)]
Expand All @@ -23,26 +26,26 @@ impl<IT: InnerTypes> NativeContextHolder<IT> {
pub fn context(&self) -> &impl NativeContext<IT> {
&self.context
}
pub fn boolean(&self, v: bool) -> IT::Boolean {
pub fn boolean(&self, v: bool) -> Result<IT::Boolean, CubeError> {
self.context.boolean(v)
}
pub fn string(&self, v: String) -> IT::String {
pub fn string(&self, v: String) -> Result<IT::String, CubeError> {
self.context.string(v)
}
pub fn number(&self, v: f64) -> IT::Number {
pub fn number(&self, v: f64) -> Result<IT::Number, CubeError> {
self.context.number(v)
}
pub fn undefined(&self) -> NativeObjectHandle<IT> {
pub fn undefined(&self) -> Result<NativeObjectHandle<IT>, CubeError> {
self.context.undefined()
}
pub fn empty_array(&self) -> IT::Array {
pub fn empty_array(&self) -> Result<IT::Array, CubeError> {
self.context.empty_array()
}
pub fn empty_struct(&self) -> IT::Struct {
pub fn empty_struct(&self) -> Result<IT::Struct, CubeError> {
self.context.empty_struct()
}
#[allow(dead_code)]
pub fn to_string_fn(&self, result: String) -> IT::Function {
pub fn to_string_fn(&self, result: String) -> Result<IT::Function, CubeError> {
self.context.to_string_fn(result)
}
}
Loading