@@ -412,7 +412,7 @@ fn starts_with_definition_keyword(sql: &str, keyword: &str) -> bool {
412412 trimmed[ keyword. len ( ) ..]
413413 . chars ( )
414414 . next ( )
415- . map ( |ch| ch. is_whitespace ( ) )
415+ . map ( |ch| ch. is_whitespace ( ) || ch == '(' )
416416 . unwrap_or ( true )
417417}
418418
@@ -461,7 +461,7 @@ fn keyword_matches_at(bytes: &[u8], idx: usize, keyword: &[u8]) -> bool {
461461
462462 bytes
463463 . get ( idx + keyword. len ( ) )
464- . map ( |byte| byte. is_ascii_whitespace ( ) )
464+ . map ( |byte| byte. is_ascii_whitespace ( ) || * byte == b'(' )
465465 . unwrap_or ( true )
466466}
467467
@@ -1887,6 +1887,56 @@ models:
18871887 remove_definitions_file ( & db_path) ;
18881888 }
18891889
1890+ #[ test]
1891+ fn test_compact_parenthesized_definitions_are_detected_for_persistence_updates ( ) {
1892+ let _guard = test_lock ( ) ;
1893+ sidemantic_clear ( ) ;
1894+
1895+ let db_path = unique_db_path ( "compact_parenthesized_persistence" ) ;
1896+ let db_path = CString :: new ( db_path. to_string_lossy ( ) . to_string ( ) ) . unwrap ( ) ;
1897+ remove_definitions_file ( & db_path) ;
1898+ let definitions_path = get_definitions_path ( db_path. as_ptr ( ) ) . unwrap ( ) ;
1899+
1900+ let model = CString :: new ( "MODEL(name orders, table orders, primary_key order_id)" ) . unwrap ( ) ;
1901+ assert_success ( sidemantic_define ( model. as_ptr ( ) , db_path. as_ptr ( ) , false ) ) ;
1902+
1903+ let old_metric = CString :: new ( "METRIC(name revenue, agg sum, sql gross_amount)" ) . unwrap ( ) ;
1904+ assert_success ( sidemantic_add_definition (
1905+ old_metric. as_ptr ( ) ,
1906+ db_path. as_ptr ( ) ,
1907+ false ,
1908+ ) ) ;
1909+
1910+ let new_metric = CString :: new ( "METRIC(name revenue, agg sum, sql net_amount)" ) . unwrap ( ) ;
1911+ assert_success ( sidemantic_add_definition (
1912+ new_metric. as_ptr ( ) ,
1913+ db_path. as_ptr ( ) ,
1914+ true ,
1915+ ) ) ;
1916+
1917+ let content = fs:: read_to_string ( & definitions_path) . unwrap ( ) ;
1918+ assert_eq ! (
1919+ content. matches( "METRIC(name revenue" ) . count( ) ,
1920+ 1 ,
1921+ "{content}"
1922+ ) ;
1923+ assert ! ( !content. contains( "gross_amount" ) , "{content}" ) ;
1924+ assert ! ( content. contains( "net_amount" ) , "{content}" ) ;
1925+
1926+ sidemantic_clear ( ) ;
1927+ assert_success ( sidemantic_autoload ( db_path. as_ptr ( ) ) ) ;
1928+
1929+ let rewritten = take_rewrite_sql ( sidemantic_rewrite (
1930+ CString :: new ( "SELECT orders.revenue FROM orders" )
1931+ . unwrap ( )
1932+ . as_ptr ( ) ,
1933+ ) ) ;
1934+ assert ! ( rewritten. contains( "net_amount" ) , "{rewritten}" ) ;
1935+ assert ! ( !rewritten. contains( "gross_amount" ) , "{rewritten}" ) ;
1936+
1937+ remove_definitions_file ( & db_path) ;
1938+ }
1939+
18901940 #[ test]
18911941 fn test_prefixed_definition_persists_under_target_model_block ( ) {
18921942 let _guard = test_lock ( ) ;
0 commit comments