module Database.PostgreSQL.PQTypes.Model.Table
  ( TableColumn (..)
  , tblColumn
  , sqlAddColumn
  , sqlAlterColumn
  , sqlDropColumn
  , Rows (..)
  , Table (..)
  , tblTable
  , sqlCreateTable
  , sqlAlterTable
  , DropTableMode (..)
  , sqlDropTable
  , TableInitialSetup (..)
  ) where

import Control.Monad.Catch
import Data.ByteString (ByteString)
import Data.Int
import Data.Monoid.Utils
import Database.PostgreSQL.PQTypes

import Database.PostgreSQL.PQTypes.Model.Check
import Database.PostgreSQL.PQTypes.Model.ColumnType
import Database.PostgreSQL.PQTypes.Model.ForeignKey
import Database.PostgreSQL.PQTypes.Model.Index
import Database.PostgreSQL.PQTypes.Model.PrimaryKey
import Database.PostgreSQL.PQTypes.Model.Trigger

data TableColumn = TableColumn
  { TableColumn -> RawSQL ()
colName :: RawSQL ()
  , TableColumn -> ColumnType
colType :: ColumnType
  , TableColumn -> Maybe (RawSQL ())
colCollation :: Maybe (RawSQL ())
  , TableColumn -> Bool
colNullable :: Bool
  , TableColumn -> Maybe (RawSQL ())
colDefault :: Maybe (RawSQL ())
  }
  deriving (Int -> TableColumn -> ShowS
[TableColumn] -> ShowS
TableColumn -> String
(Int -> TableColumn -> ShowS)
-> (TableColumn -> String)
-> ([TableColumn] -> ShowS)
-> Show TableColumn
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TableColumn -> ShowS
showsPrec :: Int -> TableColumn -> ShowS
$cshow :: TableColumn -> String
show :: TableColumn -> String
$cshowList :: [TableColumn] -> ShowS
showList :: [TableColumn] -> ShowS
Show)

tblColumn :: TableColumn
tblColumn :: TableColumn
tblColumn =
  TableColumn
    { colName :: RawSQL ()
colName = String -> RawSQL ()
forall a. HasCallStack => String -> a
error String
"tblColumn: column name must be specified"
    , colType :: ColumnType
colType = String -> ColumnType
forall a. HasCallStack => String -> a
error String
"tblColumn: column type must be specified"
    , colCollation :: Maybe (RawSQL ())
colCollation = Maybe (RawSQL ())
forall a. Maybe a
Nothing
    , colNullable :: Bool
colNullable = Bool
True
    , colDefault :: Maybe (RawSQL ())
colDefault = Maybe (RawSQL ())
forall a. Maybe a
Nothing
    }

sqlAddColumn :: TableColumn -> RawSQL ()
sqlAddColumn :: TableColumn -> RawSQL ()
sqlAddColumn TableColumn {Bool
Maybe (RawSQL ())
RawSQL ()
ColumnType
colName :: TableColumn -> RawSQL ()
colType :: TableColumn -> ColumnType
colCollation :: TableColumn -> Maybe (RawSQL ())
colNullable :: TableColumn -> Bool
colDefault :: TableColumn -> Maybe (RawSQL ())
colName :: RawSQL ()
colType :: ColumnType
colCollation :: Maybe (RawSQL ())
colNullable :: Bool
colDefault :: Maybe (RawSQL ())
..} =
  [RawSQL ()] -> RawSQL ()
forall m. (IsString m, Monoid m) => [m] -> m
smconcat
    [ RawSQL ()
"ADD COLUMN"
    , RawSQL ()
colName
    , ColumnType -> RawSQL ()
columnTypeToSQL ColumnType
colType
    , RawSQL ()
-> (RawSQL () -> RawSQL ()) -> Maybe (RawSQL ()) -> RawSQL ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe RawSQL ()
"" (\RawSQL ()
c -> RawSQL ()
"COLLATE \"" RawSQL () -> RawSQL () -> RawSQL ()
forall a. Semigroup a => a -> a -> a
<> RawSQL ()
c RawSQL () -> RawSQL () -> RawSQL ()
forall a. Semigroup a => a -> a -> a
<> RawSQL ()
"\"") Maybe (RawSQL ())
colCollation
    , if Bool
colNullable then RawSQL ()
"NULL" else RawSQL ()
"NOT NULL"
    , RawSQL ()
-> (RawSQL () -> RawSQL ()) -> Maybe (RawSQL ()) -> RawSQL ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe RawSQL ()
"" (RawSQL ()
"DEFAULT" RawSQL () -> RawSQL () -> RawSQL ()
forall m. (IsString m, Monoid m) => m -> m -> m
<+>) Maybe (RawSQL ())
colDefault
    ]

sqlAlterColumn :: RawSQL () -> RawSQL () -> RawSQL ()
sqlAlterColumn :: RawSQL () -> RawSQL () -> RawSQL ()
sqlAlterColumn RawSQL ()
cname RawSQL ()
alter = RawSQL ()
"ALTER COLUMN" RawSQL () -> RawSQL () -> RawSQL ()
forall m. (IsString m, Monoid m) => m -> m -> m
<+> RawSQL ()
cname RawSQL () -> RawSQL () -> RawSQL ()
forall m. (IsString m, Monoid m) => m -> m -> m
<+> RawSQL ()
alter

