packages feed

qhs-0.4.0: src/Qhs/SQL.hs

module Qhs.SQL (open, close, createTable, insertRow, execute) where

import Control.Exception (SomeException, try)
import Control.Monad (forM)
import Data.List (intercalate)
import Data.String (fromString)
import Data.Text qualified as Text
import Database.SQLite.Simple qualified as SQLite

import Qhs.SQLType

-- | Open a new database connection.
open :: String -> IO SQLite.Connection
open = SQLite.open

-- | Close a database connection.
close :: SQLite.Connection -> IO ()
close = SQLite.close

-- | Creates a new table.
createTable :: SQLite.Connection -> String -> [String] -> [SQLType] -> IO (Either SomeException ())
createTable conn name columns types = do
  let stmt = "CREATE TABLE " ++ sqlQuote name ++ " "
           ++ tupled (zipWith quote types columns) ++ ";"
  try $ SQLite.execute_ conn (fromString stmt)
    where quote t c = sqlQuote c ++ " " ++ show t

-- | Inserts a row into a table.
insertRow :: SQLite.Connection -> String -> [String] -> [SQLType] -> [String] -> IO (Either SomeException ())
insertRow conn name columns types entry = do
  let stmt = "INSERT INTO " ++ sqlQuote name ++ tupled (map sqlQuote columns)
           ++ " VALUES " ++ tupled (replicate (length columns) "?") ++ ";"
  try $ SQLite.execute conn (fromString stmt) (zip types entry)

-- | Executes a SQL statement.
execute :: SQLite.Connection -> String -> IO (Either SomeException ([String], [[Any]]))
execute conn query = do
  e <- try $ SQLite.query_ conn (fromString query)
  columns <- SQLite.withStatement conn (fromString query) \stmt -> do
    cnt <- toInteger <$> SQLite.columnCount stmt
    forM [0..cnt-1] \i ->
      Text.unpack <$> SQLite.columnName stmt (fromInteger i)
  return $ (columns,) <$> e

sqlQuote :: String -> String
sqlQuote xs = "`" ++ xs ++ "`"

tupled :: [String] -> String
tupled xs = "(" ++ intercalate ", " xs ++ ")"