{-| Module : Database.Persist.Migration.Utils.Sql Maintainer : Brandon Chinn Stability : experimental Portability : portable Defines helper functions for writing SQL queries. -} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} module Database.Persist.Migration.Utils.Sql ( commas , uncommas , quote , interpolate ) where import Data.Monoid ((<>)) import Data.Text (Text) import qualified Data.Text as Text import qualified Data.Text.Encoding as Text import Database.Persist (PersistValue(..)) -- | Split the given line by commas, ignoring commas within parentheses. -- -- > commas "a,b,c" == ["a", "b", "c"] -- > commas "a,b,c (d,e),z" == ["a", "b", "c (d,e)", "z"] -- > commas "a,b,c (d,e,(f,g)),z" == ["a", "b", "c (d,e,(f,g))", "z"] commas :: Text -> [Text] commas t = go (Text.unpack t) "" [] (0 :: Int) where go src buffer result level = let result' = result ++ [Text.pack buffer] in case src of "" -> result' ',':xs | level == 0 -> go xs "" result' level '(':xs -> go xs (buffer ++ "(") result (level + 1) ')':xs -> go xs (buffer ++ ")") result (max 0 $ level - 1) x:xs -> go xs (buffer ++ [x]) result level -- | Join the given Text with commas separating each item. uncommas :: [Text] -> Text uncommas = Text.intercalate "," -- | Quote the given Text. quote :: Text -> Text quote t = "\"" <> t <> "\"" -- | Interpolate the given values into the SQL string. interpolate :: Text -> [PersistValue] -> Text interpolate t values = if length splitted == length values + 1 then Text.concat . interleave splitted . map showValue $ values else error $ "Number of ?'s does not match number of values: " ++ show t where splitted = Text.splitOn "?" t interleave (x:xs) (y:ys) = x : y : interleave xs ys interleave xs [] = xs interleave [] ys = ys showValue = \case PersistText v -> "'" <> v <> "'" PersistByteString v -> "'" <> Text.decodeUtf8 v <> "'" PersistInt64 v -> Text.pack . show $ v PersistDouble v -> Text.pack . show $ v PersistRational v -> Text.pack . show $ v PersistBool v -> Text.pack . show $ v PersistNull -> "NULL" v -> error $ "Could not interpolate value: " ++ show v