{-# LANGUAGE CPP #-}

-- |
--
-- Using migrations is fairly easy. After you've defined the lists of
-- migrations and tables, just run
-- 'Database.PostgreSQL.PQTypes.Checks.migrateDatabase':
--
-- @
-- definitions = emptyDbDefinitions { ... }
--
-- migrations :: [Migration]
-- migrations = ...
--
-- migrateDatabase options definitions migrations
-- @
--
-- Migrations are run strictly in the order specified in the migrations
-- list, starting with the first migration for which the corresponding
-- table in the DB has the version number equal to the 'mgrFrom' field of
-- the migration.
module Database.PostgreSQL.PQTypes.Model.Migration
  ( DropTableMode (..)
  , MigrationAction (..)
  , Migration (..)
  , isStandardMigration
  , isDropTableMigration
  ) where

import Data.Int

import Database.PostgreSQL.PQTypes.FromRow (FromRow)
import Database.PostgreSQL.PQTypes.Model.Index
import Database.PostgreSQL.PQTypes.Model.Table
import Database.PostgreSQL.PQTypes.SQL (SQL)
import Database.PostgreSQL.PQTypes.SQL.Raw

-- | Migration action to run, either an arbitrary 'MonadDB' action, or
-- something more fine-grained.
data MigrationAction m
  = -- | Standard migration, i.e. an arbitrary 'MonadDB' action.
    StandardMigration (m ())
  | -- | Drop table migration. Parameter is the drop table mode
    -- (@RESTRICT@/@CASCADE@). The 'Migration' record holds the name of
    -- the table to drop.
    DropTableMigration DropTableMode
  | -- | Migration for creating an index concurrently.
    CreateIndexConcurrentlyMigration
      (RawSQL ())
      -- ^ Table name
      TableIndex
      -- ^ Index
  | -- | Migration for dropping an index concurrently.
    DropIndexConcurrentlyMigration
      (RawSQL ())
      -- ^ Table name
      TableIndex
      -- ^ Index
  | -- | Migration for modifying columns. Parameters are:
    --
    -- Name of the table that the cursor is associated with. It has to be the same as in the
    -- cursor SQL, see the second parameter.
    --
    -- SQL providing a list of primary keys from the associated table that will be used for the cursor.
    --
    -- Function that takes a batch of primary keys provided by the cursor SQL and runs an arbitrary computation
    -- within MonadDB. The function might be called repeatedly depending on the batch size and total number of
    -- selected primary keys. See the last argument.
    --
    -- Batch size of primary keys to be fetched at once by the cursor SQL and be given to the modification function.
    -- To handle multi-column primary keys, the following needs to be done:
    --
    --   1. Get the list of tuples from PostgreSQL.
    --   2. Unzip them into a tuple of lists in Haskell.
    --   3. Pass the lists to PostgreSQL as separate parameters and zip them back in the SQL,
    --      see https://stackoverflow.com/questions/12414750/is-there-something-like-a-zip-function-in-postgresql-that-combines-two-arrays for more details.
    forall t. FromRow t => ModifyColumnMigration (RawSQL ()) SQL ([t] -> m ()) Int

-- | Migration object.
data Migration m
  = Migration
  { forall (m :: * -> *). Migration m -> RawSQL ()
mgrTableName :: RawSQL ()
  -- ^ The name of the table you're migrating.
  , forall (m :: * -> *). Migration m -> Int32
mgrFrom :: Int32
  -- ^ The version you're migrating *from* (you don't specify what
  -- version you migrate TO, because version is always increased by 1,
  -- so if 'mgrFrom' is 2, that means that after that migration is run,
  -- table version will equal 3
  , forall (m :: * -> *). Migration m -> MigrationAction m
mgrAction :: MigrationAction m
  -- ^ Migration action.
  }

isStandardMigration :: Migration m -> Bool
isStandardMigration :: forall (m :: * -> *). Migration m -> Bool
isStandardMigration Migration {Int32
RawSQL ()
MigrationAction m
mgrFrom :: forall (m :: * -> *). Migration m -> Int32
mgrTableName :: forall (m :: * -> *). Migration m -> RawSQL ()
mgrAction :: forall (m :: * -> *). Migration m -> MigrationAction m
mgrTableName :: RawSQL ()
mgrFrom :: Int32
mgrAction :: MigrationAction m
..} =
  case MigrationAction m
mgrAction of
    StandardMigration {} -> Bool
True
    DropTableMigration {} -> Bool
False
    CreateIndexConcurrentlyMigration {} -> Bool
False
    DropIndexConcurrentlyMigration {} -> Bool
False
    ModifyColumnMigration {} -> Bool
False

isDropTableMigration :: Migration m -> Bool
isDropTableMigration :: forall (m :: * -> *). Migration m -> Bool
isDropTableMigration Migration {Int32
RawSQL ()
MigrationAction m
mgrFrom :: forall (m :: * -> *). Migration m -> Int32
mgrTableName :: forall (m :: * -> *). Migration m -> RawSQL ()
mgrAction :: forall (m :: * -> *). Migration m -> MigrationAction m
mgrTableName :: RawSQL ()
mgrFrom :: Int32
mgrAction :: MigrationAction m
..} =
  case MigrationAction m
mgrAction of
    StandardMigration {} -> Bool
False
    DropTableMigration {} -> Bool
True
    CreateIndexConcurrentlyMigration {} -> Bool
False
    DropIndexConcurrentlyMigration {} -> Bool
False
    ModifyColumnMigration {} -> Bool
False