@@ -2,7 +2,9 @@ use crate::constants::ENFORCED_CONTRACT;
22use crate :: models:: { Column , Fault , Model , RuleMetadata } ;
33use crate :: rules:: _helpers:: evaluation:: { plain_select, root_select, sole_table_name, top_ctes} ;
44use crate :: rules:: models:: FaultCollector ;
5- use sqlparser:: ast:: { Expr , Query , Select , SelectItem , SetExpr , SetQuantifier , Spanned } ;
5+ use sqlparser:: ast:: {
6+ Expr , Query , Select , SelectItem , SetExpr , SetQuantifier , Spanned , TableFactor ,
7+ } ;
68
79#[ derive( Clone , Copy ) ]
810struct OutputScope < ' a > {
@@ -95,6 +97,9 @@ impl OutputRule<'_> {
9597 item,
9698 SelectItem :: Wildcard ( _) | SelectItem :: QualifiedWildcard ( _, _)
9799 ) {
100+ if proven_dependency_wildcard ( select, scope. columns ) {
101+ continue ;
102+ }
98103 self . push (
99104 Some ( source_position ( item) ) ,
100105 "wildcard output does not establish enforced contract types explicitly" . into ( ) ,
@@ -168,6 +173,29 @@ impl OutputRule<'_> {
168173 }
169174}
170175
176+ fn proven_dependency_wildcard ( select : & Select , columns : & [ Column ] ) -> bool {
177+ let Some ( source) = select. from . first ( ) . filter ( |_| select. from . len ( ) == 1 ) else {
178+ return false ;
179+ } ;
180+ if !plain_select ( select, true ) || !source. joins . is_empty ( ) {
181+ return false ;
182+ }
183+ let TableFactor :: Table {
184+ name,
185+ args : Some ( _) ,
186+ ..
187+ } = & source. relation
188+ else {
189+ return false ;
190+ } ;
191+ let dependency = name. to_string ( ) . to_ascii_lowercase ( ) ;
192+ matches ! ( dependency. as_str( ) , "__ref" | "__source" )
193+ && columns
194+ . iter ( )
195+ . filter ( |column| !column. data_type . is_empty ( ) )
196+ . all ( |column| column. type_proven )
197+ }
198+
171199fn final_cte_output_boundary < ' a > (
172200 query : & ' a Query ,
173201 columns : & [ Column ] ,
@@ -234,6 +262,9 @@ fn columns_in_output_order_with_missing(
234262 let Some ( output_names) = set_expression_output_names_with_missing ( set_expr) else {
235263 return columns. to_vec ( ) ;
236264 } ;
265+ if output_names. len ( ) == 1 && output_names[ 0 ] . is_none ( ) {
266+ return columns. to_vec ( ) ;
267+ }
237268 output_names
238269 . into_iter ( )
239270 . enumerate ( )
0 commit comments