Skip to content

Commit ac5f44b

Browse files
committed
added explicit type casts option
1 parent caf1919 commit ac5f44b

3 files changed

Lines changed: 57 additions & 33 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [Unreleased]
44
### Added
55
- Changelog.
6+
- Explicit type casts options.
67

78
## [0.3.0] - 2016-10-29
89
### Added

src/Database/PostgreSQL/Simple/Bind/Common.hs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,22 @@ data ReturnType
4242

4343
-- | Options that specify how to construct the function binding.
4444
data PostgresBindOptions = PostgresBindOptions {
45-
pboFunctionName :: PGFunction -> String -- ^ Function that generates name of a binding.
46-
, pboIsNullable :: String -> String -> Bool -- ^ Which columns in returned tables can be null.
47-
, pboSetOfReturnType :: String -> ReturnType -- ^ How to process type in "setof" clause.
45+
pboFunctionName :: PGFunction -> String
46+
-- ^ Function that generates name of a binding.
47+
, pboIsNullable :: String -> String -> Bool
48+
-- ^ Which columns in returned tables can be null.
49+
, pboSetOfReturnType :: String -> ReturnType
50+
-- ^ How to process type in "setof" clause.
51+
, pboExplicitCasts :: Bool
52+
-- ^ Whether to add explicit type casts to arguments.
4853
}
4954

5055
instance Default PostgresBindOptions where
5156
def = PostgresBindOptions {
5257
pboFunctionName = \(PGFunction _schema name _args _result) -> name
5358
, pboIsNullable = \_fname _column -> False
5459
, pboSetOfReturnType = \_tname -> AsField
60+
, pboExplicitCasts = True
5561
}
5662

5763
-- | Remove 'Only' constructor.

