@@ -25,21 +25,23 @@ import (
2525 pgtypes "github.com/dolthub/doltgresql/server/types"
2626)
2727
28- // AggCast wraps a sql.Aggregation to override its declared return type and
29- // post-convert the buffer's Eval result. It preserves the sql.Aggregation
30- // interface so GroupBy aggregation machinery works correctly.
28+ // AggCast wraps a sql.Aggregation to override its declared return type and post-convert
29+ // its result, whether reached through the GroupBy buffer path (NewBuffer/Eval) or the
30+ // window function path (NewWindowFunction/Compute). It preserves both the sql.Aggregation
31+ // and sql.WindowAdaptableExpression interfaces so GroupBy and window execution both work.
3132//
3233// GMS SUM and AVG over integer columns always accumulate as float64 internally, but
3334// Postgres specifies SUM(int2/int4/int8) → bigint and AVG(int2/int4/int8) → numeric.
34- // AggCast intercepts the float64 buffer result and converts it to the target type so
35- // the correct wire type is used.
35+ // AggCast intercepts the float64 result and converts it to the target type so the
36+ // correct wire type is used.
3637type AggCast struct {
3738 inner sql.Aggregation
3839 targetType * pgtypes.DoltgresType
3940}
4041
4142var _ sql.Expression = (* AggCast )(nil )
4243var _ sql.Aggregation = (* AggCast )(nil )
44+ var _ sql.WindowFunction = (* aggCastWindowFunction )(nil )
4345
4446// NewAggCast wraps inner so that its declared type is targetType and its buffer
4547// Eval result is converted from float64 to int64.
@@ -59,9 +61,14 @@ func (a *AggCast) NewBuffer(ctx *sql.Context) (sql.AggregationBuffer, error) {
5961 return & aggCastBuffer {inner : buf , targetType : a .targetType }, nil
6062}
6163
62- // NewWindowFunction delegates to inner.
64+ // NewWindowFunction delegates to inner but wraps the result so Compute's float64 output is
65+ // converted to match targetType, the same way NewBuffer wraps the AggregationBuffer path.
6366func (a * AggCast ) NewWindowFunction (ctx * sql.Context ) (sql.WindowFunction , error ) {
64- return a .inner .NewWindowFunction (ctx )
67+ fn , err := a .inner .NewWindowFunction (ctx )
68+ if err != nil {
69+ return nil , err
70+ }
71+ return & aggCastWindowFunction {inner : fn , targetType : a .targetType }, nil
6572}
6673
6774// WithWindow delegates to inner.
@@ -126,26 +133,60 @@ func (b *aggCastBuffer) Eval(ctx *sql.Context) (any, error) {
126133 if err != nil || v == nil {
127134 return v , err
128135 }
136+ return convertAggResult (v , b .targetType )
137+ }
138+
139+ func (b * aggCastBuffer ) Dispose (ctx * sql.Context ) {
140+ b .inner .Dispose (ctx )
141+ }
142+
143+ // aggCastWindowFunction wraps a sql.WindowFunction and post-converts its float64 Compute
144+ // result the same way aggCastBuffer does for the sql.AggregationBuffer (GroupBy) path.
145+ type aggCastWindowFunction struct {
146+ inner sql.WindowFunction
147+ targetType * pgtypes.DoltgresType
148+ }
149+
150+ func (w * aggCastWindowFunction ) StartPartition (ctx * sql.Context , interval sql.WindowInterval , buffer sql.WindowBuffer ) error {
151+ return w .inner .StartPartition (ctx , interval , buffer )
152+ }
153+
154+ func (w * aggCastWindowFunction ) DefaultFramer () sql.WindowFramer {
155+ return w .inner .DefaultFramer ()
156+ }
157+
158+ func (w * aggCastWindowFunction ) Compute (ctx * sql.Context , interval sql.WindowInterval , buffer sql.WindowBuffer ) (any , error ) {
159+ v , err := w .inner .Compute (ctx , interval , buffer )
160+ if err != nil || v == nil {
161+ return v , err
162+ }
163+ return convertAggResult (v , w .targetType )
164+ }
165+
166+ func (w * aggCastWindowFunction ) Dispose (ctx * sql.Context ) {
167+ w .inner .Dispose (ctx )
168+ }
169+
170+ // convertAggResult converts a raw aggregation result (always float64 from GMS's SUM/AVG
171+ // implementations, regardless of the input column's width) to match targetType: numeric,
172+ // float32, float64 (a no-op), or int64 for everything else (bigint).
173+ func convertAggResult (v any , targetType * pgtypes.DoltgresType ) (any , error ) {
129174 f , ok := v .(float64 )
130175 if ! ok {
131176 return v , nil
132177 }
133178 switch {
134- case b . targetType .Equals (pgtypes .Numeric ):
179+ case targetType .Equals (pgtypes .Numeric ):
135180 d , _ , err := apd .NewFromString (strconv .FormatFloat (f , 'f' , - 1 , 64 ))
136181 if err != nil {
137182 return nil , err
138183 }
139184 return d , nil
140- case b . targetType .Equals (pgtypes .Float32 ):
185+ case targetType .Equals (pgtypes .Float32 ):
141186 return float32 (f ), nil
142- case b . targetType .Equals (pgtypes .Float64 ):
187+ case targetType .Equals (pgtypes .Float64 ):
143188 return f , nil
144189 default :
145190 return int64 (math .RoundToEven (f )), nil
146191 }
147192}
148-
149- func (b * aggCastBuffer ) Dispose (ctx * sql.Context ) {
150- b .inner .Dispose (ctx )
151- }
0 commit comments