on dev help sign in
upsert
a3aa0c3Eitan Chatav
squeel-postgresql/src/Squeel/PostgreSQL/Statement.hs+39−2
@@ -735,13 +735,13 @@ update
735 -> NP (Aliased (Maybe :.: Expression params '[table ::: columns])) columns
735 -> NP (Aliased (Maybe :.: Expression params '[table ::: columns])) columns
736 -> Expression params '[table ::: columns] ('Required ('NotNull 'PGBool))
736 -> Expression params '[table ::: columns] ('Required ('NotNull 'PGBool))
737 -> Statement params '[] schema schema
737 -> Statement params '[] schema schema
738−update (Alias table) columns where' = UnsafeStatement $ mconcat
738+update (Alias table) columns wh = UnsafeStatement $ mconcat
739 [ "UPDATE "
739 [ "UPDATE "
740 , fromString $ symbolVal' table
740 , fromString $ symbolVal' table
741 , " SET "
741 , " SET "
742 , ByteString.intercalate ", " . catMaybes . hcollapse $
742 , ByteString.intercalate ", " . catMaybes . hcollapse $
743 hmap (K . renderSet) columns
743 hmap (K . renderSet) columns
744− , " WHERE ", renderExpression where', ";"
744+ , " WHERE ", renderExpression wh, ";"
745 ] where
745 ] where
746 renderSet
746 renderSet
747 :: Aliased (Maybe :.: Expression params tables) column
747 :: Aliased (Maybe :.: Expression params tables) column
@@ -753,3 +753,40 @@ update (Alias table) columns where' = UnsafeStatement $ mconcat
753 , renderExpression expression
753 , renderExpression expression
754 ]
754 ]
755 Comp Nothing `As` _ -> Nothing
755 Comp Nothing `As` _ -> Nothing
756+
757+upsertInto
758+ :: (SListI columns, HasTable table schema columns)
759+ => Alias table
760+ -> NP (Aliased (Expression params '[])) columns
761+ -> NP (Aliased (Maybe :.: Expression params '[table ::: columns])) columns
762+ -> Expression params '[table ::: columns] ('Required ('NotNull 'PGBool))
763+ -> Statement params '[] schema schema
764+upsertInto (Alias table) inserts updates wh = UnsafeStatement . mconcat $
765+ [ "INSERT INTO "
766+ , fromString (symbolVal' table)
767+ , " (" <> ByteString.intercalate ", " aliases
768+ , ") VALUES ("
769+ , ByteString.intercalate ", " values
770+ , ") ON CONFLICT UPDATE "
771+ , fromString $ symbolVal' table
772+ , " SET "
773+ , ByteString.intercalate ", " . catMaybes . hcollapse $
774+ hmap (K . renderSet) updates
775+ , " WHERE ", renderExpression wh, ";"
776+ ] where
777+ renderSet
778+ :: Aliased (Maybe :.: Expression params '[table ::: columns]) column
779+ -> Maybe ByteString
780+ renderSet = \case
781+ Comp (Just expression) `As` Alias column -> Just $ mconcat
782+ [ fromString $ symbolVal' column
783+ , " = "
784+ , renderExpression expression
785+ ]
786+ Comp Nothing `As` _ -> Nothing
787+ aliases = hcollapse $ hmap
788+ (\ (_ `As` Alias name) -> K (fromString (symbolVal' name)))
789+ inserts
790+ values = hcollapse $ hmap
791+ (\ (expression `As` _) -> K (renderExpression expression))
792+ inserts
squeel-postgresql/test/Squeel/PostgreSQL/StatementSpec.hs+13−4
@@ -25,9 +25,9 @@ spec = do
25 statement :: Statement '[] SumAndCol1 Tables Tables
25 statement :: Statement '[] SumAndCol1 Tables Tables
26 statement = select $
26 statement = select $
27 ((#col1 + #col2) `As` #sum :* #col1 :* Nil)
27 ((#col1 + #col2) `As` #sum :* #col1 :* Nil)
28− `from` (#table1 & where_ true)
28+ `from` (#table1 & where_ ((#col1 :: Expression '[] Tables ('Required ('NotNull 'PGInt4)))>* #col2))
29 statement `shouldRenderAs`
29 statement `shouldRenderAs`
30− "SELECT (col1 + col2) AS sum, col1 AS col1 FROM table1 AS table1 WHERE TRUE;"
30+ "SELECT (col1 + col2) AS sum, col1 AS col1 FROM table1 AS table1 WHERE (col1 > col2);"
31 it "combines WHEREs using AND" $ do
31 it "combines WHEREs using AND" $ do
32 let
32 let
33 statement :: Statement '[] SumAndCol1 Tables Tables
33 statement :: Statement '[] SumAndCol1 Tables Tables
@@ -78,9 +78,18 @@ spec = do
78 it "correctly render simple UPDATEs" $ do
78 it "correctly render simple UPDATEs" $ do
79 let
79 let
80 statement :: Statement '[] '[] Tables Tables
80 statement :: Statement '[] '[] Tables Tables
81− statement = update #table1 (set 2 `As` #col1 :* same `As` #col2 :* Nil) true
81+ statement = update #table1 (set 2 `As` #col1 :* same `As` #col2 :* Nil) ((#col1 :: Expression '[] Tables ('Required ('NotNull 'PGInt4))) /=* #col2)
82 statement `shouldRenderAs`
82 statement `shouldRenderAs`
83− "UPDATE table1 SET col1 = 2 WHERE TRUE;"
83+ "UPDATE table1 SET col1 = 2 WHERE (col1 <> col2);"
84+ -- it "correctly render upsert INSERTs" $ do
85+ -- let
86+ -- statement :: Statement '[] '[] Tables Tables
87+ -- statement = upsertInto #table1
88+ -- (2 `As` #col1 :* 4 `As` #col2 :* Nil)
89+ -- (set 2 `As` #col1 :* same `As` #col2 :* Nil)
90+ -- (#col1 /=* #col2)
91+ -- statement `shouldRenderAs`
92+ -- "UPDATE table1 SET col1 = 2 WHERE TRUE;"
84 it "should be safe against SQL injection in literal text" $ do
93 it "should be safe against SQL injection in literal text" $ do
85 let
94 let
86 statement :: Statement '[] '[] StudentsTable StudentsTable
95 statement :: Statement '[] '[] StudentsTable StudentsTable