@@ -308,11 +308,8 @@ impl<'a> SqlGenerator<'a> {
308308 let available: Vec < & str > = self . graph . models ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
309309 SidemanticError :: model_not_found ( & model_name, & available)
310310 } ) ?;
311- let metric = model. get_metric ( & metric_name) . ok_or_else ( || {
312- let available: Vec < & str > = model. metrics . iter ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
313- SidemanticError :: metric_not_found ( & model_name, & metric_name, & available)
314- } ) ?;
315- let raw_alias = format ! ( "{metric_name}_raw" ) ;
311+ let metric = self . metric_for_model ( & model_name, & metric_name) ?;
312+ let raw_alias = self . metric_raw_alias ( model, & metric_name, metric) ;
316313 let mut raw_expr =
317314 self . normalize_cte_source_expression ( & self . metric_raw_expression ( metric, model) ) ;
318315 if !metric. filters . is_empty ( ) {
@@ -457,16 +454,13 @@ impl<'a> SqlGenerator<'a> {
457454 let available: Vec < & str > = self . graph . models ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
458455 SidemanticError :: model_not_found ( & metric_ref. model , & available)
459456 } ) ?;
460- let metric = model. get_metric ( & metric_ref. name ) . ok_or_else ( || {
461- let available: Vec < & str > = model. metrics . iter ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
462- SidemanticError :: metric_not_found ( & metric_ref. model , & metric_ref. name , & available)
463- } ) ?;
457+ let metric = self . metric_for_model ( & metric_ref. model , & metric_ref. name ) ?;
464458
465459 let alias = self . model_alias ( & metric_ref. model ) ;
466460 let use_symmetric = fan_out_at_risk. contains ( & metric_ref. model ) ;
467461 let output_alias =
468462 self . output_alias ( & metric_ref. model , & metric_ref. alias , & alias_collisions) ;
469- let raw_alias = format ! ( "{}_raw" , metric_ref. name) ;
463+ let raw_alias = self . metric_raw_alias ( model , & metric_ref. name , metric ) ;
470464 let raw_col = format ! ( "{alias}.{}" , self . quote_identifier( & raw_alias) ) ;
471465
472466 let sql_expr = match metric. r#type {
@@ -793,12 +787,136 @@ impl<'a> SqlGenerator<'a> {
793787 }
794788
795789 match owners. len ( ) {
796- 0 => Ok ( None ) ,
797- 1 => Ok ( Some ( ( owners[ 0 ] . clone ( ) , reference. to_string ( ) ) ) ) ,
798- _ => Err ( SidemanticError :: InvalidReference {
790+ 0 => { }
791+ 1 => return Ok ( Some ( ( owners[ 0 ] . clone ( ) , reference. to_string ( ) ) ) ) ,
792+ _ => {
793+ return Err ( SidemanticError :: InvalidReference {
794+ reference : reference. to_string ( ) ,
795+ } ) ;
796+ }
797+ }
798+
799+ if let Some ( metric) = self . graph . get_metric ( reference) {
800+ let graph_metric_owners = self . graph_metric_owner_models ( reference, metric) ?;
801+ return match graph_metric_owners. len ( ) {
802+ 0 => Ok ( None ) ,
803+ 1 => Ok ( Some ( (
804+ graph_metric_owners[ 0 ] . clone ( ) ,
805+ reference. to_string ( ) ,
806+ ) ) ) ,
807+ _ => Err ( SidemanticError :: InvalidReference {
808+ reference : reference. to_string ( ) ,
809+ } ) ,
810+ } ;
811+ }
812+
813+ Ok ( None )
814+ }
815+
816+ fn graph_metric_owner_models ( & self , reference : & str , metric : & Metric ) -> Result < Vec < String > > {
817+ let mut owners = HashSet :: new ( ) ;
818+ for model in self . graph . models ( ) {
819+ if model. get_metric ( reference) . is_some ( ) {
820+ owners. insert ( model. name . clone ( ) ) ;
821+ }
822+ }
823+
824+ if owners. is_empty ( ) {
825+ for fragment in [
826+ metric. sql . as_deref ( ) ,
827+ metric. numerator . as_deref ( ) ,
828+ metric. denominator . as_deref ( ) ,
829+ metric. base_metric . as_deref ( ) ,
830+ metric. entity . as_deref ( ) ,
831+ metric. base_event . as_deref ( ) ,
832+ metric. conversion_event . as_deref ( ) ,
833+ metric. cohort_event . as_deref ( ) ,
834+ metric. activity_event . as_deref ( ) ,
835+ metric. having . as_deref ( ) ,
836+ ]
837+ . into_iter ( )
838+ . flatten ( )
839+ {
840+ self . collect_owner_models_from_fragment ( fragment, & mut owners) ;
841+ }
842+
843+ if let Some ( steps) = metric. steps . as_ref ( ) {
844+ for step in steps {
845+ self . collect_owner_models_from_fragment ( step, & mut owners) ;
846+ }
847+ }
848+ if let Some ( inner_metrics) = metric. inner_metrics . as_ref ( ) {
849+ for inner_metric in inner_metrics {
850+ if let Some ( sql) = inner_metric. sql . as_deref ( ) {
851+ self . collect_owner_models_from_fragment ( sql, & mut owners) ;
852+ }
853+ }
854+ }
855+ if let Some ( entity_dimensions) = metric. entity_dimensions . as_ref ( ) {
856+ for dimension in entity_dimensions {
857+ self . collect_owner_models_from_fragment ( dimension, & mut owners) ;
858+ }
859+ }
860+ }
861+
862+ if owners. is_empty ( ) {
863+ let mut model_names: Vec < String > = self
864+ . graph
865+ . models ( )
866+ . map ( |model| model. name . clone ( ) )
867+ . collect ( ) ;
868+ if model_names. len ( ) == 1 {
869+ owners. insert ( model_names. pop ( ) . expect ( "single model name" ) ) ;
870+ }
871+ }
872+
873+ let mut owners: Vec < String > = owners. into_iter ( ) . collect ( ) ;
874+ owners. sort ( ) ;
875+ if owners. len ( ) > 1 {
876+ return Err ( SidemanticError :: InvalidReference {
799877 reference : reference. to_string ( ) ,
800- } ) ,
878+ } ) ;
879+ }
880+ Ok ( owners)
881+ }
882+
883+ fn collect_owner_models_from_fragment ( & self , fragment : & str , owners : & mut HashSet < String > ) {
884+ let model_ref_re =
885+ regex:: Regex :: new ( r"\b([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)\b" )
886+ . expect ( "valid model reference regex" ) ;
887+ for cap in model_ref_re. captures_iter ( fragment) {
888+ let Some ( model_match) = cap. get ( 1 ) else {
889+ continue ;
890+ } ;
891+ let model_name = model_match. as_str ( ) ;
892+ if self . graph . get_model ( model_name) . is_some ( ) {
893+ owners. insert ( model_name. to_string ( ) ) ;
894+ }
895+ }
896+ }
897+
898+ fn metric_for_model ( & self , model_name : & str , metric_name : & str ) -> Result < & Metric > {
899+ let model = self . graph . get_model ( model_name) . ok_or_else ( || {
900+ let available: Vec < & str > = self . graph . models ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
901+ SidemanticError :: model_not_found ( model_name, & available)
902+ } ) ?;
903+ if let Some ( metric) = model. get_metric ( metric_name) {
904+ return Ok ( metric) ;
905+ }
906+
907+ if let Some ( metric) = self . graph . get_metric ( metric_name) {
908+ let owners = self . graph_metric_owner_models ( metric_name, metric) ?;
909+ if owners. iter ( ) . any ( |owner| owner == model_name) {
910+ return Ok ( metric) ;
911+ }
801912 }
913+
914+ let available: Vec < & str > = model. metrics . iter ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
915+ Err ( SidemanticError :: metric_not_found (
916+ model_name,
917+ metric_name,
918+ & available,
919+ ) )
802920 }
803921
804922 /// Find all models required by the query
@@ -852,14 +970,7 @@ impl<'a> SqlGenerator<'a> {
852970 return Ok ( ( ) ) ;
853971 }
854972
855- let model = self . graph . get_model ( & metric_ref. model ) . ok_or_else ( || {
856- let available: Vec < & str > = self . graph . models ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
857- SidemanticError :: model_not_found ( & metric_ref. model , & available)
858- } ) ?;
859- let metric = model. get_metric ( & metric_ref. name ) . ok_or_else ( || {
860- let available: Vec < & str > = model. metrics . iter ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
861- SidemanticError :: metric_not_found ( & metric_ref. model , & metric_ref. name , & available)
862- } ) ?;
973+ let metric = self . metric_for_model ( & metric_ref. model , & metric_ref. name ) ?;
863974
864975 let exprs: Vec < & str > = [
865976 metric. sql . as_deref ( ) ,
@@ -991,6 +1102,29 @@ impl<'a> SqlGenerator<'a> {
9911102 }
9921103 }
9931104
1105+ fn metric_raw_alias ( & self , model : & Model , metric_name : & str , metric : & Metric ) -> String {
1106+ if metric_name. contains ( '.' ) && metric. r#type == MetricType :: Simple {
1107+ if let Some ( column_name) = self . simple_metric_source_column ( model, metric) {
1108+ return column_name;
1109+ }
1110+ }
1111+ format ! ( "{metric_name}_raw" )
1112+ }
1113+
1114+ fn simple_metric_source_column ( & self , model : & Model , metric : & Metric ) -> Option < String > {
1115+ let sql = metric. sql . as_deref ( ) ?. trim ( ) ;
1116+ if Self :: is_simple_identifier ( sql) {
1117+ return Some ( sql. to_string ( ) ) ;
1118+ }
1119+
1120+ let ( model_name, field_name) = sql. split_once ( '.' ) ?;
1121+ if model_name == model. name && Self :: is_simple_identifier ( field_name) {
1122+ return Some ( field_name. to_string ( ) ) ;
1123+ }
1124+
1125+ None
1126+ }
1127+
9941128 fn collect_simple_metric_dependencies (
9951129 & self ,
9961130 metric_ref : & MetricRef ,
@@ -1002,14 +1136,7 @@ impl<'a> SqlGenerator<'a> {
10021136 return Ok ( ( ) ) ;
10031137 }
10041138
1005- let model = self . graph . get_model ( & metric_ref. model ) . ok_or_else ( || {
1006- let available: Vec < & str > = self . graph . models ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
1007- SidemanticError :: model_not_found ( & metric_ref. model , & available)
1008- } ) ?;
1009- let metric = model. get_metric ( & metric_ref. name ) . ok_or_else ( || {
1010- let available: Vec < & str > = model. metrics . iter ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
1011- SidemanticError :: metric_not_found ( & metric_ref. model , & metric_ref. name , & available)
1012- } ) ?;
1139+ let metric = self . metric_for_model ( & metric_ref. model , & metric_ref. name ) ?;
10131140
10141141 match metric. r#type {
10151142 MetricType :: Simple => {
@@ -1090,14 +1217,7 @@ impl<'a> SqlGenerator<'a> {
10901217 return Ok ( ( ) ) ;
10911218 }
10921219
1093- let model = self . graph . get_model ( & metric_ref. model ) . ok_or_else ( || {
1094- let available: Vec < & str > = self . graph . models ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
1095- SidemanticError :: model_not_found ( & metric_ref. model , & available)
1096- } ) ?;
1097- let metric = model. get_metric ( & metric_ref. name ) . ok_or_else ( || {
1098- let available: Vec < & str > = model. metrics . iter ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
1099- SidemanticError :: metric_not_found ( & metric_ref. model , & metric_ref. name , & available)
1100- } ) ?;
1220+ let metric = self . metric_for_model ( & metric_ref. model , & metric_ref. name ) ?;
11011221
11021222 match metric. r#type {
11031223 MetricType :: Derived if Self :: is_inline_aggregate_expression ( metric. sql_expr ( ) ) => {
@@ -1356,14 +1476,7 @@ impl<'a> SqlGenerator<'a> {
13561476
13571477 fn has_cumulative_metrics ( & self , metric_refs : & [ MetricRef ] ) -> Result < bool > {
13581478 for metric_ref in metric_refs {
1359- let model = self . graph . get_model ( & metric_ref. model ) . ok_or_else ( || {
1360- let available: Vec < & str > = self . graph . models ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
1361- SidemanticError :: model_not_found ( & metric_ref. model , & available)
1362- } ) ?;
1363- let metric = model. get_metric ( & metric_ref. name ) . ok_or_else ( || {
1364- let available: Vec < & str > = model. metrics . iter ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
1365- SidemanticError :: metric_not_found ( & metric_ref. model , & metric_ref. name , & available)
1366- } ) ?;
1479+ let metric = self . metric_for_model ( & metric_ref. model , & metric_ref. name ) ?;
13671480 if metric. r#type == MetricType :: Cumulative
13681481 || metric. r#type == MetricType :: TimeComparison
13691482 || metric. r#type == MetricType :: Conversion
@@ -4029,7 +4142,8 @@ impl<'a> SqlGenerator<'a> {
40294142 metric_name : & str ,
40304143 alias : & str ,
40314144 ) -> String {
4032- let raw_col = format ! ( "{alias}.{metric_name}_raw" ) ;
4145+ let raw_alias = format ! ( "{metric_name}_raw" ) ;
4146+ let raw_col = format ! ( "{alias}.{}" , self . quote_identifier( & raw_alias) ) ;
40334147 match metric. agg . as_ref ( ) {
40344148 Some ( Aggregation :: CountDistinct ) => format ! ( "COUNT(DISTINCT {raw_col})" ) ,
40354149 Some ( Aggregation :: Count ) => format ! ( "COUNT({raw_col})" ) ,
@@ -4078,14 +4192,7 @@ impl<'a> SqlGenerator<'a> {
40784192 return Ok ( None ) ;
40794193 }
40804194
4081- let model = self . graph . get_model ( & model_name) . ok_or_else ( || {
4082- let available: Vec < & str > = self . graph . models ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
4083- SidemanticError :: model_not_found ( & model_name, & available)
4084- } ) ?;
4085- let metric = model. get_metric ( & metric_name) . ok_or_else ( || {
4086- let available: Vec < & str > = model. metrics . iter ( ) . map ( |m| m. name . as_str ( ) ) . collect ( ) ;
4087- SidemanticError :: metric_not_found ( & model_name, & metric_name, & available)
4088- } ) ?;
4195+ let metric = self . metric_for_model ( & model_name, & metric_name) ?;
40894196
40904197 let alias = self . model_alias ( & model_name) ;
40914198 let expanded = match metric. r#type {
0 commit comments