src/Database/PostgreSQL/Simple/Bind/Implementation.hs

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
{-# LANGUAGE FlexibleInstances #-}
1515
{-# LANGUAGE RecordWildCards #-}
1616
{-# LANGUAGE CPP #-}
17+
{-# LANGUAGE LambdaCase #-}
1718

1819
{-|
1920
Module: Database.PostgreSQL.Simple.Bind.Implementation
@@ -51,7 +52,7 @@ import Database.PostgreSQL.Simple.FromRow (FromRow(..))
5152
import Database.PostgreSQL.Simple.Types (Query(..))
5253
import GHC.TypeLits (Symbol)
5354
import Language.Haskell.TH.Syntax (Q, Dec(..), Exp(..), Type(..), Clause(..), Body(..), Pat(..))
54-
import Language.Haskell.TH.Syntax (Name, mkName, newName, Lit(..), TyLit(..), TyVarBndr(..))
55+
import Language.Haskell.TH.Syntax (Name, mkName, newName, Lit(..), TyLit(..), TyVarBndr(..), lift)
5556
import qualified Data.ByteString.Char8 as BS
5657

5758
-- | Mapping from PostgreSQL types to Haskell types.
@@ -65,41 +66,51 @@ mkFunction :: PostgresBindOptions -> PGFunction -> Q [Dec]
6566
mkFunction opt f = sequence $ (($ f) . ($ opt)) <$> [mkFunctionT, mkFunctionE]
6667

6768
#ifdef DebugQueries
68-
data Argument = forall a . (Show a, ToField a) => MandatoryArg String a
69-
| forall a . (Show a, ToField a) => OptionalArg String (Maybe a)
69+
data Argument = forall a . (Show a, ToField a) => MandatoryArg String String a
70+
| forall a . (Show a, ToField a) => OptionalArg String String (Maybe a)
7071

7172
instance Show Argument where
72-
show (MandatoryArg name value) = "mandatory: " ++ name ++ " => " ++ show value
73-
show (OptionalArg name value) = "optional: " ++ name ++ " => " ++ show value
73+
show (MandatoryArg name atype value) = "mandatory: " ++ name ++ " => " ++ show value ++ "::" ++ atype
74+
show (OptionalArg name atype value) = "optional: " ++ name ++ " => " ++ show value ++ "::" ++ atype
7475
#else
75-
data Argument = forall a . (ToField a) => MandatoryArg String a
76-
| forall a . (ToField a) => OptionalArg String (Maybe a)
76+
data Argument = forall a . (ToField a) => MandatoryArg String String a
77+
| forall a . (ToField a) => OptionalArg String String (Maybe a)
7778
#endif
7879

7980
instance ToField Argument where
80-
toField (MandatoryArg _ x) = toField x
81-
toField (OptionalArg _ (Just x)) = toField x
82-
toField (OptionalArg name Nothing) = throw . DefaultValueNotFound $ name
81+
toField (MandatoryArg _name _atype x) = toField x
82+
toField (OptionalArg _name _atype (Just x)) = toField x
83+
toField (OptionalArg name _atype Nothing) = throw . DefaultValueNotFound $ name
8384

84-
formatArgument :: Argument -> Maybe String
85-
formatArgument (MandatoryArg _name _value) = Just "?"
86-
formatArgument (OptionalArg name (Just _value)) = Just $ name ++
85+
86+
callExpression :: String
8787
#ifdef OlderCallSyntax
88-
" := ?"
88+
callExpression = " := "
8989
#else
90-
" => ?"
90+
callExpression = " => "
9191
#endif
9292

93-
formatArgument (OptionalArg _name Nothing) = Nothing
93+
formatArgument :: Bool -> Argument -> Maybe String
94+
formatArgument cast = format where
95+
format = \case
96+
(MandatoryArg _name atype _value) -> Just $ placeholder atype
97+
98+
(OptionalArg name atype (Just _value)) -> Just . concat $ [
99+
name , callExpression, placeholder atype]
100+
101+
(OptionalArg _name _atype Nothing) -> Nothing
102+
103+
placeholder atype = if cast then ("(?)::" ++ atype) else "?"
104+
94105

95-
formatArguments :: [Argument] -> String
96-
formatArguments = concat . (intersperse ",") . catMaybes . (map formatArgument)
106+
formatArguments :: Bool -> [Argument] -> String
107+
formatArguments cast = concat . (intersperse ",") . catMaybes . (map $ formatArgument cast)
97108

98109
filterArguments :: [Argument] -> [Argument]
99110
filterArguments = filter isPresented where
100111
isPresented :: Argument -> Bool
101-
isPresented (OptionalArg _name Nothing) = False
102-
isPresented _ = True
112+
isPresented (OptionalArg _name _atype Nothing) = False
113+
isPresented _ = True
103114

104115

105116
-- | Example: "varchar" -> PostgresType "varchar"
@@ -193,12 +204,17 @@ traceIdWrapE = id
193204
-- (PGFunction "public" "foo"
194205
-- [PGArgument "x" "varchar" True, PGArgument "y" "bigint" False] (PGSingle "varchar"))
195206
-- args -> { Query $ BS.pack $ concat ["select public.foo (", (formatArguments args), ")"] }
196-
mkSqlQuery :: PostgresBindOptions -> PGFunction -> Maybe Name -> Exp
207+
mkSqlQuery :: PostgresBindOptions -> PGFunction -> Maybe Name -> Q Exp
197208
mkSqlQuery opt (PGFunction schema fname _args ret) argsName =
198-
toQuery . traceIdWrapE . AppE (VarE 'concat) . ListE $ [
199-
mkStrLit $ concat [prefix opt, " ", functionName, "("]
200-
, maybe (mkStrLit "") (\args -> (VarE 'formatArguments) `AppE` (VarE args)) argsName
201-
, mkStrLit ")"] where
209+
lift (pboExplicitCasts opt) >>= \cast ->
210+
return . toQuery . traceIdWrapE . AppE (VarE 'concat) . ListE $ [
211+
mkStrLit $ concat [prefix opt, " ", functionName, "("]
212+
, maybe (mkStrLit "") (\args -> foldl1 AppE [
213+
VarE 'formatArguments
214+
, cast
215+
, VarE args
216+
]) argsName
217+
, mkStrLit ")"] where
202218

203219
prefix (PostgresBindOptions {..}) = case ret of
204220
PGTable _ -> mkSelect AsRow
@@ -240,7 +256,7 @@ traceShowIdWrapE = id
240256
-- [PGArgument "x" "varchar" True, PGArgument "y" "bigint" False] (PGSingle "varchar")) -> {
241257
-- foo conn x1 x2 = query conn
242258
-- (Query $ BS.pack $ concat ["select public.foo (", (formatArguments args), ")"])
243-
-- (filterArguments [MandatoryArg "x1" x1, OptionalArg "x2" x2])
259+
-- (filterArguments [MandatoryArg "x1" x1, OptionalArg "x2" x2]) }
244260
mkFunctionE :: PostgresBindOptions -> PGFunction -> Q Dec
245261
mkFunctionE opt@(PostgresBindOptions {..}) f@(PGFunction _schema _fname args ret) = do
246262
names <- sequence $ replicate (length args) (newName "x")
@@ -250,18 +266,19 @@ mkFunctionE opt@(PostgresBindOptions {..}) f@(PGFunction _schema _fname args ret
250266
True -> return Nothing
251267
False -> Just <$> newName "args"
252268

253-
let wrapArg (PGArgument n _ d) argName = foldl1 AppE $ case d of
254-
False -> [(ConE 'MandatoryArg), (LitE (StringL n)), (VarE argName)]
255-
True -> [(ConE 'OptionalArg), (LitE (StringL n)), (VarE argName)]
269+
let wrapArg (PGArgument n t d) argName = foldl1 AppE $ case d of
270+
False -> [ConE 'MandatoryArg, LitE (StringL n), LitE (StringL t), VarE argName]
271+
True -> [ConE 'OptionalArg, LitE (StringL n), LitE (StringL t), VarE argName]
256272

257273
let argsExpr = (VarE 'filterArguments) `AppE` (ListE $ zipWith wrapArg args names)
258274

259275
let funcName = mkName $ pboFunctionName f
260276
let funcArgs = (VarP connName):(map VarP names)
277+
sqlQuery <- mkSqlQuery opt f argsName
261278
let funcBody = NormalB $ unwrapE opt ret $ foldl1 AppE $ [
262279
VarE $ maybe 'query_ (const 'query) argsName
263280
, VarE connName
264-
, mkSqlQuery opt f argsName
281+
, sqlQuery
265282
] ++ (const (traceShowIdWrapE argsExpr) <$> maybeToList argsName)
266283

267284
let decl = (\name -> ValD (VarP name) (NormalB argsExpr) []) <$> maybeToList argsName

0 commit comments

Comments
 (0)