@@ -755,6 +755,7 @@ class ResolveAggregateArg :: Symbol -> Row (Row Type) -> Type -> Constraint
755755class ResolveAggregateArg args tables argType | args tables -> argType
756756
757757instance ResolveAggregateArg "*" tables Star
758+else instance ResolveAggregateArg "" tables Star
758759else instance
759760 ( SkipSpaces args trimmedFront
760761 , ExtractWord trimmedFront col _rest
@@ -766,11 +767,32 @@ class ResolveAggregateArgCol :: Symbol -> Row (Row Type) -> Type -> Constraint
766767class ResolveAggregateArgCol col tables argType | col tables -> argType
767768
768769instance ResolveAggregateArgCol "*" tables Star
770+else instance
771+ ( Symbol.Cons h _t col
772+ , ResolveAggregateArgColByHead h col tables argType
773+ ) =>
774+ ResolveAggregateArgCol col tables argType
775+
776+class ResolveAggregateArgColByHead :: Symbol -> Symbol -> Row (Row Type) -> Type -> Constraint
777+class ResolveAggregateArgColByHead head col tables argType | head col tables -> argType
778+
779+-- Numeric literals: treat as Star (return type determined by function)
780+instance ResolveAggregateArgColByHead "0" col tables Star
781+else instance ResolveAggregateArgColByHead "1" col tables Star
782+else instance ResolveAggregateArgColByHead "2" col tables Star
783+else instance ResolveAggregateArgColByHead "3" col tables Star
784+else instance ResolveAggregateArgColByHead "4" col tables Star
785+else instance ResolveAggregateArgColByHead "5" col tables Star
786+else instance ResolveAggregateArgColByHead "6" col tables Star
787+else instance ResolveAggregateArgColByHead "7" col tables Star
788+else instance ResolveAggregateArgColByHead "8" col tables Star
789+else instance ResolveAggregateArgColByHead "9" col tables Star
790+-- Column reference
769791else instance
770792 ( ResolveColumn col tables (Column typ constraints)
771793 , UnwrapMaybe typ unwrapped
772794 ) =>
773− ResolveAggregateArgCol col tables unwrapped
795+ ResolveAggregateArgColByHead head col tables unwrapped
774796
775797-- Map (funcName, argType) -> returnType
776798class AggregateReturnType :: Symbol -> Type -> Type -> Constraint
@@ -792,6 +814,25 @@ else instance AggregateReturnType "STRING_AGG" argType String
792814else instance AggregateReturnType "string_agg" argType String
793815else instance AggregateReturnType "COALESCE" argType argType
794816else instance AggregateReturnType "coalesce" argType argType
817+-- Window functions
818+else instance AggregateReturnType "ROW_NUMBER" argType Int
819+else instance AggregateReturnType "row_number" argType Int
820+else instance AggregateReturnType "RANK" argType Int
821+else instance AggregateReturnType "rank" argType Int
822+else instance AggregateReturnType "DENSE_RANK" argType Int
823+else instance AggregateReturnType "dense_rank" argType Int
824+else instance AggregateReturnType "NTILE" argType Int
825+else instance AggregateReturnType "ntile" argType Int
826+else instance AggregateReturnType "LAG" argType argType
827+else instance AggregateReturnType "lag" argType argType
828+else instance AggregateReturnType "LEAD" argType argType
829+else instance AggregateReturnType "lead" argType argType
830+else instance AggregateReturnType "FIRST_VALUE" argType argType
831+else instance AggregateReturnType "first_value" argType argType
832+else instance AggregateReturnType "LAST_VALUE" argType argType
833+else instance AggregateReturnType "last_value" argType argType
834+else instance AggregateReturnType "NTH_VALUE" argType argType
835+else instance AggregateReturnType "nth_value" argType argType
795836else instance
796837 Fail (Beside (Text "Unknown function: ") (Quote funcName)) =>
797838 AggregateReturnType funcName argType returnType
@@ -819,34 +860,77 @@ instance
819860 Fail (Text "Aggregate function requires AS alias (e.g. COUNT(*) AS cnt)") =>
820861 ParseAfterAggregateByHead "," tail tables returnType accRL outRL
821862
822−-- Otherwise: expect "AS alias"
863+-- Otherwise: extract keyword and dispatch (OVER or AS)
823864else instance
824865 ( Symbol.Append h t rest
825866 , ExtractWord rest keyword afterKeyword
826− , ParseAggregateAS keyword afterKeyword tables returnType accRL outRL
867+ , ParseAfterAggregateKeyword keyword afterKeyword tables returnType accRL outRL
827868 ) =>
828869 ParseAfterAggregateByHead h t tables returnType accRL outRL
829870
830−class ParseAggregateAS :: Symbol -> Symbol -> Row (Row Type) -> Type -> RL.RowList Type -> RL.RowList Type -> Constraint
831−class ParseAggregateAS keyword afterKeyword tables returnType accRL outRL | keyword afterKeyword tables returnType accRL -> outRL
871+class ParseAfterAggregateKeyword :: Symbol -> Symbol -> Row (Row Type) -> Type -> RL.RowList Type -> RL.RowList Type -> Constraint
872+class ParseAfterAggregateKeyword keyword afterKeyword tables returnType accRL outRL | keyword afterKeyword tables returnType accRL -> outRL
832873
874+-- OVER: parse the over clause, then continue to AS
833875instance
876+ ( SkipSpaces afterKeyword rest
877+ , ParseOverClause rest tables returnType accRL outRL
878+ ) =>
879+ ParseAfterAggregateKeyword "OVER" afterKeyword tables returnType accRL outRL
880+
881+else instance
882+ ( SkipSpaces afterKeyword rest
883+ , ParseOverClause rest tables returnType accRL outRL
884+ ) =>
885+ ParseAfterAggregateKeyword "over" afterKeyword tables returnType accRL outRL
886+
887+-- AS: extract alias
888+else instance
834889 ( ExtractWord afterKeyword alias afterAlias
835890 , SkipSpaces afterAlias rest
836891 , ParseSelectExpectEnd rest tables (RL.Cons alias returnType accRL) outRL
837892 ) =>
838− ParseAggregateAS "AS" afterKeyword tables returnType accRL outRL
893+ ParseAfterAggregateKeyword "AS" afterKeyword tables returnType accRL outRL
839894
840895else instance
841896 ( ExtractWord afterKeyword alias afterAlias
842897 , SkipSpaces afterAlias rest
843898 , ParseSelectExpectEnd rest tables (RL.Cons alias returnType accRL) outRL
844899 ) =>
845− ParseAggregateAS "as" afterKeyword tables returnType accRL outRL
900+ ParseAfterAggregateKeyword "as" afterKeyword tables returnType accRL outRL
846901
847902else instance
848903 Fail (Text "Aggregate function requires AS alias (e.g. COUNT(*) AS cnt)") =>
849− ParseAggregateAS keyword afterKeyword tables returnType accRL outRL
904+ ParseAfterAggregateKeyword keyword afterKeyword tables returnType accRL outRL
905+
906+-- Parse OVER (...) clause: expect (, skip until ), then continue to AS alias
907+class ParseOverClause :: Symbol -> Row (Row Type) -> Type -> RL.RowList Type -> RL.RowList Type -> Constraint
908+class ParseOverClause rest tables returnType accRL outRL | rest tables returnType accRL -> outRL
909+
910+instance
911+ Fail (Text "Expected ( after OVER") =>
912+ ParseOverClause "" tables returnType accRL outRL
913+
914+else instance
915+ ( Symbol.Cons h t rest
916+ , ParseOverClauseByHead h t tables returnType accRL outRL
917+ ) =>
918+ ParseOverClause rest tables returnType accRL outRL
919+
920+class ParseOverClauseByHead :: Symbol -> Symbol -> Row (Row Type) -> Type -> RL.RowList Type -> RL.RowList Type -> Constraint
921+class ParseOverClauseByHead head tail tables returnType accRL outRL | head tail tables returnType accRL -> outRL
922+
923+-- Open paren: extract until close paren, skip content, continue to AS
924+instance
925+ ( ExtractUntilParen tail _overContent afterParen
926+ , SkipSpaces afterParen rest
927+ , ParseAfterAggregate rest tables returnType accRL outRL
928+ ) =>
929+ ParseOverClauseByHead "(" tail tables returnType accRL outRL
930+
931+else instance
932+ Fail (Text "Expected ( after OVER") =>
933+ ParseOverClauseByHead h t tables returnType accRL outRL
850934
851935-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
852936-- ParseWhere: parse "id = $id AND age > $age" -> params