-- |
-- Module      : Database.Relational.SqlSyntax.Updates
-- Copyright   : 2013-2018 Kei Hibino
-- License     : BSD3
--
-- Maintainer  : ex8k.hibino@gmail.com
-- Stability   : experimental
-- Portability : unknown
--
-- This module provides types and expanding operations of SQL update and insert structure.
module Database.Relational.SqlSyntax.Updates (
  -- * Update and Insert assignments
  AssignColumn, AssignTerm, Assignment,

  composeSets,
  composeChunkValues,
  composeChunkValuesWithColumns,
  composeValuesListWithColumns,
  ) where

import Data.Monoid ((<>))

import Language.SQL.Keyword (Keyword(..), (|*|), (.=.))
import qualified Language.SQL.Keyword as SQL

import Database.Relational.Internal.String (StringSQL, rowConsStringSQL)


-- | Column SQL String of assignment
type AssignColumn = StringSQL

-- | Value SQL String of assignment
type AssignTerm   = StringSQL

-- | Assignment pair
type Assignment = (AssignColumn, AssignTerm)

-- | Compose SET clause from ['Assignment'].
composeSets :: [Assignment] -> StringSQL
composeSets as = assigns  where
  assignList = foldr (\ (col, term) r ->
                       (col .=. term) : r)
               [] as
  assigns | null assignList = error "Update assignment list is null!"
          | otherwise       = SET <> SQL.fold (|*|) assignList

-- | Compose VALUES clause from a row of value expressions.
composeChunkValues :: Int          -- ^ record count per chunk
                   -> [AssignTerm] -- ^ value expression list
                   -> Keyword
composeChunkValues n0 vs =
    VALUES <> cvs
  where
    n | n0 >= 1    =  n0
      | otherwise  =  error $ "Invalid record count value: " ++ show n0
    cvs = SQL.fold (|*|) . replicate n $ rowConsStringSQL vs

-- | Compose columns row and VALUES clause from a row of value expressions.
composeChunkValuesWithColumns :: Int          -- ^ record count per chunk
                              -> [Assignment] -- ^
                              -> StringSQL
composeChunkValuesWithColumns sz as =
    rowConsStringSQL cs <> composeChunkValues sz vs
  where
    (cs, vs) = unzip as

-- | Compose columns row and VALUES clause from rows list of value expressions.
composeValuesListWithColumns :: [[Assignment]]
                             -> StringSQL
composeValuesListWithColumns pss =
    rowConsStringSQL cs <> VALUES <> SQL.fold (|*|) (map rowConsStringSQL vss)
  where
    cs = case pss of
           []    ->  error "insertValueList: no assignment chunks"
           ps:_  ->  fst $ unzip ps
    vss = map (snd . unzip) pss