packages feed

generic-persistence-0.2.0.0: src/Database/GP/SqlGenerator.hs

module Database.GP.SqlGenerator
  ( insertStmtFor,
    updateStmtFor,
    selectStmtFor,
    deleteStmtFor,
    selectAllStmtFor,
    selectAllWhereStmtFor,
    createTableStmtFor,
    dropTableStmtFor,
  )
where

import           Data.List (intercalate)
import           Database.GP.Entity
import           Database.GP.TypeInfo

-- | A function that returns an SQL insert statement for an entity. Type 'a' must be an instance of Data.
-- The function will use the field names of the data type to generate the column names in the insert statement.
-- The values of the fields will be used as the values in the insert statement.
-- Output example: INSERT INTO Person (id, name, age, address) VALUES (123456, "Alice", 25, "123 Main St");
insertStmtFor :: Entity a => a -> String
insertStmtFor x =
  "INSERT INTO "
    ++ tableName x
    ++ " ("
    ++ intercalate ", " columns
    ++ ") VALUES ("
    ++ intercalate ", " (params (length columns))
    ++ ");"
  where
    columns = columnNamesFor x


columnNamesFor :: Entity a => a -> [String]
columnNamesFor x =  map snd fieldColumnPairs
  where
    fieldColumnPairs = fieldsToColumns x 


params :: Int -> [String]
params n = replicate n "?"

-- | A function that returns an SQL update statement for an entity. Type 'a' must be an instance of Entity.
updateStmtFor :: Entity a => a -> String
updateStmtFor x =
  "UPDATE "
    ++ tableName x
    ++ " SET "
    ++ intercalate ", " updatePairs
    ++ " WHERE "
    ++ idColumn x
    ++ " = ?"
    ++ ";"
  where
    updatePairs = map (++ " = ?") (columnNamesFor x)

idColumn :: Entity a => a -> String
idColumn x = columnNameFor x (idField x)

-- | A function that returns an SQL select statement for entity type `a` with primary key `id`.
selectStmtFor :: forall a. (Entity a) => TypeInfo a -> String
selectStmtFor ti =
  "SELECT "
    ++ intercalate ", " (columnNamesFor x)
    ++ " FROM "
    ++ tableName x
    ++ " WHERE "
    ++ idColumn x
    ++ " = ?;"
  where
    x = evidenceFrom ti :: a

selectAllStmtFor :: forall a. (Entity a) => TypeInfo a -> String
selectAllStmtFor ti =
  "SELECT "
    ++ intercalate ", " (columnNamesFor x)
    ++ " FROM "
    ++ tableName x
    ++ ";"
  where
    x = evidenceFrom ti :: a

selectAllWhereStmtFor :: forall a. (Entity a) => TypeInfo a -> String -> String
selectAllWhereStmtFor ti field =
  "SELECT "
    ++ intercalate ", " (columnNamesFor x)
    ++ " FROM "
    ++ tableName x
    ++ " WHERE "
    ++ column
    ++ " = ?;"
  where
    x = evidenceFrom ti :: a
    column = columnNameFor x field

deleteStmtFor :: Entity a => a -> String
deleteStmtFor x =
  "DELETE FROM "
    ++ tableName x
    ++ " WHERE "
    ++ idColumn x
    ++ " = ?;"

createTableStmtFor :: forall a. (Entity a) => TypeInfo a -> String
createTableStmtFor ti =
  "CREATE TABLE "
    ++ tableName x
    ++ " ("
    ++ intercalate ", " (map (\(f,c) -> c ++ " " ++ columnTypeFor x f ++ optionalPK f) (fieldsToColumns x))
    ++ ");"
  where
    x = evidenceFrom ti :: a
    isIdField f = f == idField x
    optionalPK f = if isIdField f then " PRIMARY KEY" else ""
    
    
columnTypeFor :: forall a. (Entity a) => a -> String -> String
columnTypeFor x field = 
  case fType of
    "Int" -> "INTEGER"
    "String" -> "TEXT"
    "Double" -> "REAL"
    "Float" -> "REAL"
    "Bool" -> "INT"
    _ -> "TEXT"
    where
      maybeFType = maybeFieldTypeFor x field
      fType = maybe "OTHER" show maybeFType


dropTableStmtFor :: forall a. (Entity a) => TypeInfo a -> String
dropTableStmtFor ti =
  "DROP TABLE IF EXISTS "
    ++ tableName x
    ++ ";"
  where
    x = evidenceFrom ti :: a