@@ -982,9 +982,10 @@ else instance
982class ParseOverClauseByHead :: Symbol -> Symbol -> Row (Row Type) -> Type -> RL.RowList Type -> RL.RowList Type -> Constraint
982class ParseOverClauseByHead :: Symbol -> Symbol -> Row (Row Type) -> Type -> RL.RowList Type -> RL.RowList Type -> Constraint
983class ParseOverClauseByHead head tail tables returnType accRL outRL | head tail tables returnType accRL -> outRL
983class ParseOverClauseByHead head tail tables returnType accRL outRL | head tail tables returnType accRL -> outRL
985−-- Open paren: extract until close paren, skip content, continue to AS
985+-- Open paren: extract until close paren, validate content, continue to AS
987− ( ExtractUntilParen tail _overContent afterParen
987+ ( ExtractUntilParen tail overContent afterParen
988+ , ValidateOverContent overContent tables
988 , SkipSpaces afterParen rest
989 , SkipSpaces afterParen rest
989 , ParseAfterAggregate rest tables returnType accRL outRL
990 , ParseAfterAggregate rest tables returnType accRL outRL
@@ -994,6 +995,153 @@ else instance
994 Fail (Text "Expected ( after OVER") =>
995 Fail (Text "Expected ( after OVER") =>
995 ParseOverClauseByHead h t tables returnType accRL outRL
996 ParseOverClauseByHead h t tables returnType accRL outRL
998+-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
999+-- ValidateOverContent: validates PARTITION BY / ORDER BY inside OVER(...)
1000+-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1002+class ValidateOverContent :: Symbol -> Row (Row Type) -> Constraint
1003+class ValidateOverContent content tables
1005+instance ValidateOverContent "" tables
1008+ ( SkipSpaces content content'
1009+ , ValidateOverContentTrimmed content' tables
1011+ ValidateOverContent content tables
1013+class ValidateOverContentTrimmed :: Symbol -> Row (Row Type) -> Constraint
1014+class ValidateOverContentTrimmed content tables
1016+instance ValidateOverContentTrimmed "" tables
1019+ ( ExtractWord content keyword rest
1020+ , ValidateOverKeyword keyword rest tables
1022+ ValidateOverContentTrimmed content tables
1024+class ValidateOverKeyword :: Symbol -> Symbol -> Row (Row Type) -> Constraint
1025+class ValidateOverKeyword keyword rest tables
1028+ ( ExtractWord rest byWord afterBy
1029+ , AssertIsBy byWord
1030+ , ValidatePartitionByColumns afterBy tables
1032+ ValidateOverKeyword "PARTITION" rest tables
1035+ ( ExtractWord rest byWord afterBy
1036+ , AssertIsBy byWord
1037+ , ValidatePartitionByColumns afterBy tables
1039+ ValidateOverKeyword "partition" rest tables
1042+ ( ExtractWord rest byWord afterBy
1043+ , AssertIsBy byWord
1044+ , ValidateOrderBy afterBy tables
1046+ ValidateOverKeyword "ORDER" rest tables
1049+ ( ExtractWord rest byWord afterBy
1050+ , AssertIsBy byWord
1051+ , ValidateOrderBy afterBy tables
1053+ ValidateOverKeyword "order" rest tables
1056+ Fail (Text "Expected PARTITION BY or ORDER BY inside OVER clause") =>
1057+ ValidateOverKeyword keyword rest tables
1059+class AssertIsBy :: Symbol -> Constraint
1060+class AssertIsBy sym
1062+instance AssertIsBy "BY"
1063+else instance AssertIsBy "by"
1064+else instance Fail (Text "Expected BY after PARTITION or ORDER") => AssertIsBy sym
1066+-- ValidatePartitionByColumns: column list that terminates at ORDER keyword
1067+class ValidatePartitionByColumns :: Symbol -> Row (Row Type) -> Constraint
1068+class ValidatePartitionByColumns sym tables
1070+instance ValidatePartitionByColumns "" tables
1073+ ( SkipSpaces sym sym'
1074+ , ValidatePartitionByColumnsTrimmed sym' tables
1076+ ValidatePartitionByColumns sym tables
1078+class ValidatePartitionByColumnsTrimmed :: Symbol -> Row (Row Type) -> Constraint
1079+class ValidatePartitionByColumnsTrimmed sym tables
1081+instance ValidatePartitionByColumnsTrimmed "" tables
1084+ ( Symbol.Cons h t sym
1085+ , ValidatePartitionByColumnsGo h t "" tables
1087+ ValidatePartitionByColumnsTrimmed sym tables
1089+class ValidatePartitionByColumnsGo :: Symbol -> Symbol -> Symbol -> Row (Row Type) -> Constraint
1090+class ValidatePartitionByColumnsGo head tail acc tables
1092+-- Comma: flush column, continue
1094+ ( ResolveColumn acc tables typ
1095+ , SkipSpaces tail rest
1096+ , ValidatePartitionByColumnsTrimmed rest tables
1098+ ValidatePartitionByColumnsGo "," tail acc tables
1100+-- Space: flush word, check if ORDER keyword or column
1102+ ( SkipSpaces tail rest
1103+ , FlushPartitionWord acc rest tables
1105+ ValidatePartitionByColumnsGo " " tail acc tables
1107+-- End of string: flush final column
1109+ ( Symbol.Append acc h acc'
1110+ , ResolveColumn acc' tables typ
1112+ ValidatePartitionByColumnsGo h "" acc tables
1114+-- Regular char: accumulate
1116+ ( Symbol.Append acc h acc'
1117+ , Symbol.Cons nextH nextT tail
1118+ , ValidatePartitionByColumnsGo nextH nextT acc' tables
1120+ ValidatePartitionByColumnsGo h tail acc tables
1122+class FlushPartitionWord :: Symbol -> Symbol -> Row (Row Type) -> Constraint
1123+class FlushPartitionWord word rest tables
1126+ ( ExtractWord rest byWord afterBy
1127+ , AssertIsBy byWord
1128+ , ValidateOrderBy afterBy tables
1130+ FlushPartitionWord "ORDER" rest tables
1133+ ( ExtractWord rest byWord afterBy
1134+ , AssertIsBy byWord
1135+ , ValidateOrderBy afterBy tables
1137+ FlushPartitionWord "order" rest tables
1140+ ( ResolveColumn word tables typ
1141+ , ValidatePartitionByColumnsTrimmed rest tables
1143+ FlushPartitionWord word rest tables
997-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1145-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
998-- ParseWhere: parse "id = $id AND age > $age" -> params
1146-- ParseWhere: parse "id = $id AND age > $age" -> params
999-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1147-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━