@@ -460,29 +460,146 @@ pub enum AggFunc {
460460 External ( Arc < ExtFunc > ) ,
461461}
462462
463- #[ derive( Debug , Clone , Copy , PartialEq , Eq , strum:: EnumIter ) ]
463+ #[ derive( Debug , Clone , strum:: EnumIter ) ]
464464pub enum WindowFunc {
465465 RowNumber ,
466+ Rank ,
467+ DenseRank ,
468+ PercentRank ,
469+ CumeDist ,
470+ Ntile ,
471+ Lag ,
472+ Lead ,
473+ FirstValue ,
474+ LastValue ,
475+ NthValue ,
476+ #[ strum( disabled) ]
477+ External ( Arc < ExtFunc > ) ,
466478}
467479
468480impl WindowFunc {
481+ /// SQL name of this window function. Matches the strings used by
482+ /// `Display` so EXPLAIN output and error messages agree.
483+ pub fn as_str ( & self ) -> & ' static str {
484+ match self {
485+ Self :: RowNumber => "row_number" ,
486+ Self :: Rank => "rank" ,
487+ Self :: DenseRank => "dense_rank" ,
488+ Self :: PercentRank => "percent_rank" ,
489+ Self :: CumeDist => "cume_dist" ,
490+ Self :: Ntile => "ntile" ,
491+ Self :: Lag => "lag" ,
492+ Self :: Lead => "lead" ,
493+ Self :: FirstValue => "first_value" ,
494+ Self :: LastValue => "last_value" ,
495+ Self :: NthValue => "nth_value" ,
496+ Self :: External ( _) => unreachable ! (
497+ "WindowFunc::External is not constructible: ExtFunc has no Window variant"
498+ ) ,
499+ }
500+ }
501+
469502 pub fn arities ( & self ) -> & ' static [ i32 ] {
470503 match self {
471- Self :: RowNumber => & [ 0 ] ,
504+ Self :: RowNumber | Self :: Rank | Self :: DenseRank | Self :: PercentRank | Self :: CumeDist => {
505+ & [ 0 ]
506+ }
507+ Self :: Ntile | Self :: FirstValue | Self :: LastValue => & [ 1 ] ,
508+ Self :: NthValue => & [ 2 ] ,
509+ Self :: Lag | Self :: Lead => & [ 1 , 2 , 3 ] ,
510+ Self :: External ( _) => unreachable ! (
511+ "WindowFunc::External is not constructible: ExtFunc has no Window variant"
512+ ) ,
513+ }
514+ }
515+
516+ /// Whether name resolution + runtime dispatch are wired up. Stub variants
517+ /// must not be advertised via `pragma_function_list`, or introspection
518+ /// drifts ahead of the resolver and users get "no such function" when
519+ /// they try to call them.
520+ pub fn is_implemented ( & self ) -> bool {
521+ matches ! ( self , Self :: RowNumber )
522+ }
523+ }
524+
525+ impl PartialEq for WindowFunc {
526+ fn eq ( & self , other : & Self ) -> bool {
527+ match ( self , other) {
528+ ( Self :: RowNumber , Self :: RowNumber )
529+ | ( Self :: Rank , Self :: Rank )
530+ | ( Self :: DenseRank , Self :: DenseRank )
531+ | ( Self :: PercentRank , Self :: PercentRank )
532+ | ( Self :: CumeDist , Self :: CumeDist )
533+ | ( Self :: Ntile , Self :: Ntile )
534+ | ( Self :: Lag , Self :: Lag )
535+ | ( Self :: Lead , Self :: Lead )
536+ | ( Self :: FirstValue , Self :: FirstValue )
537+ | ( Self :: LastValue , Self :: LastValue )
538+ | ( Self :: NthValue , Self :: NthValue ) => true ,
539+ ( Self :: External ( a) , Self :: External ( b) ) => Arc :: ptr_eq ( a, b) ,
540+ _ => false ,
472541 }
473542 }
474543}
475544
545+ impl Eq for WindowFunc { }
546+
476547impl Deterministic for WindowFunc {
477548 fn is_deterministic ( & self ) -> bool {
478- true
549+ match self {
550+ Self :: RowNumber
551+ | Self :: Rank
552+ | Self :: DenseRank
553+ | Self :: PercentRank
554+ | Self :: CumeDist
555+ | Self :: Ntile
556+ | Self :: Lag
557+ | Self :: Lead
558+ | Self :: FirstValue
559+ | Self :: LastValue
560+ | Self :: NthValue => true ,
561+ Self :: External ( _) => unreachable ! (
562+ "WindowFunc::External is not constructible: ExtFunc has no Window variant"
563+ ) ,
564+ }
479565 }
480566}
481567
482568impl std:: fmt:: Display for WindowFunc {
483569 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
570+ f. write_str ( self . as_str ( ) )
571+ }
572+ }
573+
574+ /// Function reference used by AggStep / AggValue / AggFinal opcodes.
575+ /// Aggregates used in window context and pure window functions share the same
576+ /// step/value dispatch path; this enum carries which side of that split a
577+ /// particular call belongs to.
578+ #[ derive( Debug , Clone ) ]
579+ pub enum AccumulatorFunc {
580+ Agg ( AggFunc ) ,
581+ Window ( WindowFunc ) ,
582+ }
583+
584+ impl AccumulatorFunc {
585+ /// Extract the inner `AggFunc` when this kind is known to be an
586+ /// aggregate. `unreachable!`s on `Window(...)` — the only opcodes
587+ /// that carry an `AccumulatorFunc` are the AggStep / AggValue /
588+ /// AggFinal trio, and the call sites that emit those wrap aggregates
589+ /// only. A `Window` value reaching here is a planner bug.
590+ pub fn expect_agg ( & self ) -> & AggFunc {
484591 match self {
485- Self :: RowNumber => write ! ( f, "row_number" ) ,
592+ Self :: Agg ( f) => f,
593+ Self :: Window ( f) => {
594+ unreachable ! ( "window function {f} reached an aggregate-only dispatch path" )
595+ }
596+ }
597+ }
598+
599+ pub fn as_str ( & self ) -> & ' static str {
600+ match self {
601+ Self :: Agg ( f) => f. as_str ( ) ,
602+ Self :: Window ( f) => f. as_str ( ) ,
486603 }
487604 }
488605}
@@ -1721,8 +1838,11 @@ impl Func {
17211838 push ( f. to_string ( ) , "w" , f. arities ( ) , f. is_deterministic ( ) ) ;
17221839 }
17231840
1724- // Window functions.
1841+ // Window functions (skip stub variants until they're wired up) .
17251842 for f in WindowFunc :: iter ( ) {
1843+ if !f. is_implemented ( ) {
1844+ continue ;
1845+ }
17261846 push ( f. to_string ( ) , "w" , f. arities ( ) , f. is_deterministic ( ) ) ;
17271847 }
17281848
0 commit comments