@@ -2,7 +2,8 @@ use crate::{
22 query_result_transform:: { DBResponsePrimitive , DBResponseValue } ,
33 transport:: JsRawData ,
44} ;
5- use cubeshared:: codegen:: { root_as_http_message, HttpCommand } ;
5+ use cubeshared:: codegen:: { root_as_http_message_with_opts, HttpCommand } ;
6+ use flatbuffers:: VerifierOptions ;
67use indexmap:: IndexMap ;
78use neon:: prelude:: Finalize ;
89
@@ -12,7 +13,7 @@ pub enum ParseError {
1213 EmptyResultSet ,
1314 NullRow ,
1415 ColumnNameNotDefined ,
15- FlatBufferError ,
16+ FlatBufferError ( String ) ,
1617 ErrorMessage ( String ) ,
1718}
1819
@@ -23,7 +24,7 @@ impl std::fmt::Display for ParseError {
2324 ParseError :: EmptyResultSet => write ! ( f, "Empty resultSet" ) ,
2425 ParseError :: NullRow => write ! ( f, "Null row" ) ,
2526 ParseError :: ColumnNameNotDefined => write ! ( f, "Column name is not defined" ) ,
26- ParseError :: FlatBufferError => write ! ( f, "FlatBuffer parsing error" ) ,
27+ ParseError :: FlatBufferError ( msg ) => write ! ( f, "FlatBuffer parsing error: {}" , msg ) ,
2728 ParseError :: ErrorMessage ( msg) => write ! ( f, "Error: {}" , msg) ,
2829 }
2930 }
@@ -48,14 +49,18 @@ impl QueryResult {
4849 columns_pos : IndexMap :: new ( ) ,
4950 } ;
5051
51- let http_message =
52- root_as_http_message ( msg_data) . map_err ( |_| ParseError :: FlatBufferError ) ?;
52+ let mut opts = VerifierOptions :: default ( ) ;
53+ opts. max_tables = 10_000_000 ; // Support up to 10M tables
54+ opts. max_apparent_size = 1 << 31 ; // 2GB limit for large datasets
55+
56+ let http_message = root_as_http_message_with_opts ( & opts, msg_data)
57+ . map_err ( |err| ParseError :: FlatBufferError ( err. to_string ( ) ) ) ?;
5358
5459 match http_message. command_type ( ) {
5560 HttpCommand :: HttpError => {
56- let http_error = http_message
57- . command_as_http_error ( )
58- . ok_or ( ParseError :: FlatBufferError ) ?;
61+ let http_error = http_message. command_as_http_error ( ) . ok_or_else ( || {
62+ ParseError :: FlatBufferError ( "Failed to parse HttpError command" . to_string ( ) )
63+ } ) ?;
5964 let error_message = http_error. error ( ) . unwrap_or ( "Unknown error" ) . to_string ( ) ;
6065 Err ( ParseError :: ErrorMessage ( error_message) )
6166 }
@@ -145,3 +150,204 @@ impl QueryResult {
145150 } )
146151 }
147152}
153+
154+ #[ cfg( test) ]
155+ mod tests {
156+ use super :: * ;
157+ use cubeshared:: codegen:: {
158+ root_as_http_message_unchecked, HttpColumnValue , HttpColumnValueArgs , HttpCommand ,
159+ HttpMessage , HttpMessageArgs , HttpResultSet , HttpResultSetArgs , HttpRow , HttpRowArgs ,
160+ } ;
161+ use flatbuffers:: FlatBufferBuilder ;
162+
163+ /// Helper function to create a test HttpMessage with a given number of rows and columns
164+ fn create_test_message ( num_rows : usize , num_columns : usize ) -> Vec < u8 > {
165+ let mut builder = FlatBufferBuilder :: new ( ) ;
166+
167+ // Create column names
168+ let column_names: Vec < _ > = ( 0 ..num_columns)
169+ . map ( |i| builder. create_string ( & format ! ( "column_{}" , i) ) )
170+ . collect ( ) ;
171+
172+ // Create rows with values
173+ let mut rows_vec = Vec :: with_capacity ( num_rows) ;
174+ for row_idx in 0 ..num_rows {
175+ // Create column values for this row
176+ let mut values_vec = Vec :: with_capacity ( num_columns) ;
177+ for col_idx in 0 ..num_columns {
178+ let value_str = builder. create_string ( & format ! ( "row_{}_col_{}" , row_idx, col_idx) ) ;
179+ let col_value = HttpColumnValue :: create (
180+ & mut builder,
181+ & HttpColumnValueArgs {
182+ string_value : Some ( value_str) ,
183+ } ,
184+ ) ;
185+ values_vec. push ( col_value) ;
186+ }
187+
188+ let values_vector = builder. create_vector ( & values_vec) ;
189+ let row = HttpRow :: create (
190+ & mut builder,
191+ & HttpRowArgs {
192+ values : Some ( values_vector) ,
193+ } ,
194+ ) ;
195+ rows_vec. push ( row) ;
196+ }
197+
198+ // Create the result set
199+ let columns_vector = builder. create_vector ( & column_names) ;
200+ let rows_vector = builder. create_vector ( & rows_vec) ;
201+
202+ let result_set = HttpResultSet :: create (
203+ & mut builder,
204+ & HttpResultSetArgs {
205+ columns : Some ( columns_vector) ,
206+ rows : Some ( rows_vector) ,
207+ } ,
208+ ) ;
209+
210+ // Create the message
211+ let connection_id = builder. create_string ( "test_connection" ) ;
212+ let message = HttpMessage :: create (
213+ & mut builder,
214+ & HttpMessageArgs {
215+ message_id : 1 ,
216+ command_type : HttpCommand :: HttpResultSet ,
217+ command : Some ( result_set. as_union_value ( ) ) ,
218+ connection_id : Some ( connection_id) ,
219+ } ,
220+ ) ;
221+
222+ builder. finish ( message, None ) ;
223+ builder. finished_data ( ) . to_vec ( )
224+ }
225+
226+ #[ test]
227+ fn test_parse_small_result_set ( ) {
228+ // Small result set should work fine
229+ let msg_data = create_test_message ( 10 , 5 ) ;
230+ let result = QueryResult :: from_cubestore_fb ( & msg_data) ;
231+ assert ! ( result. is_ok( ) ) ;
232+
233+ let query_result = result. unwrap ( ) ;
234+ assert_eq ! ( query_result. columns. len( ) , 5 ) ;
235+ assert_eq ! ( query_result. rows. len( ) , 10 ) ;
236+ }
237+
238+ #[ test]
239+ fn test_parse_medium_result_set ( ) {
240+ // Medium result set: 1000 rows, 20 columns
241+ let msg_data = create_test_message ( 1000 , 20 ) ;
242+ let result = QueryResult :: from_cubestore_fb ( & msg_data) ;
243+ assert ! ( result. is_ok( ) ) ;
244+
245+ let query_result = result. unwrap ( ) ;
246+ assert_eq ! ( query_result. columns. len( ) , 20 ) ;
247+ assert_eq ! ( query_result. rows. len( ) , 1000 ) ;
248+ }
249+
250+ #[ test]
251+ fn test_parse_large_result_set ( ) {
252+ // Large result set: 10,000 rows, 30 columns
253+ // This should start showing verification issues
254+ let msg_data = create_test_message ( 10_000 , 30 ) ;
255+ let result = QueryResult :: from_cubestore_fb ( & msg_data) ;
256+ assert ! ( result. is_ok( ) ) ;
257+
258+ let query_result = result. unwrap ( ) ;
259+ assert_eq ! ( query_result. columns. len( ) , 30 ) ;
260+ assert_eq ! ( query_result. rows. len( ) , 10_000 ) ;
261+ }
262+
263+ #[ test]
264+ fn test_parse_very_large_result_set ( ) {
265+ // Very large result set: 33,000 rows, 40 columns
266+ let msg_data = create_test_message ( 33_000 , 40 ) ;
267+ let result = QueryResult :: from_cubestore_fb ( & msg_data) ;
268+ assert ! ( result. is_ok( ) ) ;
269+
270+ let query_result = result. unwrap ( ) ;
271+ assert_eq ! ( query_result. columns. len( ) , 40 ) ;
272+ assert_eq ! ( query_result. rows. len( ) , 33_000 ) ;
273+ }
274+
275+ #[ test]
276+ fn test_parse_huge_result_set ( ) {
277+ // Huge result set: 50,000 rows, 100 columns
278+ let msg_data = create_test_message ( 50_000 , 100 ) ;
279+ let result = QueryResult :: from_cubestore_fb ( & msg_data) ;
280+ assert ! ( result. is_ok( ) ) ;
281+
282+ let query_result = result. unwrap ( ) ;
283+ assert_eq ! ( query_result. columns. len( ) , 100 ) ;
284+ assert_eq ! ( query_result. rows. len( ) , 50_000 ) ;
285+ }
286+
287+ #[ test]
288+ fn test_compare_with_unchecked_parse ( ) {
289+ // Test to demonstrate that unchecked parsing would work
290+ let msg_data = create_test_message ( 33_000 , 40 ) ;
291+
292+ // Checked version (current implementation)
293+ let checked_result = QueryResult :: from_cubestore_fb ( & msg_data) ;
294+
295+ // Try unchecked version to verify the data itself is valid
296+ let unchecked_result = unsafe {
297+ let http_message = root_as_http_message_unchecked ( & msg_data) ;
298+ match http_message. command_type ( ) {
299+ HttpCommand :: HttpResultSet => {
300+ let result_set = http_message. command_as_http_result_set ( ) ;
301+ if let Some ( rs) = result_set {
302+ if let Some ( rows) = rs. rows ( ) {
303+ println ! ( "Unchecked parse found {} rows" , rows. len( ) ) ;
304+ Ok ( rows. len ( ) )
305+ } else {
306+ Err ( "No rows" )
307+ }
308+ } else {
309+ Err ( "No result set" )
310+ }
311+ }
312+ _ => Err ( "Wrong command type" ) ,
313+ }
314+ } ;
315+
316+ assert ! ( checked_result. is_ok( ) ) ;
317+ assert ! ( unchecked_result. is_ok( ) ) ;
318+ }
319+
320+ #[ test]
321+ fn test_parse_with_custom_verifier_options ( ) {
322+ use cubeshared:: codegen:: root_as_http_message_with_opts;
323+ use flatbuffers:: VerifierOptions ;
324+
325+ // Test that custom verifier options can handle large datasets
326+ let msg_data = create_test_message ( 33_000 , 40 ) ;
327+
328+ // Create custom verifier options with increased limits
329+ let mut opts = VerifierOptions :: default ( ) ;
330+ opts. max_tables = 10_000_000 ; // Support up to 10M tables
331+ opts. max_apparent_size = 1 << 31 ; // 2GB limit
332+
333+ // This should succeed with custom options
334+ let result = root_as_http_message_with_opts ( & opts, & msg_data) ;
335+
336+ match result {
337+ Ok ( http_message) => match http_message. command_type ( ) {
338+ HttpCommand :: HttpResultSet => {
339+ let result_set = http_message. command_as_http_result_set ( ) ;
340+ if let Some ( rs) = result_set {
341+ if let Some ( rows) = rs. rows ( ) {
342+ assert_eq ! ( rows. len( ) , 33_000 ) ;
343+ }
344+ }
345+ }
346+ _ => panic ! ( "Wrong command type" ) ,
347+ } ,
348+ Err ( e) => {
349+ panic ! ( "Failed to parse with custom verifier options: {:?}" , e) ;
350+ }
351+ }
352+ }
353+ }
0 commit comments