From 41a7da58a5ca0bce38a0463f71d934f932caf559 Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Mon, 26 Aug 2024 14:58:21 +0200 Subject: [PATCH 1/5] feat(cubesql): CubeScan - don't clone strings for non stream response --- .../cubesql/src/compile/engine/df/scan.rs | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs index 73e3baab72d14..a9e53adf7197d 100644 --- a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs +++ b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs @@ -446,8 +446,11 @@ struct CubeScanExecutionPlan { } #[derive(Debug)] -pub enum FieldValue { +pub enum FieldValue<'a> { + // Neon doesn't allow us to build string reference, because V8 uses UTF-16, while Rust uses UTF-8 String(String), + // Best variant for us with strings, because we dont need to clone string + StringRef(&'a String), Number(f64), Bool(bool), Null, @@ -479,9 +482,8 @@ impl ValueObject for JsonValueObject { &'a mut self, index: usize, field_name: &str, - ) -> std::result::Result { - let option = self.rows[index].as_object_mut(); - let as_object = if let Some(as_object) = option { + ) -> std::result::Result, CubeError> { + let as_object = if let Some(as_object) = self.rows[index].as_object() { as_object } else { return Err(CubeError::user(format!( @@ -489,17 +491,15 @@ impl ValueObject for JsonValueObject { self.rows[index] ))); }; - let value = as_object - .get(field_name) - .unwrap_or(&Value::Null) - // TODO expose strings as references to avoid clonning - .clone(); + + let value = as_object.get(field_name).unwrap_or(&Value::Null); + Ok(match value { - Value::String(s) => FieldValue::String(s), + Value::String(s) => FieldValue::StringRef(s), Value::Number(n) => FieldValue::Number(n.as_f64().ok_or( DataFusionError::Execution(format!("Can't convert {:?} to float", n)), )?), - Value::Bool(b) => FieldValue::Bool(b), + Value::Bool(b) => FieldValue::Bool(*b), Value::Null => FieldValue::Null, x => { return Err(CubeError::user(format!( @@ -851,12 +851,15 @@ async fn load_data( .map(|v| v.iter().filter(|d| d.granularity.is_some()).count()) .unwrap_or(0) == 0; + let result = if no_members_query { let limit = request.limit.unwrap_or(1); let mut data = Vec::new(); + for _ in 0..limit { data.push(serde_json::Value::Null) } + V1LoadResult::new( V1LoadResultAnnotation { measures: json!(Vec::::new()), @@ -881,9 +884,9 @@ async fn load_data( data } else { - return Err(ArrowError::ComputeError(format!( - "Unable to extract result from Cube.js response", - ))); + return Err(ArrowError::ComputeError( + "Unable to extract result from Cube.js response, empty results".to_string(), + )); } }; @@ -945,6 +948,7 @@ pub fn transform_response( field_name, { (FieldValue::String(v), builder) => builder.append_value(v)?, + (FieldValue::StringRef(v), builder) => builder.append_value(v)?, (FieldValue::Bool(v), builder) => builder.append_value(if v { "true" } else { "false" })?, (FieldValue::Number(v), builder) => builder.append_value(v.to_string())?, }, @@ -961,7 +965,7 @@ pub fn transform_response( field_name, { (FieldValue::Number(number), builder) => builder.append_value(number.round() as i16)?, - (FieldValue::String(s), builder) => match s.parse::() { + (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => match s.parse::() { Ok(v) => builder.append_value(v)?, Err(error) => { warn!( @@ -986,7 +990,7 @@ pub fn transform_response( field_name, { (FieldValue::Number(number), builder) => builder.append_value(number.round() as i32)?, - (FieldValue::String(s), builder) => match s.parse::() { + (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => match s.parse::() { Ok(v) => builder.append_value(v)?, Err(error) => { warn!( @@ -1011,7 +1015,7 @@ pub fn transform_response( field_name, { (FieldValue::Number(number), builder) => builder.append_value(number.round() as i64)?, - (FieldValue::String(s), builder) => match s.parse::() { + (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => match s.parse::() { Ok(v) => builder.append_value(v)?, Err(error) => { warn!( @@ -1036,7 +1040,7 @@ pub fn transform_response( field_name, { (FieldValue::Number(number), builder) => builder.append_value(number as f32)?, - (FieldValue::String(s), builder) => match s.parse::() { + (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => match s.parse::() { Ok(v) => builder.append_value(v)?, Err(error) => { warn!( @@ -1061,7 +1065,7 @@ pub fn transform_response( field_name, { (FieldValue::Number(number), builder) => builder.append_value(number)?, - (FieldValue::String(s), builder) => match s.parse::() { + (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => match s.parse::() { Ok(v) => builder.append_value(v)?, Err(error) => { warn!( @@ -1086,7 +1090,7 @@ pub fn transform_response( field_name, { (FieldValue::Bool(v), builder) => builder.append_value(v)?, - (FieldValue::String(v), builder) => match v.as_str() { + (FieldValue::String(ref v), builder) | (FieldValue::StringRef(&ref v), builder) => match v.as_str() { "true" | "1" => builder.append_value(true)?, "false" | "0" => builder.append_value(false)?, _ => { @@ -1108,7 +1112,7 @@ pub fn transform_response( response, field_name, { - (FieldValue::String(s), builder) => { + (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => { let timestamp = NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%dT%H:%M:%S%.f") .or_else(|_| NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%d %H:%M:%S%.f")) .or_else(|_| NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%dT%H:%M:%S")) @@ -1144,7 +1148,7 @@ pub fn transform_response( response, field_name, { - (FieldValue::String(s), builder) => { + (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => { let timestamp = NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%dT%H:%M:%S%.f") .or_else(|_| NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%d %H:%M:%S%.f")) .or_else(|_| NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%dT%H:%M:%S")) @@ -1180,7 +1184,7 @@ pub fn transform_response( response, field_name, { - (FieldValue::String(s), builder) => { + (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => { let date = NaiveDate::parse_from_str(s.as_str(), "%Y-%m-%d") // FIXME: temporary solution for cases when expected type is Date32 // but underlying data is a Timestamp @@ -1224,7 +1228,7 @@ pub fn transform_response( response, field_name, { - (FieldValue::String(s), builder) => { + (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => { let mut parts = s.split("."); match parts.next() { None => builder.append_null()?, From 2dba6ab1bd7d7815a50deb22580dd56be4dc051c Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Mon, 26 Aug 2024 15:11:24 +0200 Subject: [PATCH 2/5] chore: tests for strings --- .../cubesql/src/compile/engine/df/scan.rs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs index a9e53adf7197d..f33640fa40bde 100644 --- a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs +++ b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs @@ -478,11 +478,11 @@ impl ValueObject for JsonValueObject { Ok(self.rows.len()) } - fn get<'a>( - &'a mut self, + fn get( + &mut self, index: usize, field_name: &str, - ) -> std::result::Result, CubeError> { + ) -> std::result::Result { let as_object = if let Some(as_object) = self.rows[index].as_object() { as_object } else { @@ -1399,11 +1399,11 @@ mod tests { "timeDimensions": [] }, "data": [ - {"KibanaSampleDataEcommerce.count": null, "KibanaSampleDataEcommerce.maxPrice": null, "KibanaSampleDataEcommerce.isBool": null, "KibanaSampleDataEcommerce.orderDate": null}, - {"KibanaSampleDataEcommerce.count": 5, "KibanaSampleDataEcommerce.maxPrice": 5.05, "KibanaSampleDataEcommerce.isBool": true, "KibanaSampleDataEcommerce.orderDate": "2022-01-01 00:00:00.000"}, - {"KibanaSampleDataEcommerce.count": "5", "KibanaSampleDataEcommerce.maxPrice": "5.05", "KibanaSampleDataEcommerce.isBool": false, "KibanaSampleDataEcommerce.orderDate": "2023-01-01 00:00:00.000"}, - {"KibanaSampleDataEcommerce.count": null, "KibanaSampleDataEcommerce.maxPrice": null, "KibanaSampleDataEcommerce.isBool": "true", "KibanaSampleDataEcommerce.orderDate": "9999-12-31 00:00:00.000"}, - {"KibanaSampleDataEcommerce.count": null, "KibanaSampleDataEcommerce.maxPrice": null, "KibanaSampleDataEcommerce.isBool": "false", "KibanaSampleDataEcommerce.orderDate": null} + {"KibanaSampleDataEcommerce.count": null, "KibanaSampleDataEcommerce.maxPrice": null, "KibanaSampleDataEcommerce.isBool": null, "KibanaSampleDataEcommerce.orderDate": null, "KibanaSampleDataEcommerce.city": "City 1"}, + {"KibanaSampleDataEcommerce.count": 5, "KibanaSampleDataEcommerce.maxPrice": 5.05, "KibanaSampleDataEcommerce.isBool": true, "KibanaSampleDataEcommerce.orderDate": "2022-01-01 00:00:00.000", "KibanaSampleDataEcommerce.city": "City 2"}, + {"KibanaSampleDataEcommerce.count": "5", "KibanaSampleDataEcommerce.maxPrice": "5.05", "KibanaSampleDataEcommerce.isBool": false, "KibanaSampleDataEcommerce.orderDate": "2023-01-01 00:00:00.000", "KibanaSampleDataEcommerce.city": "City 3"}, + {"KibanaSampleDataEcommerce.count": null, "KibanaSampleDataEcommerce.maxPrice": null, "KibanaSampleDataEcommerce.isBool": "true", "KibanaSampleDataEcommerce.orderDate": "9999-12-31 00:00:00.000", "KibanaSampleDataEcommerce.city": "City 4"}, + {"KibanaSampleDataEcommerce.count": null, "KibanaSampleDataEcommerce.maxPrice": null, "KibanaSampleDataEcommerce.isBool": "false", "KibanaSampleDataEcommerce.orderDate": null, "KibanaSampleDataEcommerce.city": null} ] } "#; @@ -1479,6 +1479,7 @@ mod tests { DataType::Boolean, false, ), + Field::new("KibanaSampleDataEcommerce.city", DataType::Utf8, false), ])); let scan_node = CubeScanExecutionPlan { @@ -1502,6 +1503,7 @@ mod tests { dimensions: Some(vec![ "KibanaSampleDataEcommerce.isBool".to_string(), "KibanaSampleDataEcommerce.orderDate".to_string(), + "KibanaSampleDataEcommerce.city".to_string(), ]), segments: None, time_dimensions: None, @@ -1581,6 +1583,13 @@ mod tests { Some(false) ])) as ArrayRef, Arc::new(BooleanArray::from(vec![None, None, None, None, None,])) as ArrayRef, + Arc::new(StringArray::from(vec![ + Some("City 1"), + Some("City 2"), + Some("City 3"), + Some("City 4"), + None + ])) as ArrayRef, ], ) .unwrap() From eb8d5ef69697acecd0f5fa3e7c4ac02cc6b075fb Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Mon, 26 Aug 2024 15:18:59 +0200 Subject: [PATCH 3/5] chore: we are cube :) --- rust/cubesql/cubesql/src/compile/engine/df/scan.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs index f33640fa40bde..f3e8a39ddc33f 100644 --- a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs +++ b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs @@ -885,7 +885,7 @@ async fn load_data( data } else { return Err(ArrowError::ComputeError( - "Unable to extract result from Cube.js response, empty results".to_string(), + "Unable to extract results from response: results is empty".to_string(), )); } }; From 21103c12297fbf4252fff67e059a0b7078b2406e Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Mon, 26 Aug 2024 17:12:39 +0200 Subject: [PATCH 4/5] chore: use cow --- packages/cubejs-backend-native/src/stream.rs | 3 +- .../cubesql/src/compile/engine/df/scan.rs | 76 +++++++++---------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/packages/cubejs-backend-native/src/stream.rs b/packages/cubejs-backend-native/src/stream.rs index c05807e1a992d..181d2840b7146 100644 --- a/packages/cubejs-backend-native/src/stream.rs +++ b/packages/cubejs-backend-native/src/stream.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use cubesql::compile::engine::df::scan::{ transform_response, FieldValue, MemberField, RecordBatch, SchemaRef, ValueObject, }; @@ -211,7 +212,7 @@ impl ValueObject for JsValueObject<'_> { CubeError::user(format!("Can't get '{}' field value: {}", field_name, e)) })?; if let Ok(s) = value.downcast::(&mut self.cx) { - Ok(FieldValue::String(s.value(&mut self.cx))) + Ok(FieldValue::String(Cow::Owned(s.value(&mut self.cx)))) } else if let Ok(n) = value.downcast::(&mut self.cx) { Ok(FieldValue::Number(n.value(&mut self.cx))) } else if let Ok(b) = value.downcast::(&mut self.cx) { diff --git a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs index f3e8a39ddc33f..27dd997793e27 100644 --- a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs +++ b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs @@ -1,10 +1,3 @@ -use std::{ - any::Any, - fmt, - sync::Arc, - task::{Context, Poll}, -}; - use async_trait::async_trait; use cubeclient::models::{V1LoadRequestQuery, V1LoadResult, V1LoadResultAnnotation}; pub use datafusion::{ @@ -27,6 +20,13 @@ pub use datafusion::{ }; use futures::Stream; use log::warn; +use std::{ + any::Any, + borrow::Cow, + fmt, + sync::Arc, + task::{Context, Poll}, +}; use crate::{ compile::{ @@ -447,10 +447,11 @@ struct CubeScanExecutionPlan { #[derive(Debug)] pub enum FieldValue<'a> { - // Neon doesn't allow us to build string reference, because V8 uses UTF-16, while Rust uses UTF-8 - String(String), - // Best variant for us with strings, because we dont need to clone string - StringRef(&'a String), + // Why Cow? + // We use N-API via Neon (only for streaming), which doesn't allow us to build string reference, + // because V8 uses UTF-16 It allocates/converts a new strings while doing JsString.value() + // @see v8 WriteUtf8 for more details. Cow::Owned is used for this variant + String(Cow<'a, str>), Number(f64), Bool(bool), Null, @@ -483,9 +484,7 @@ impl ValueObject for JsonValueObject { index: usize, field_name: &str, ) -> std::result::Result { - let as_object = if let Some(as_object) = self.rows[index].as_object() { - as_object - } else { + let Some(as_object) = self.rows[index].as_object() else { return Err(CubeError::user(format!( "Unexpected response from Cube, row is not an object: {:?}", self.rows[index] @@ -495,7 +494,7 @@ impl ValueObject for JsonValueObject { let value = as_object.get(field_name).unwrap_or(&Value::Null); Ok(match value { - Value::String(s) => FieldValue::StringRef(s), + Value::String(s) => FieldValue::String(Cow::Borrowed(s)), Value::Number(n) => FieldValue::Number(n.as_f64().ok_or( DataFusionError::Execution(format!("Can't convert {:?} to float", n)), )?), @@ -948,7 +947,6 @@ pub fn transform_response( field_name, { (FieldValue::String(v), builder) => builder.append_value(v)?, - (FieldValue::StringRef(v), builder) => builder.append_value(v)?, (FieldValue::Bool(v), builder) => builder.append_value(if v { "true" } else { "false" })?, (FieldValue::Number(v), builder) => builder.append_value(v.to_string())?, }, @@ -965,7 +963,7 @@ pub fn transform_response( field_name, { (FieldValue::Number(number), builder) => builder.append_value(number.round() as i16)?, - (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => match s.parse::() { + (FieldValue::String(s), builder) => match s.parse::() { Ok(v) => builder.append_value(v)?, Err(error) => { warn!( @@ -990,7 +988,7 @@ pub fn transform_response( field_name, { (FieldValue::Number(number), builder) => builder.append_value(number.round() as i32)?, - (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => match s.parse::() { + (FieldValue::String(s), builder) => match s.parse::() { Ok(v) => builder.append_value(v)?, Err(error) => { warn!( @@ -1015,7 +1013,7 @@ pub fn transform_response( field_name, { (FieldValue::Number(number), builder) => builder.append_value(number.round() as i64)?, - (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => match s.parse::() { + (FieldValue::String(s), builder) => match s.parse::() { Ok(v) => builder.append_value(v)?, Err(error) => { warn!( @@ -1040,7 +1038,7 @@ pub fn transform_response( field_name, { (FieldValue::Number(number), builder) => builder.append_value(number as f32)?, - (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => match s.parse::() { + (FieldValue::String(s), builder) => match s.parse::() { Ok(v) => builder.append_value(v)?, Err(error) => { warn!( @@ -1065,7 +1063,7 @@ pub fn transform_response( field_name, { (FieldValue::Number(number), builder) => builder.append_value(number)?, - (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => match s.parse::() { + (FieldValue::String(s), builder) => match s.parse::() { Ok(v) => builder.append_value(v)?, Err(error) => { warn!( @@ -1090,7 +1088,7 @@ pub fn transform_response( field_name, { (FieldValue::Bool(v), builder) => builder.append_value(v)?, - (FieldValue::String(ref v), builder) | (FieldValue::StringRef(&ref v), builder) => match v.as_str() { + (FieldValue::String(v), builder) => match v.as_ref() { "true" | "1" => builder.append_value(true)?, "false" | "0" => builder.append_value(false)?, _ => { @@ -1112,13 +1110,13 @@ pub fn transform_response( response, field_name, { - (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => { - let timestamp = NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%dT%H:%M:%S%.f") - .or_else(|_| NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%d %H:%M:%S%.f")) - .or_else(|_| NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%dT%H:%M:%S")) - .or_else(|_| NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%dT%H:%M:%S%.fZ")) + (FieldValue::String(s), builder) => { + let timestamp = NaiveDateTime::parse_from_str(s.as_ref(), "%Y-%m-%dT%H:%M:%S%.f") + .or_else(|_| NaiveDateTime::parse_from_str(s.as_ref(), "%Y-%m-%d %H:%M:%S%.f")) + .or_else(|_| NaiveDateTime::parse_from_str(s.as_ref(), "%Y-%m-%dT%H:%M:%S")) + .or_else(|_| NaiveDateTime::parse_from_str(s.as_ref(), "%Y-%m-%dT%H:%M:%S%.fZ")) .or_else(|_| { - NaiveDate::parse_from_str(s.as_str(), "%Y-%m-%d").map(|date| { + NaiveDate::parse_from_str(s.as_ref(), "%Y-%m-%d").map(|date| { date.and_hms_opt(0, 0, 0).unwrap() }) }) @@ -1148,13 +1146,13 @@ pub fn transform_response( response, field_name, { - (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => { - let timestamp = NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%dT%H:%M:%S%.f") - .or_else(|_| NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%d %H:%M:%S%.f")) - .or_else(|_| NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%dT%H:%M:%S")) - .or_else(|_| NaiveDateTime::parse_from_str(s.as_str(), "%Y-%m-%dT%H:%M:%S%.fZ")) + (FieldValue::String(s), builder) => { + let timestamp = NaiveDateTime::parse_from_str(s.as_ref(), "%Y-%m-%dT%H:%M:%S%.f") + .or_else(|_| NaiveDateTime::parse_from_str(s.as_ref(), "%Y-%m-%d %H:%M:%S%.f")) + .or_else(|_| NaiveDateTime::parse_from_str(s.as_ref(), "%Y-%m-%dT%H:%M:%S")) + .or_else(|_| NaiveDateTime::parse_from_str(s.as_ref(), "%Y-%m-%dT%H:%M:%S%.fZ")) .or_else(|_| { - NaiveDate::parse_from_str(s.as_str(), "%Y-%m-%d").map(|date| { + NaiveDate::parse_from_str(s.as_ref(), "%Y-%m-%d").map(|date| { date.and_hms_opt(0, 0, 0).unwrap() }) }) @@ -1184,11 +1182,11 @@ pub fn transform_response( response, field_name, { - (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => { - let date = NaiveDate::parse_from_str(s.as_str(), "%Y-%m-%d") + (FieldValue::String(s), builder) => { + let date = NaiveDate::parse_from_str(s.as_ref(), "%Y-%m-%d") // FIXME: temporary solution for cases when expected type is Date32 // but underlying data is a Timestamp - .or_else(|_| NaiveDate::parse_from_str(s.as_str(), "%Y-%m-%dT00:00:00.000")) + .or_else(|_| NaiveDate::parse_from_str(s.as_ref(), "%Y-%m-%dT00:00:00.000")) .map_err(|e| { DataFusionError::Execution(format!( "Can't parse date: '{}': {}", @@ -1228,7 +1226,7 @@ pub fn transform_response( response, field_name, { - (FieldValue::String(ref s), builder) | (FieldValue::StringRef(&ref s), builder) => { + (FieldValue::String(s), builder) => { let mut parts = s.split("."); match parts.next() { None => builder.append_null()?, @@ -1460,6 +1458,8 @@ mod tests { #[tokio::test] async fn test_df_cube_scan_execute() { + assert_eq!(std::mem::size_of::(), 24); + let schema = Arc::new(Schema::new(vec![ Field::new("KibanaSampleDataEcommerce.count", DataType::Utf8, false), Field::new("KibanaSampleDataEcommerce.count", DataType::Utf8, false), From c9e0774e14f4e0ce508a4e201d6d4384a55d3441 Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Mon, 26 Aug 2024 17:26:41 +0200 Subject: [PATCH 5/5] chore: fmt --- packages/cubejs-backend-native/src/stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-backend-native/src/stream.rs b/packages/cubejs-backend-native/src/stream.rs index 181d2840b7146..9c7c45f2dcbbb 100644 --- a/packages/cubejs-backend-native/src/stream.rs +++ b/packages/cubejs-backend-native/src/stream.rs @@ -1,7 +1,7 @@ -use std::borrow::Cow; use cubesql::compile::engine::df::scan::{ transform_response, FieldValue, MemberField, RecordBatch, SchemaRef, ValueObject, }; +use std::borrow::Cow; use std::cell::RefCell; use std::future::Future;