@@ -2293,5 +2293,257 @@ mod tests {
22932293 ) ;
22942294
22952295 assert ! ( !diff2. has_breaking_changes( ) ) ; // Just a rename
2296+
2297+ // Case 3: array<array<struct<x int>>> - doubly nested array with struct
2298+ let before3 = StructType :: new_unchecked ( [ create_field_with_id (
2299+ "matrix" ,
2300+ DataType :: Array ( Box :: new ( ArrayType :: new (
2301+ DataType :: Array ( Box :: new ( ArrayType :: new (
2302+ DataType :: try_struct_type ( [ create_field_with_id (
2303+ "x" ,
2304+ DataType :: INTEGER ,
2305+ false ,
2306+ 2 ,
2307+ ) ] )
2308+ . unwrap ( ) ,
2309+ false ,
2310+ ) ) ) ,
2311+ false ,
2312+ ) ) ) ,
2313+ false ,
2314+ 1 ,
2315+ ) ] ) ;
2316+
2317+ let after3 = StructType :: new_unchecked ( [ create_field_with_id (
2318+ "matrix" ,
2319+ DataType :: Array ( Box :: new ( ArrayType :: new (
2320+ DataType :: Array ( Box :: new ( ArrayType :: new (
2321+ DataType :: try_struct_type ( [
2322+ create_field_with_id ( "renamed_x" , DataType :: INTEGER , false , 2 ) , // Renamed!
2323+ create_field_with_id ( "y" , DataType :: INTEGER , true , 3 ) , // Added!
2324+ ] )
2325+ . unwrap ( ) ,
2326+ false ,
2327+ ) ) ) ,
2328+ false ,
2329+ ) ) ) ,
2330+ false ,
2331+ 1 ,
2332+ ) ] ) ;
2333+
2334+ let diff3 = SchemaDiffArgs {
2335+ before : & before3,
2336+ after : & after3,
2337+ }
2338+ . compute_diff ( )
2339+ . unwrap ( ) ;
2340+
2341+ assert_eq ! ( diff3. added_fields. len( ) , 1 ) ;
2342+ assert_eq ! ( diff3. removed_fields. len( ) , 0 ) ;
2343+ assert_eq ! ( diff3. updated_fields. len( ) , 1 ) ;
2344+ assert_eq ! (
2345+ diff3. added_fields[ 0 ] . path,
2346+ ColumnName :: new( [ "matrix" , "element" , "element" , "y" ] )
2347+ ) ;
2348+ assert_eq ! (
2349+ diff3. updated_fields[ 0 ] . path,
2350+ ColumnName :: new( [ "matrix" , "element" , "element" , "renamed_x" ] )
2351+ ) ;
2352+ assert_eq ! (
2353+ diff3. updated_fields[ 0 ] . change_type,
2354+ FieldChangeType :: Renamed
2355+ ) ;
2356+ assert ! ( !diff3. has_breaking_changes( ) ) ; // Rename and added nullable field
2357+
2358+ // Case 4: map<array<struct<a int>>, array<struct<b int>>> - map with arrays of structs
2359+ let before4 = StructType :: new_unchecked ( [ create_field_with_id (
2360+ "complex_map" ,
2361+ DataType :: Map ( Box :: new ( MapType :: new (
2362+ DataType :: Array ( Box :: new ( ArrayType :: new (
2363+ DataType :: try_struct_type ( [ create_field_with_id (
2364+ "key_field" ,
2365+ DataType :: INTEGER ,
2366+ false ,
2367+ 2 ,
2368+ ) ] )
2369+ . unwrap ( ) ,
2370+ false ,
2371+ ) ) ) ,
2372+ DataType :: Array ( Box :: new ( ArrayType :: new (
2373+ DataType :: try_struct_type ( [ create_field_with_id (
2374+ "value_field" ,
2375+ DataType :: STRING ,
2376+ false ,
2377+ 3 ,
2378+ ) ] )
2379+ . unwrap ( ) ,
2380+ false ,
2381+ ) ) ) ,
2382+ false ,
2383+ ) ) ) ,
2384+ false ,
2385+ 1 ,
2386+ ) ] ) ;
2387+
2388+ let after4 = StructType :: new_unchecked ( [ create_field_with_id (
2389+ "complex_map" ,
2390+ DataType :: Map ( Box :: new ( MapType :: new (
2391+ DataType :: Array ( Box :: new ( ArrayType :: new (
2392+ DataType :: try_struct_type ( [
2393+ create_field_with_id ( "renamed_key_field" , DataType :: INTEGER , false , 2 ) , // Renamed!
2394+ ] )
2395+ . unwrap ( ) ,
2396+ false ,
2397+ ) ) ) ,
2398+ DataType :: Array ( Box :: new ( ArrayType :: new (
2399+ DataType :: try_struct_type ( [
2400+ create_field_with_id ( "renamed_value_field" , DataType :: STRING , false , 3 ) , // Renamed!
2401+ ] )
2402+ . unwrap ( ) ,
2403+ false ,
2404+ ) ) ) ,
2405+ false ,
2406+ ) ) ) ,
2407+ false ,
2408+ 1 ,
2409+ ) ] ) ;
2410+
2411+ let diff4 = SchemaDiffArgs {
2412+ before : & before4,
2413+ after : & after4,
2414+ }
2415+ . compute_diff ( )
2416+ . unwrap ( ) ;
2417+
2418+ assert_eq ! ( diff4. added_fields. len( ) , 0 ) ;
2419+ assert_eq ! ( diff4. removed_fields. len( ) , 0 ) ;
2420+ assert_eq ! ( diff4. updated_fields. len( ) , 2 ) ;
2421+
2422+ let paths: HashSet < ColumnName > = diff4
2423+ . updated_fields
2424+ . iter ( )
2425+ . map ( |u| u. path . clone ( ) )
2426+ . collect ( ) ;
2427+ assert ! ( paths. contains( & ColumnName :: new( [
2428+ "complex_map" ,
2429+ "key" ,
2430+ "element" ,
2431+ "renamed_key_field"
2432+ ] ) ) ) ;
2433+ assert ! ( paths. contains( & ColumnName :: new( [
2434+ "complex_map" ,
2435+ "value" ,
2436+ "element" ,
2437+ "renamed_value_field"
2438+ ] ) ) ) ;
2439+ assert ! ( !diff4. has_breaking_changes( ) ) ; // Just renames
2440+
2441+ // Case 5: map<struct<id int>, map<struct<key int>, struct<data string>>> - map with nested map and structs
2442+ let before5 = StructType :: new_unchecked ( [ create_field_with_id (
2443+ "nested_maps" ,
2444+ DataType :: Map ( Box :: new ( MapType :: new (
2445+ DataType :: try_struct_type ( [ create_field_with_id (
2446+ "outer_key" ,
2447+ DataType :: INTEGER ,
2448+ false ,
2449+ 2 ,
2450+ ) ] )
2451+ . unwrap ( ) ,
2452+ DataType :: Map ( Box :: new ( MapType :: new (
2453+ DataType :: try_struct_type ( [ create_field_with_id (
2454+ "inner_key" ,
2455+ DataType :: INTEGER ,
2456+ false ,
2457+ 3 ,
2458+ ) ] )
2459+ . unwrap ( ) ,
2460+ DataType :: try_struct_type ( [
2461+ create_field_with_id ( "data" , DataType :: STRING , false , 4 ) ,
2462+ create_field_with_id ( "removed" , DataType :: INTEGER , true , 5 ) ,
2463+ ] )
2464+ . unwrap ( ) ,
2465+ false ,
2466+ ) ) ) ,
2467+ false ,
2468+ ) ) ) ,
2469+ false ,
2470+ 1 ,
2471+ ) ] ) ;
2472+
2473+ let after5 = StructType :: new_unchecked ( [ create_field_with_id (
2474+ "nested_maps" ,
2475+ DataType :: Map ( Box :: new ( MapType :: new (
2476+ DataType :: try_struct_type ( [ create_field_with_id (
2477+ "renamed_outer_key" , // Renamed!
2478+ DataType :: INTEGER ,
2479+ false ,
2480+ 2 ,
2481+ ) ] )
2482+ . unwrap ( ) ,
2483+ DataType :: Map ( Box :: new ( MapType :: new (
2484+ DataType :: try_struct_type ( [ create_field_with_id (
2485+ "renamed_inner_key" , // Renamed!
2486+ DataType :: INTEGER ,
2487+ false ,
2488+ 3 ,
2489+ ) ] )
2490+ . unwrap ( ) ,
2491+ DataType :: try_struct_type ( [
2492+ create_field_with_id ( "renamed_data" , DataType :: STRING , false , 4 ) , // Renamed!
2493+ create_field_with_id ( "added" , DataType :: LONG , true , 6 ) , // Added!
2494+ ] )
2495+ . unwrap ( ) ,
2496+ false ,
2497+ ) ) ) ,
2498+ false ,
2499+ ) ) ) ,
2500+ false ,
2501+ 1 ,
2502+ ) ] ) ;
2503+
2504+ let diff5 = SchemaDiffArgs {
2505+ before : & before5,
2506+ after : & after5,
2507+ }
2508+ . compute_diff ( )
2509+ . unwrap ( ) ;
2510+
2511+ assert_eq ! ( diff5. added_fields. len( ) , 1 ) ;
2512+ assert_eq ! ( diff5. removed_fields. len( ) , 1 ) ;
2513+ assert_eq ! ( diff5. updated_fields. len( ) , 3 ) ;
2514+
2515+ assert_eq ! (
2516+ diff5. added_fields[ 0 ] . path,
2517+ ColumnName :: new( [ "nested_maps" , "value" , "value" , "added" ] )
2518+ ) ;
2519+ assert_eq ! (
2520+ diff5. removed_fields[ 0 ] . path,
2521+ ColumnName :: new( [ "nested_maps" , "value" , "value" , "removed" ] )
2522+ ) ;
2523+
2524+ let paths: HashSet < ColumnName > = diff5
2525+ . updated_fields
2526+ . iter ( )
2527+ . map ( |u| u. path . clone ( ) )
2528+ . collect ( ) ;
2529+ assert ! ( paths. contains( & ColumnName :: new( [
2530+ "nested_maps" ,
2531+ "key" ,
2532+ "renamed_outer_key"
2533+ ] ) ) ) ;
2534+ assert ! ( paths. contains( & ColumnName :: new( [
2535+ "nested_maps" ,
2536+ "value" ,
2537+ "key" ,
2538+ "renamed_inner_key"
2539+ ] ) ) ) ;
2540+ assert ! ( paths. contains( & ColumnName :: new( [
2541+ "nested_maps" ,
2542+ "value" ,
2543+ "value" ,
2544+ "renamed_data"
2545+ ] ) ) ) ;
2546+
2547+ assert ! ( diff5. has_breaking_changes( ) ) ; // Removal is breaking
22962548 }
22972549}
0 commit comments