module PgSchema.DML.Update where

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

-- TODO:
-- updateByKey Connection -> [r] -> IO [r']
-- updateByKeyJSON Connection -> [r] -> IO [r']
-- updateExp (e.q. update t set a = a + c + 1 where b > 10)

-- | Update rows matching a condition; the result type selects which columns are returned.
updateByCond :: forall ann -> forall r r'.
  (ann ~ 'Ann ren sch d t, UpdateReturning ann r r') =>
  Connection -> r -> Cond sch t -> IO [r']
updateByCond :: forall (ren :: Renamer) sch (d :: Nat) (t :: NameNSK).
forall (ann :: Ann) ->
forall r r'.
(ann ~ 'Ann ren sch d t, UpdateReturning ann r r') =>
Connection -> r -> Cond sch t -> IO [r']
updateByCond ann @r @r' Connection
conn r
r (forall (ann :: Ann) ->
forall r r' s.
(CRecInfo ann r, CRecInfo ann r', IsString s, Monoid s,
 ann ~ 'Ann ren sch d t) =>
Cond sch t -> (s, [SomeToField])
forall r r' s.
(CRecInfo ann r, CRecInfo ann r', IsString s, Monoid s,
 ann ~ 'Ann ren sch d t) =>
Cond sch t -> (s, [SomeToField])
forall (ren :: Renamer) sch (d :: Nat) (t :: NameNSK).
forall (ann :: Ann) ->
forall r r' s.
(CRecInfo ann r, CRecInfo ann r', IsString s, Monoid s,
 ann ~ 'Ann ren sch d t) =>
Cond sch t -> (s, [SomeToField])
updateText ann @r @r' -> (String
q,[SomeToField]
ps)) =
  String -> IO [r'] -> IO [r']
forall a. String -> a -> a
trace' (String
q String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"\n\n" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [SomeToField] -> String
forall a. Show a => a -> String
P.show [SomeToField]
ps String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"\n\n")
  (IO [r'] -> IO [r']) -> IO [r'] -> IO [r']
forall a b. (a -> b) -> a -> b
$ ([PgTag ann r'] -> [r']) -> IO [PgTag ann r'] -> IO [r']
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((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']) -> IO [PgTag ann r'] -> IO [r']
forall a b. (a -> b) -> a -> b
$ Connection
-> Query -> (PgTag ann r :. [SomeToField]) -> IO [PgTag ann r']
forall q r.
(ToRow q, FromRow r) =>
Connection -> Query -> q -> IO [r]
query Connection
conn (String -> Query
forall a. IsString a => String -> a
fromString String
q)
  ((PgTag ann r :. [SomeToField]) -> IO [PgTag ann r'])
-> (PgTag ann r :. [SomeToField]) -> IO [PgTag ann r']
forall a b. (a -> b) -> a -> b
$ forall {k} (s :: k) t. t -> PgTag s t
forall (s :: Ann) t. t -> PgTag s t
PgTag @ann @r r
r PgTag ann r -> [SomeToField] -> PgTag ann r :. [SomeToField]
forall h t. h -> t -> h :. t
:. [SomeToField]
ps

-- | Update records by condition without @RETURNING@.
updateByCond_ :: forall ann -> forall r.
  (ann ~ 'Ann ren sch d t, UpdateNonReturning ann r) =>
  Connection -> r -> Cond sch t -> IO Int64
updateByCond_ :: forall (ren :: Renamer) sch (d :: Nat) (t :: NameNSK).
forall (ann :: Ann) ->
forall r.
(ann ~ 'Ann ren sch d t, UpdateNonReturning ann r) =>
Connection -> r -> Cond sch t -> IO Int64
updateByCond_ ann @r Connection
conn r
r (forall (ann :: Ann) ->
forall r s.
(IsString s, Monoid s, CRecInfo ann r) =>
Cond sch t -> (s, [SomeToField])
forall r s.
(IsString s, Monoid s, CRecInfo ann r) =>
Cond sch t -> (s, [SomeToField])
forall sch (t :: NameNSK).
forall (ann :: Ann) ->
forall r s.
(IsString s, Monoid s, CRecInfo ann r) =>
Cond sch t -> (s, [SomeToField])
updateText_ ann @r -> (String
q, [SomeToField]
ps)) =
  String -> IO Int64 -> IO Int64
forall a. String -> a -> a
trace' (String
q String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"\n\n" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [SomeToField] -> String
forall a. Show a => a -> String
P.show [SomeToField]
ps String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"\n\n")
  (IO Int64 -> IO Int64) -> IO Int64 -> IO Int64
forall a b. (a -> b) -> a -> b
$ Connection -> Query -> (PgTag ann r :. [SomeToField]) -> IO Int64
forall q. ToRow q => Connection -> Query -> q -> IO Int64
execute Connection
conn (String -> Query
forall a. IsString a => String -> a
fromString String
q)
  ((PgTag ann r :. [SomeToField]) -> IO Int64)
-> (PgTag ann r :. [SomeToField]) -> IO Int64
forall a b. (a -> b) -> a -> b
$ forall {k} (s :: k) t. t -> PgTag s t
forall (s :: Ann) t. t -> PgTag s t
PgTag @ann @r r
r PgTag ann r -> [SomeToField] -> PgTag ann r :. [SomeToField]
forall h t. h -> t -> h :. t
:. [SomeToField]
ps

-- | Construct SQL text for updating records by condition and returning some fields.
updateText :: forall ann -> forall r r' s.
  (CRecInfo ann r, CRecInfo ann r', IsString s, Monoid s, ann ~ 'Ann ren sch d t)
  => Cond sch t -> (s, [SomeToField])
updateText :: forall (ren :: Renamer) sch (d :: Nat) (t :: NameNSK).
forall (ann :: Ann) ->
forall r r' s.
(CRecInfo ann r, CRecInfo ann r', IsString s, Monoid s,
 ann ~ 'Ann ren sch d t) =>
Cond sch t -> (s, [SomeToField])
updateText ann @r @r' (forall (ann :: Ann) ->
forall r s.
(IsString s, Monoid s, CRecInfo ann r) =>
Cond sch t -> (s, [SomeToField])
forall r s.
(IsString s, Monoid s, CRecInfo ann r) =>
Cond sch t -> (s, [SomeToField])
forall sch (t :: NameNSK).
forall (ann :: Ann) ->
forall r s.
(IsString s, Monoid s, CRecInfo ann r) =>
Cond sch t -> (s, [SomeToField])
updateText_ ann @r -> (s
q, [SomeToField]
p)) = (s
q s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
" returning " s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
fs', [SomeToField]
p)
  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 updating records by condition without @RETURNING@.
updateText_
  :: forall ann -> forall r s. (IsString s, Monoid s, CRecInfo ann r)
  => Cond sch t -> (s, [SomeToField])
updateText_ :: forall sch (t :: NameNSK).
forall (ann :: Ann) ->
forall r s.
(IsString s, Monoid s, CRecInfo ann r) =>
Cond sch t -> (s, [SomeToField])
updateText_ ann @r (Int -> Cond sch t -> (Text, [SomeToField])
forall sch (t :: NameNSK).
Int -> Cond sch t -> (Text, [SomeToField])
pgCond Int
0 -> (Text
condTxt, [SomeToField]
condParams)) =
  (s
"update " s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
tn s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
" t0 set " s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
fs s -> s -> s
forall a. Semigroup a => a -> a -> a
<> Text -> s
forall t. IsString t => Text -> t
fromText Text
whereTxt, [SomeToField]
condParams )
  where
    ri :: RecordInfo Text
ri = forall (ann :: Ann) r. CRecInfo ann r => RecordInfo Text
getRecordInfo @ann @r
    fs :: s
fs = s -> [s] -> s
forall a. Monoid a => a -> [a] -> a
intercalate' s
", " [Text -> s
forall t. IsString t => Text -> t
fromText FieldInfo Text
fi.fieldDbName s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
" = ?" | 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
    whereTxt :: Text
whereTxt
      | Text -> Bool
T.null Text
condTxt = Text
forall a. Monoid a => a
mempty
      | Bool
otherwise = Text
" where " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
condTxt