@@ -1188,4 +1188,71 @@ mod tests {
11881188 assert_eq ! ( result. data. get( layer0_key) . unwrap( ) . height( ) , 3 ) ;
11891189 assert_eq ! ( result. data. get( layer1_key) . unwrap( ) . height( ) , 3 ) ;
11901190 }
1191+
1192+ /// Test that literal mappings survive stat transforms (e.g., histogram grouping).
1193+ ///
1194+ /// This tests the fix for issue #129 where literal aesthetic columns like
1195+ /// `'foo' AS stroke` were lost during stat transforms because they weren't
1196+ /// included in the GROUP BY clause.
1197+ #[ cfg( feature = "duckdb" ) ]
1198+ #[ test]
1199+ fn test_histogram_with_literal_mapping ( ) {
1200+ let reader = DuckDBReader :: from_connection_string ( "duckdb://memory" ) . unwrap ( ) ;
1201+
1202+ // Create test data
1203+ reader
1204+ . connection ( )
1205+ . execute (
1206+ "CREATE TABLE hist_literal_test AS SELECT RANDOM() * 100 as value FROM range(100)" ,
1207+ duckdb:: params![ ] ,
1208+ )
1209+ . unwrap ( ) ;
1210+
1211+ // Histogram with a literal stroke mapping - should preserve the literal column
1212+ let query = r#"
1213+ SELECT * FROM hist_literal_test
1214+ VISUALISE value AS x
1215+ DRAW histogram MAPPING 'foo' AS stroke
1216+ "# ;
1217+
1218+ let result = prepare_data_with_reader ( query, & reader) . unwrap ( ) ;
1219+
1220+ // Should have layer 0 data with binned results
1221+ assert ! ( result. data. contains_key( & naming:: layer_key( 0 ) ) ) ;
1222+ let layer_df = result. data . get ( & naming:: layer_key ( 0 ) ) . unwrap ( ) ;
1223+
1224+ // Should have prefixed aesthetic-named columns
1225+ let col_names: Vec < String > = layer_df
1226+ . get_column_names_str ( )
1227+ . iter ( )
1228+ . map ( |s| s. to_string ( ) )
1229+ . collect ( ) ;
1230+
1231+ let x_col = naming:: aesthetic_column ( "x" ) ;
1232+ let y_col = naming:: aesthetic_column ( "y" ) ;
1233+ let stroke_col = naming:: aesthetic_column ( "stroke" ) ;
1234+
1235+ assert ! (
1236+ col_names. contains( & x_col) ,
1237+ "Should have '{}' column: {:?}" ,
1238+ x_col,
1239+ col_names
1240+ ) ;
1241+ assert ! (
1242+ col_names. contains( & y_col) ,
1243+ "Should have '{}' column: {:?}" ,
1244+ y_col,
1245+ col_names
1246+ ) ;
1247+ // The literal stroke column should survive the stat transform
1248+ assert ! (
1249+ col_names. contains( & stroke_col) ,
1250+ "Should have '{}' column (literal mapping should survive stat transform): {:?}" ,
1251+ stroke_col,
1252+ col_names
1253+ ) ;
1254+
1255+ // Should have fewer rows than original (binned)
1256+ assert ! ( layer_df. height( ) < 100 ) ;
1257+ }
11911258}
0 commit comments