{-# LANGUAGE KindSignatures #-}
module PgSchema.DML.Insert where

import Data.Bifunctor
import Data.String
import Data.Text as T
import Database.PostgreSQL.Simple
import GHC.Int
import PgSchema.Ann
import PgSchema.DML.Insert.Types
import PgSchema.Schema
import PgSchema.Types
import PgSchema.Utils.Internal


-- | Insert records into a table.
-- You can request any subset of columns from the inserted row via the result type.
--
-- All mandatory fields having no defaults should be present.
--
insertSch
  :: forall ann -> forall r r'. InsertReturning ann r r'
  => Connection -> [r] -> IO ([r'], Text)
insertSch :: forall (ann :: Ann) ->
forall r r'.
InsertReturning ann r r' =>
Connection -> [r] -> IO ([r'], Text)
insertSch ann @r @r' Connection
conn = let sql :: Text
sql = forall (ann :: Ann) ->
forall r r' s.
(CRecInfo ann r, CRecInfo ann r', IsString s, Monoid s) =>
s
forall r r' s.
(CRecInfo ann r, CRecInfo ann r', IsString s, Monoid s) =>
s
insertText ann @r @r' in
  String -> ([r] -> IO ([r'], Text)) -> [r] -> IO ([r'], Text)
forall a. String -> a -> a
trace' (Text -> String
T.unpack Text
sql)
    (([r] -> IO ([r'], Text)) -> [r] -> IO ([r'], Text))
-> ([r] -> IO ([r'], Text)) -> [r] -> IO ([r'], Text)
forall a b. (a -> b) -> a -> b
$ ([PgTag ann r'] -> ([r'], Text))
-> IO [PgTag ann r'] -> IO ([r'], Text)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((, Text
sql) ([r'] -> ([r'], Text))
-> ([PgTag ann r'] -> [r']) -> [PgTag ann r'] -> ([r'], Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PgTag ann r' -> r') -> [PgTag ann r'] -> [r']
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall {k} (s :: k) t. PgTag s t -> t
forall (s :: Ann) t. PgTag s t -> t
unPgTag @ann @r'))
    (IO [PgTag ann r'] -> IO ([r'], Text))
-> ([r] -> IO [PgTag ann r']) -> [r] -> IO ([r'], Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Connection -> Query -> [PgTag ann r] -> IO [PgTag ann r']
forall q r.
(ToRow q, FromRow r) =>
Connection -> Query -> [q] -> IO [r]
returning Connection
conn (String -> Query
forall a. IsString a => String -> a
fromString (String -> Query) -> String -> Query
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
sql)
    ([PgTag ann r] -> IO [PgTag ann r'])
-> ([r] -> [PgTag ann r]) -> [r] -> IO [PgTag ann r']
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (r -> PgTag ann r) -> [r] -> [PgTag ann r]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall {k} (s :: k) t. t -> PgTag s t
forall (s :: Ann) t. t -> PgTag s t
PgTag @ann @r)

-- | Insert records into a table without @RETURNING@.
insertSch_ :: forall ann -> forall r. (InsertNonReturning ann r) =>
  Connection -> [r] -> IO (Int64, Text)
insertSch_ :: forall (ann :: Ann) ->
forall r.
InsertNonReturning ann r =>
Connection -> [r] -> IO (Int64, Text)
insertSch_ ann @r Connection
conn [r]
recs = let sql :: Text
sql = forall (ann :: Ann) ->
forall r s. (IsString s, Monoid s, CRecInfo ann r) => s
forall r s. (IsString s, Monoid s, CRecInfo ann r) => s
insertText_ ann @r in do
  String -> IO (Int64, Text) -> IO (Int64, Text)
forall a. String -> a -> a
trace' (Text -> String
T.unpack Text
sql)
    (IO (Int64, Text) -> IO (Int64, Text))
-> IO (Int64, Text) -> IO (Int64, Text)
forall a b. (a -> b) -> a -> b
$ (Int64 -> (Int64, Text)) -> IO Int64 -> IO (Int64, Text)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (, Text
sql)
    (IO Int64 -> IO (Int64, Text))
-> ([r] -> IO Int64) -> [r] -> IO (Int64, Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Connection -> Query -> [PgTag ann r] -> IO Int64
forall q. ToRow q => Connection -> Query -> [q] -> IO Int64
executeMany Connection
conn (String -> Query
forall a. IsString a => String -> a
fromString (String -> Query) -> String -> Query
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
sql)
    ([PgTag ann r] -> IO Int64)
-> ([r] -> [PgTag ann r]) -> [r] -> IO Int64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (r -> PgTag ann r) -> [r] -> [PgTag ann r]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall {k} (s :: k) t. t -> PgTag s t
forall (s :: Ann) t. t -> PgTag s t
PgTag @ann @r) ([r] -> IO (Int64, Text)) -> [r] -> IO (Int64, Text)
forall a b. (a -> b) -> a -> b
$ [r]
recs

-- | Construct SQL text for inserting records into a table and returning some fields.
insertText
  :: forall ann -> forall r r' s
  . (CRecInfo ann r, CRecInfo ann r', IsString s, Monoid s) => s
insertText :: forall (ann :: Ann) ->
forall r r' s.
(CRecInfo ann r, CRecInfo ann r', IsString s, Monoid s) =>
s
insertText ann @r @r'= forall (ann :: Ann) ->
forall r s. (IsString s, Monoid s, CRecInfo ann r) => s
forall r s. (IsString s, Monoid s, CRecInfo ann r) => s
insertText_ ann @r s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
" returning " s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
fs'
  where
    ri :: RecordInfo Text
ri = forall (ann :: Ann) r. CRecInfo ann r => RecordInfo Text
getRecordInfo @ann @r'
    fs' :: s
fs' = Text -> s
forall t. IsString t => Text -> t
fromText (Text -> s) -> Text -> s
forall a b. (a -> b) -> a -> b
$ Text -> [Text] -> Text
T.intercalate Text
"," [ FieldInfo Text
fi.fieldDbName | FieldInfo Text
fi <- RecordInfo Text
ri.fields]

-- | Construct SQL text for inserting records into a table without @RETURNING@.
insertText_ :: forall ann -> forall r s. (IsString s, Monoid s) =>
  CRecInfo ann r => s
insertText_ :: forall (ann :: Ann) ->
forall r s. (IsString s, Monoid s, CRecInfo ann r) => s
insertText_ ann @r = s
"insert into " s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
tn s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
"(" s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
fs s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
") values (" s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
qs s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
")"
  where
    ri :: RecordInfo Text
ri = forall (ann :: Ann) r. CRecInfo ann r => RecordInfo Text
getRecordInfo @ann @r
    (s
fs,s
qs) = ([Text] -> s) -> ([Text] -> s) -> ([Text], [Text]) -> (s, s)
forall a b c d. (a -> b) -> (c -> d) -> (a, c) -> (b, d)
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap [Text] -> s
inter [Text] -> s
inter (([Text], [Text]) -> (s, s)) -> ([Text], [Text]) -> (s, s)
forall a b. (a -> b) -> a -> b
$ [(Text, Text)] -> ([Text], [Text])
forall a b. [(a, b)] -> ([a], [b])
unzip [ (FieldInfo Text
fi.fieldDbName,Text
"?") | FieldInfo Text
fi <- RecordInfo Text
ri.fields]
    tn :: s
tn = Text -> s
forall t. IsString t => Text -> t
fromText (Text -> s) -> Text -> s
forall a b. (a -> b) -> a -> b
$ NameNS -> Text
qualName RecordInfo Text
ri.tabName
    inter :: [Text] -> s
inter = Text -> s
forall t. IsString t => Text -> t
fromText (Text -> s) -> ([Text] -> Text) -> [Text] -> s
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text] -> Text
T.intercalate Text
","