{-# LANGUAGE OverloadedStrings #-} -- | -- Module : Database.Relational.Query.SQL -- Copyright : 2013 Kei Hibino -- License : BSD3 -- -- Maintainer : ex8k.hibino@gmail.com -- Stability : experimental -- Portability : unknown -- -- This module defines functions to generate simple SQL strings. module Database.Relational.Query.SQL ( -- * Query suffix QuerySuffix, showsQuerySuffix, -- * Update SQL updatePrefixSQL, updateSQL', updateOtherThanKeySQL', updateOtherThanKeySQL, -- * Insert SQL insertPrefixSQL, insertSQL, insertSizedChunkSQL, -- * Delete SQL deletePrefixSQL', deletePrefixSQL ) where import Data.Array (listArray, (!)) import Data.Monoid (mconcat, (<>)) import Language.SQL.Keyword (Keyword(..), (.=.), (|*|)) import qualified Language.SQL.Keyword as SQL import Database.Record.ToSql (untypedUpdateValuesIndex) import Database.Relational.Query.Internal.SQL (StringSQL, stringSQL, showStringSQL, rowStringSQL) import Database.Relational.Query.Pi (Pi) import qualified Database.Relational.Query.Pi.Unsafe as UnsafePi import Database.Relational.Query.Component (ColumnSQL, showsColumnSQL, showsColumnSQL) import Database.Relational.Query.Table (Table, name, columns) import qualified Database.Relational.Query.Projection as Projection -- | Type for query suffix words type QuerySuffix = [Keyword] -- | Expand query suffix words showsQuerySuffix :: QuerySuffix -> StringSQL showsQuerySuffix = mconcat -- | Generate prefix string of update SQL. updatePrefixSQL :: Table r -> StringSQL updatePrefixSQL table = UPDATE <> stringSQL (name table) -- | Generate update SQL by specified key and table. -- Columns name list of table are also required. updateSQL' :: String -- ^ Table name -> [ColumnSQL] -- ^ Column name list to update -> [ColumnSQL] -- ^ Key column name list -> String -- ^ Result SQL updateSQL' table cols key = showStringSQL $ mconcat [UPDATE, stringSQL table, SET, SQL.fold (|*|) updAssigns, WHERE, SQL.fold SQL.and keyAssigns] where assigns cs = [ showsColumnSQL c .=. "?" | c <- cs ] updAssigns = assigns cols keyAssigns = assigns key -- | Generate update SQL by specified key and table. -- Columns name list of table are also required. updateOtherThanKeySQL' :: String -- ^ Table name -> [ColumnSQL] -- ^ Column name list -> [Int] -- ^ Key column indexes -> String -- ^ Result SQL updateOtherThanKeySQL' table cols ixs = updateSQL' table updColumns keyColumns where width' = length cols cols' = listArray (0, width' -1) cols otherThanKey = untypedUpdateValuesIndex ixs width' columns' is = [ cols' ! i | i <- is ] updColumns = columns' otherThanKey keyColumns = columns' ixs -- | Generate update SQL specified by single key. updateOtherThanKeySQL :: Table r -- ^ Table metadata -> Pi r p -- ^ Key columns -> String -- ^ Result SQL updateOtherThanKeySQL tbl key = updateOtherThanKeySQL' (name tbl) (columns tbl) (UnsafePi.unsafeExpandIndexes key) -- | Generate prefix string of insert SQL. insertPrefixSQL :: Pi r r' -> Table r -> StringSQL insertPrefixSQL pi' table = INSERT <> INTO <> stringSQL (name table) <> rowStringSQL [showsColumnSQL c | c <- cols] where cols = Projection.columns . Projection.pi (Projection.unsafeFromTable table) $ pi' -- | Generate records chunk insert SQL. insertChunkSQL :: Int -- ^ Records count to insert -> Pi r r' -- ^ Columns selector to insert -> Table r -- ^ Table metadata -> String -- ^ Result SQL insertChunkSQL n0 pi' tbl = showStringSQL $ insertPrefixSQL pi' tbl <> VALUES <> vs where n | n0 >= 1 = n0 | otherwise = error $ "Invalid chunk count value: " ++ show n0 w = UnsafePi.width pi' vs = SQL.fold (|*|) . replicate n $ rowStringSQL (replicate w "?") -- | Generate size measured records chunk insert SQL. insertSizedChunkSQL :: Pi r r' -- ^ Columns selector to insert -> Table r -- ^ Table metadata -> Int -- ^ Chunk size threshold (column count) -> (String, Int) -- ^ Result SQL and records count of chunk insertSizedChunkSQL pi' tbl th = (insertChunkSQL n pi' tbl, n) where w = UnsafePi.width pi' n = th `quot` w + 1 -- | Generate insert SQL. insertSQL :: Pi r r' -- ^ Columns selector to insert -> Table r -- ^ Table metadata -> String -- ^ Result SQL insertSQL = insertChunkSQL 1 -- | Generate all column delete SQL by specified table. Untyped table version. deletePrefixSQL' :: String -> StringSQL deletePrefixSQL' table = DELETE <> FROM <> stringSQL table -- | Generate all column delete SQL by specified table. deletePrefixSQL :: Table r -- ^ Table metadata -> StringSQL -- ^ Result SQL deletePrefixSQL = deletePrefixSQL' . name