2 | import public Data.Vect.Quantifiers
3 | import public Postgres.Data.ResultStatus
4 | import public Postgres.Result
5 | import public Postgres.Query
7 | import Postgres.DB.Core
8 | import Postgres.Data.Conn
9 | import Postgres.Data.ConnectionStatus
10 | import Postgres.Data.PostgresType
11 | import Postgres.Data.PostgresValue
12 | import Postgres.Data.PostgresTable
13 | import Postgres.Exec
14 | import Postgres.LoadTypes
15 | import Postgres.Notification
18 | import public Control.TransitionIndexed
36 | withConn : HasIO io => (pgUrl: String) -> (onOpen: Conn -> io b) -> (onError: ConnectionStatus -> io b) -> io b
37 | withConn pgUrl onOpen onError
38 | = do conn <- pgOpen pgUrl
39 | out <- case (pgStatus conn) of
50 | data DBState = Open | Closed
53 | data OpenResult = OK | Failed String
56 | OpenResultState : OpenResult -> DBState
57 | OpenResultState = \case OK => Open
58 | (Failed _) => Closed
61 | data Connection : Type where
62 | MkConnection : Conn -> TypeDictionary -> Connection
64 | getConn : Connection -> Conn
65 | getConn (MkConnection conn _) = conn
67 | getTypes : Connection -> TypeDictionary
68 | getTypes (MkConnection _ types) = types
71 | data Database : (ty : Type) -> (s1 : DBState) -> (s2Fn : (ty -> DBState)) -> Type where
72 | DBOpen : (url : String) -> Database OpenResult Closed OpenResultState
73 | DBClose : Database () Open (const Closed)
75 | Exec : (fn : Connection -> IO a) -> Database a Open (const Open)
77 | DIO : IO a -> Database a s1 (const s1)
79 | GetTypes : Database TypeDictionary Open (const Open)
81 | Pure : (x : a) -> Database a (stateFn x) stateFn
82 | Bind : (db : Database a s1 s2Fn) -> (f : (x : a) -> Database b (s2Fn x) s3Fn) -> Database b s1 s3Fn
84 | %name Database
db, db1, db2
87 | TransitionIndexedPointed DBState Database where
91 | TransitionIndexedMonad DBState Database where
95 | liftIO' : IO a -> Database a s1 (const s1)
98 | data ConnectionState : DBState -> Type where
99 | CConnected : (conn : Connection) -> ConnectionState Open
100 | CDisconnected : ConnectionState Closed
102 | openResult : HasIO io => Conn -> io OpenResult
103 | openResult conn = case (pgStatus conn) of
105 | x => Failed <$> pgErrorMessage conn
107 | runDatabase' : HasIO io => ConnectionState s1 -> Database a s1 s2Fn -> io (x : a ** ConnectionState (s2Fn x))
108 | runDatabase' CDisconnected (DBOpen url) = do conn <- pgOpen url
109 | status <- openResult conn
111 | OK => do Right types <- pgLoadTypes conn
112 | | Left err => pure (
Failed err ** CDisconnected)
113 | Prelude.pure (
OK ** CConnected (MkConnection conn types))
114 | Failed err => pure (
Failed err ** CDisconnected)
115 | runDatabase' (CConnected conn) DBClose = do pgClose (getConn conn)
116 | pure $ (
() ** CDisconnected)
117 | runDatabase' (CConnected conn) (Exec fn) = liftIO $
do res <- fn conn
118 | pure (
res ** CConnected conn)
119 | runDatabase' (CConnected conn) GetTypes = pure $ (
getTypes conn ** CConnected conn)
120 | runDatabase' cs (Pure y) = pure (
y ** cs)
121 | runDatabase' cs (Bind db f) = do (
res ** cs')
<- runDatabase' cs db
122 | runDatabase' cs' (f res)
123 | runDatabase' cs (DIO io) = do v <- liftIO io
127 | evalDatabase : HasIO io => Database a Closed (const Closed) -> io a
128 | evalDatabase db = pure $
fst !(runDatabase' CDisconnected db)
131 | initDatabase : Database () Closed (const Closed)
132 | initDatabase = pure ()
135 | openDatabase : (url : String) -> Database OpenResult Closed OpenResultState
136 | openDatabase = DBOpen
139 | closeDatabase : Database () Open (const Closed)
140 | closeDatabase = DBClose
143 | exec : (Connection -> IO a) -> Database a Open (const Open)
149 | unsafeExec : (Conn -> IO a) -> Database a Open (const Open)
150 | unsafeExec f = Exec (\(MkConnection conn empty) => f conn)
156 | pgExec : (Conn -> IO a) -> Connection -> IO a
157 | pgExec f = f . getConn
163 | withDB : HasIO io => (url : String) -> Database a Open (const Open) -> io $
Either String a
164 | withDB url dbOps = evalDatabase dbCommands
166 | dbCommands : Database (Either String a) Closed (const Closed)
167 | dbCommands = do initDatabase
168 | OK <- openDatabase url
169 | | Failed err => pure $
Left err
176 | debugDumpTypes : Database () Open (const Open)
177 | debugDumpTypes = do types <- GetTypes
178 | liftIO' $
putStrLn $
show types
187 | stringQuery : (header : Bool) -> (query : String) -> Connection -> IO (Either String (StringResultset header))
188 | stringQuery header query (MkConnection conn types) = pgStringResultsQuery header query conn
192 | jsonQuery : (query : String) -> Connection -> IO (Maybe JSON)
193 | jsonQuery = pgExec . pgJSONResultQuery
198 | expectedQuery : {cols : Nat}
199 | -> (expected : Vect cols Type)
200 | -> (query : String)
201 | -> {auto castable : (All Castable expected)}
203 | -> IO (Either String (
rowCount ** Vect rowCount (HVect expected))
)
204 | expectedQuery expected query (MkConnection conn types) = pgResultQuery expected query conn
212 | tableQuery : PostgresTable t =>
215 | -> (cols : Vect n (ColumnIdentifier, Type))
216 | -> HasMappings IdrCast table cols =>
217 | (conn : Connection)
218 | -> IO (Either String (
rowCount ** Vect rowCount (HVect (Builtin.snd <$> cols)))
)
219 | tableQuery table cols @{mappings} conn with (select table cols @{mappings})
220 | tableQuery table cols @{mappings} conn | query =
221 | expectedQuery (snd <$> cols) query conn @{allCastable table}
223 | namespace StringColumns
230 | tableQuery' : PostgresTable t =>
233 | -> (cols : Vect n (String, Type))
234 | -> HasMappings IdrCast table (mapFst (MkColumnId (aliasOrName table)) <$> cols) =>
235 | (conn : Connection)
236 | -> IO (Either String (
rowCount ** Vect rowCount (HVect (Builtin.snd <$> (mapFst (MkColumnId (aliasOrName table)) <$> cols))))
)
237 | tableQuery' table cols conn = tableQuery table (mapFst (MkColumnId (aliasOrName table)) <$> cols) conn
244 | perform : (command : String) -> Connection -> IO ResultStatus
245 | perform cmd = pgExec (\c => withExecResult c cmd (pure . pgResultStatus))
256 | tableInsert : {n : _}
257 | -> (table : PersistedTable)
258 | -> (cols : Vect n ColumnIdentifier)
259 | -> {colTypes : Vect n Type}
260 | -> (values : HVect colTypes)
261 | -> HasLooseMappings PGCast table (zip cols colTypes) =>
262 | (conn : Connection)
264 | tableInsert table cols values conn with (insert table cols values)
265 | tableInsert table cols values conn | query =
270 | listen : (channel : String) -> Connection -> IO ResultStatus
271 | listen = pgExec . pgListen
284 | nextNotification : Connection -> IO (Maybe Notification)
285 | nextNotification = pgExec pgNextNotification
298 | notificationStream : Connection -> Stream (IO Notification)
299 | notificationStream = pgNotificationStream . getConn