sqlDropColumn :: RawSQL () -> RawSQL ()
sqlDropColumn :: RawSQL () -> RawSQL ()
sqlDropColumn RawSQL ()
cname = RawSQL ()
"DROP COLUMN" RawSQL () -> RawSQL () -> RawSQL ()
forall m. (IsString m, Monoid m) => m -> m -> m
<+> RawSQL ()
cname

----------------------------------------

data Rows = forall row. (Show row, ToRow row) => Rows [ByteString] [row]

data Table
  = Table
  { Table -> RawSQL ()
tblName :: RawSQL ()
  -- ^ Must be in lower case.
  , Table -> Int32
tblVersion :: Int32
  , Table -> [TableColumn]
tblColumns :: [TableColumn]
  , Table -> Maybe PrimaryKey
tblPrimaryKey :: Maybe PrimaryKey
  , Table -> [Check]
tblChecks :: [Check]
  , Table -> [ForeignKey]
tblForeignKeys :: [ForeignKey]
  , Table -> [TableIndex]
tblIndexes :: [TableIndex]
  , Table -> [Trigger]
tblTriggers :: [Trigger]
  , Table -> Maybe TableInitialSetup
tblInitialSetup :: Maybe TableInitialSetup
  }

data TableInitialSetup = TableInitialSetup
  { TableInitialSetup
-> forall (m :: * -> *). (MonadDB m, MonadThrow m) => m Bool
checkInitialSetup :: forall m. (MonadDB m, MonadThrow m) => m Bool
  , TableInitialSetup
-> forall (m :: * -> *). (MonadDB m, MonadThrow m) => m ()
initialSetup :: forall m. (MonadDB m, MonadThrow m) => m ()
  }

tblTable :: Table
tblTable :: Table
tblTable =
  Table
    { tblName :: RawSQL ()
tblName = String -> RawSQL ()
forall a. HasCallStack => String -> a
error String
"tblTable: table name must be specified"
    , tblVersion :: Int32
tblVersion = String -> Int32
forall a. HasCallStack => String -> a
error String
"tblTable: table version must be specified"
    , tblColumns :: [TableColumn]
tblColumns = String -> [TableColumn]
forall a. HasCallStack => String -> a
error String
"tblTable: table columns must be specified"
    , tblPrimaryKey :: Maybe PrimaryKey
tblPrimaryKey = Maybe PrimaryKey
forall a. Maybe a
Nothing
    , tblChecks :: [Check]
tblChecks = []
    , tblForeignKeys :: [ForeignKey]
tblForeignKeys = []
    , tblIndexes :: [TableIndex]
tblIndexes = []
    , tblTriggers :: [Trigger]
tblTriggers = []
    , tblInitialSetup :: Maybe TableInitialSetup
tblInitialSetup = Maybe TableInitialSetup
forall a. Maybe a
Nothing
    }

sqlCreateTable :: RawSQL () -> RawSQL ()
sqlCreateTable :: RawSQL () -> RawSQL ()
sqlCreateTable RawSQL ()
tname = RawSQL ()
"CREATE TABLE" RawSQL () -> RawSQL () -> RawSQL ()
forall m. (IsString m, Monoid m) => m -> m -> m
<+> RawSQL ()
tname RawSQL () -> RawSQL () -> RawSQL ()
forall m. (IsString m, Monoid m) => m -> m -> m
<+> RawSQL ()
"()"

-- | Whether to also drop objects that depend on the table.
data DropTableMode
  = -- | Automatically drop objects that depend on the table (such as views).
    DropTableCascade
  | -- | Refuse to drop the table if any objects depend on it. This is the default.
    DropTableRestrict

sqlDropTable :: RawSQL () -> DropTableMode -> RawSQL ()
sqlDropTable :: RawSQL () -> DropTableMode -> RawSQL ()
sqlDropTable RawSQL ()
tname DropTableMode
mode =
  RawSQL ()
"DROP TABLE"
    RawSQL () -> RawSQL () -> RawSQL ()
forall m. (IsString m, Monoid m) => m -> m -> m
<+> RawSQL ()
tname
    RawSQL () -> RawSQL () -> RawSQL ()
forall m. (IsString m, Monoid m) => m -> m -> m
<+> case DropTableMode
mode of
      DropTableMode
DropTableCascade -> RawSQL ()
"CASCADE"
      DropTableMode
DropTableRestrict -> RawSQL ()
"RESTRICT"

sqlAlterTable :: RawSQL () -> [RawSQL ()] -> RawSQL ()
sqlAlterTable :: RawSQL () -> [RawSQL ()] -> RawSQL ()
sqlAlterTable RawSQL ()
tname [RawSQL ()]
alter_statements =
  [RawSQL ()] -> RawSQL ()
forall m. (IsString m, Monoid m) => [m] -> m
smconcat
    [ RawSQL ()
"ALTER TABLE"
    , RawSQL ()
tname
    , RawSQL () -> [RawSQL ()] -> RawSQL ()
forall m. Monoid m => m -> [m] -> m
mintercalate RawSQL ()
", " [RawSQL ()]
alter_statements
    ]