git.iko.soy/rowtype-yoga/purescript-yoga-postgres
on main help sign in
commit9ed1354b67e8faed4db7b2f2e77cf89819327ee9 authorMark <mark.eibes@gmail.com> dateFri 13 Feb 2026, 15:03 (8 months ago) parent0ec143c
2 files changed+100−3
src/Yoga/Postgres/Schema.purs+90−3 test/Schema.purs+10
src/Yoga/Postgres/Schema.purs+90−3
@@ -478,15 +478,102 @@ else instance
478478class ValidateAfterName :: Symbol -> Symbol -> Row Type -> Constraint
479479class ValidateAfterName acc rest cols
480480
481+-- End of input
481482instance FlushWord acc cols => ValidateAfterName acc "" cols
483+
484+-- Non-empty: branch on first character
482485else instance
483486 ( FlushWord acc cols
484− , Symbol.Cons "," tail rest
485− , SkipSpaces tail rest'
486− , ValidateColumns rest' cols
487+ , Symbol.Cons h t rest
488+ , ValidateAfterNameByHead h t cols
487489 ) =>
488490 ValidateAfterName acc rest cols
489491
492+class ValidateAfterNameByHead :: Symbol -> Symbol -> Row Type -> Constraint
493+class ValidateAfterNameByHead head tail cols
494+
495+-- Comma: done with this column, continue
496+instance
497+ ( SkipSpaces tail rest
498+ , ValidateColumns rest cols
499+ ) =>
500+ ValidateAfterNameByHead "," tail cols
501+
502+-- Anything else (e.g. "AS ..."): extract the word and handle
503+else instance
504+ ( Symbol.Append h t rest
505+ , ExtractWord rest word afterWord
506+ , HandleAfterColumnWord word afterWord cols
507+ ) =>
508+ ValidateAfterNameByHead h t cols
509+
510+-- Extract the first word from a Symbol (up to space or end)
511+class ExtractWord :: Symbol -> Symbol -> Symbol -> Constraint
512+class ExtractWord sym word rest | sym -> word rest
513+
514+instance ExtractWord "" "" ""
515+else instance
516+ ( Symbol.Cons h t sym
517+ , ExtractWordGo h t "" word rest
518+ ) =>
519+ ExtractWord sym word rest
520+
521+class ExtractWordGo :: Symbol -> Symbol -> Symbol -> Symbol -> Symbol -> Constraint
522+class ExtractWordGo head tail acc word rest | head tail acc -> word rest
523+
524+-- Stop on space
525+instance (SkipSpaces tail rest) => ExtractWordGo " " tail acc acc rest
526+-- Stop on comma (keep comma in rest)
527+else instance Symbol.Cons "," tail rest => ExtractWordGo "," tail acc acc rest
528+-- End of string
529+else instance Symbol.Append acc h word => ExtractWordGo h "" acc word ""
530+-- Regular char: accumulate
531+else instance
532+ ( Symbol.Append acc h acc'
533+ , Symbol.Cons nextH nextT tail
534+ , ExtractWordGo nextH nextT acc' word rest
535+ ) =>
536+ ExtractWordGo h tail acc word rest
537+
538+-- After a column name + space + word: if AS, skip alias; otherwise error
539+class HandleAfterColumnWord :: Symbol -> Symbol -> Row Type -> Constraint
540+class HandleAfterColumnWord word rest cols
541+
542+-- AS alias: skip the alias, then expect comma or end
543+instance SkipAlias rest cols => HandleAfterColumnWord "AS" rest cols
544+else instance SkipAlias rest cols => HandleAfterColumnWord "as" rest cols
545+
546+-- Skip the alias word, then expect comma or end of string
547+class SkipAlias :: Symbol -> Row Type -> Constraint
548+class SkipAlias sym cols
549+
550+instance SkipAlias "" cols
551+else instance
552+ ( ExtractWord sym _alias afterAlias
553+ , SkipSpaces afterAlias rest
554+ , ExpectCommaOrEnd rest cols
555+ ) =>
556+ SkipAlias sym cols
557+
558+class ExpectCommaOrEnd :: Symbol -> Row Type -> Constraint
559+class ExpectCommaOrEnd sym cols
560+
561+instance ExpectCommaOrEnd "" cols
562+else instance
563+ ( Symbol.Cons h t sym
564+ , ExpectCommaOrEndByHead h t cols
565+ ) =>
566+ ExpectCommaOrEnd sym cols
567+
568+class ExpectCommaOrEndByHead :: Symbol -> Symbol -> Row Type -> Constraint
569+class ExpectCommaOrEndByHead head tail cols
570+
571+instance
572+ ( SkipSpaces tail rest
573+ , ValidateColumns rest cols
574+ ) =>
575+ ExpectCommaOrEndByHead "," tail cols
576+
490577-- Skip leading spaces
491578class SkipSpaces :: Symbol -> Symbol -> Constraint
492579class SkipSpaces sym result | sym -> result
test/Schema.purs+10
@@ -100,6 +100,12 @@ builderSelectWhere = from usersTable # selectAll # where_ @"id = $1" # toSQL
100100builderSelectColsWhere :: String
101101builderSelectColsWhere = from usersTable # select @"name" # where_ @"age > $1" # toSQL
102102
103+builderSelectAlias :: String
104+builderSelectAlias = from usersTable # select @"name AS n, email AS e" # toSQL
105+
106+builderWhereComplex :: String
107+builderWhereComplex = from usersTable # selectAll # where_ @"name = $1 AND age > $2" # toSQL
108+
103109-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
104110-- Spec
105111-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -153,3 +159,7 @@ spec = do
153159 builderSelectWhere `shouldEqual` "SELECT * FROM users WHERE id = $1"
154160 it "builds SELECT columns with WHERE" do
155161 builderSelectColsWhere `shouldEqual` "SELECT name FROM users WHERE age > $1"
162+ it "builds SELECT with aliases" do
163+ builderSelectAlias `shouldEqual` "SELECT name AS n, email AS e FROM users"
164+ it "builds complex WHERE" do
165+ builderWhereComplex `shouldEqual` "SELECT * FROM users WHERE name = $1 AND age > $2"