-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | postgresql-tx interfacing for use with postgresql-query. -- -- Please see the README on GitHub at -- https://github.com/simspace/postgresql-tx#readme @package postgresql-tx-query @version 0.3.0.0 module Database.PostgreSQL.Tx.Query.Internal.Reexport -- | The 1-tuple type or single-value "collection". -- -- This type is structurally equivalent to the Identity type, but -- its intent is more about serving as the anonymous 1-tuple type missing -- from Haskell for attaching typeclass instances. -- -- Parameter usage example: -- --
-- encodeSomething (Only (42::Int)) ---- -- Result usage example: -- --
-- xs <- decodeSomething
-- forM_ xs $ \(Only id) -> {- ... -}
--
newtype Only a
Only :: a -> Only a
[fromOnly] :: Only a -> a
newtype Oid
Oid :: CUInt -> Oid
pgSelectCount :: forall m a q. (Entity a, MonadPostgres m, MonadLogger m, MonadFail m, ToSqlBuilder q) => Proxy a -> q -> m Integer
-- | Select count of entities with given query
--
--
-- activeUsers :: Handler Integer
-- activeUsers = do
-- pgSelectCount (Proxy :: Proxy User)
-- [sqlExp|WHERE active = #{True}|]
--
--
-- Executes arbitrary query and parses it as entities and their ids
pgQueryEntities :: (ToSqlBuilder q, MonadPostgres m, MonadLogger m, Entity a, FromRow a, FromField (EntityId a)) => q -> m [Ent a]
-- | Update entity using ToMarkedRow instanced value. Requires
-- Proxy while EntityId is not a data type.
--
--
-- fixUser :: Text -> EntityId User -> Handler ()
-- fixUser username uid = do
-- pgGetEntity uid
-- >>= maybe notFound run
-- where
-- run user =
-- pgUpdateEntity uid
-- $ MR [("active", mkValue True)
-- ("name", mkValue username)]
--
--
-- Returns True if record was actually updated and False if
-- there was not row with such id (or was more than 1, in fact)
pgUpdateEntity :: forall a b m. (ToMarkedRow b, Entity a, MonadPostgres m, MonadLogger m, ToField (EntityId a), Functor m, Typeable a, Typeable b) => EntityId a -> b -> m Bool
-- | Delete entity.
--
-- -- rmUser :: EntityId User -> Handler () -- rmUser uid = do -- pgDeleteEntity uid ---- -- Return True if row was actually deleted. pgDeleteEntity :: (Entity a, MonadPostgres m, MonadLogger m, ToField (EntityId a), Functor m) => EntityId a -> m Bool -- | Insert many entities without returning list of id like -- pgInsertManyEntitiesId does pgInsertManyEntities :: (Entity a, MonadPostgres m, MonadLogger m, ToRow a) => [a] -> m Int64 -- | Same as pgInsertEntity but insert many entities at one action. -- Returns list of id's of inserted entities pgInsertManyEntitiesId :: (Entity a, MonadPostgres m, MonadLogger m, ToRow a, FromField (EntityId a)) => [a] -> m [EntityId a] -- | Get entity by some fields constraint -- --
-- getUser :: UserName -> Handler User
-- getUser name = do
-- pgGetEntityBy
-- (MR [("name", mkValue name),
-- ("active", mkValue True)])
-- >>= maybe notFound return
--
--
-- The query here will be like
--
--
-- pgQuery [sqlExp|SELECT id, name, phone ... FROM users WHERE name = {True}|]
--
pgGetEntityBy :: forall m a b. (Entity a, MonadPostgres m, MonadLogger m, ToMarkedRow b, FromField (EntityId a), FromRow a, Functor m) => b -> m (Maybe (Ent a))
-- | Select entity by id
--
-- -- getUser :: EntityId User -> Handler User -- getUser uid = do -- pgGetEntity uid -- >>= maybe notFound return --pgGetEntity :: forall m a. (ToField (EntityId a), Entity a, FromRow a, MonadPostgres m, MonadLogger m, Functor m) => EntityId a -> m (Maybe a) -- | Select entities by condition formed from MarkedRow. Usefull -- function when you know pgSelectEntitiesBy :: forall a m b. (Functor m, MonadPostgres m, MonadLogger m, Entity a, ToMarkedRow b, FromRow a, FromField (EntityId a)) => b -> m [Ent a] -- | Same as pgSelectEntities but do not select id pgSelectJustEntities :: (Functor m, MonadPostgres m, MonadLogger m, Entity a, FromRow a, ToSqlBuilder q) => (FN -> FN) -> q -> m [a] -- | Select entities as pairs of (id, entity). -- --
-- handler :: Handler [Ent a]
-- handler = do
-- now <- liftIO getCurrentTime
-- let back = addUTCTime (days (-7)) now
-- pgSelectEntities id
-- [sqlExp|WHERE created BETWEEN #{now} AND #{back}
-- ORDER BY created|]
--
-- handler2 :: Text -> Handler [Ent Foo]
-- handler2 fvalue = do
-- pgSelectEntities ("t"<>)
-- [sqlExp|AS t INNER JOIN table2 AS t2
-- ON t.t2_id = t2.id
-- WHERE t.field = #{fvalue}
-- ORDER BY t2.field2|]
-- -- Here the query will be: SELECT ... FROM tbl AS t INNER JOIN ...
--
pgSelectEntities :: (Functor m, MonadPostgres m, MonadLogger m, Entity a, FromRow a, ToSqlBuilder q, FromField (EntityId a)) => (FN -> FN) -> q -> m [Ent a]
-- | Insert new entity and return it's id
pgInsertEntity :: forall a m. (MonadPostgres m, MonadLogger m, MonadFail m, Entity a, ToRow a, FromField (EntityId a)) => a -> m (EntityId a)
-- | Same as insertEntity but generates query to insert many queries
-- at same time
--
--
-- >>> data Foo = Foo { fName :: Text, fSize :: Int }
--
-- >>> instance Entity Foo where {newtype EntityId Foo = FooId Int ; fieldNames _ = ["name", "size"] ; tableName _ = "foo"}
--
-- >>> instance ToRow Foo where { toRow Foo{..} = [toField fName, toField fSize] }
--
-- >>> runSqlBuilder con $ insertManyEntities $ NL.fromList [Foo "meter" 1, Foo "table" 2, Foo "earth" 151930000000]
-- "INSERT INTO \"foo\" (\"name\",\"size\") VALUES ('meter',1),('table',2),('earth',151930000000)"
--
insertManyEntities :: (Entity a, ToRow a) => NonEmpty a -> SqlBuilder
-- | Generates INSERT INTO query for any instance of Entity
-- and ToRow
--
--
-- >>> data Foo = Foo { fName :: Text, fSize :: Int }
--
-- >>> instance Entity Foo where {newtype EntityId Foo = FooId Int ; fieldNames _ = ["name", "size"] ; tableName _ = "foo"}
--
-- >>> instance ToRow Foo where { toRow Foo{..} = [toField fName, toField fSize] }
--
-- >>> runSqlBuilder con $ insertEntity $ Foo "Enterprise" 910
-- "INSERT INTO \"foo\" (\"name\", \"size\") VALUES ('Enterprise', 910)"
--
insertEntity :: (Entity a, ToRow a) => a -> SqlBuilder
-- | Convert entity instance to marked row to perform inserts updates and
-- same stuff
--
--
-- >>> data Foo = Foo { fName :: Text, fSize :: Int }
--
-- >>> instance Entity Foo where {newtype EntityId Foo = FooId Int ; fieldNames _ = ["name", "size"] ; tableName _ = "foo"}
--
-- >>> instance ToRow Foo where { toRow Foo{..} = [toField fName, toField fSize] }
--
-- >>> runSqlBuilder con $ mrToBuilder ", " $ entityToMR $ Foo "Enterprise" 610
-- " \"name\" = 'Enterprise' , \"size\" = 610 "
--
entityToMR :: (Entity a, ToRow a) => a -> MarkedRow
-- | Generates SELECT FROM WHERE query with most used conditions
--
--
-- >>> data Foo = Foo { fName :: Text, fSize :: Int }
--
-- >>> instance Entity Foo where {newtype EntityId Foo = FooId Int ; fieldNames _ = ["name", "size"] ; tableName _ = "foo"}
--
-- >>> runSqlBuilder con $ selectEntitiesBy id (Proxy :: Proxy Foo) $ MR []
-- "SELECT \"name\", \"size\" FROM \"foo\""
--
--
--
-- >>> runSqlBuilder con $ selectEntitiesBy id (Proxy :: Proxy Foo) $ MR [("name", mkValue "fooname")]
-- "SELECT \"name\", \"size\" FROM \"foo\" WHERE \"name\" = 'fooname' "
--
--
--
-- >>> runSqlBuilder con $ selectEntitiesBy id (Proxy :: Proxy Foo) $ MR [("name", mkValue "fooname"), ("size", mkValue 10)]
-- "SELECT \"name\", \"size\" FROM \"foo\" WHERE \"name\" = 'fooname' AND \"size\" = 10 "
--
selectEntitiesBy :: (Entity a, ToMarkedRow b) => ([FN] -> [FN]) -> Proxy a -> b -> SqlBuilder
-- | Generate SELECT query string for entity
--
--
-- >>> data Foo = Foo { fName :: Text, fSize :: Int }
--
-- >>> instance Entity Foo where {newtype EntityId Foo = FooId Int ; fieldNames _ = ["name", "size"] ; tableName _ = "foo"}
--
-- >>> runSqlBuilder con $ selectEntity (entityFieldsId id) (Proxy :: Proxy Foo)
-- "SELECT \"id\", \"name\", \"size\" FROM \"foo\""
--
--
--
-- >>> runSqlBuilder con $ selectEntity (entityFieldsId ("f"<>)) (Proxy :: Proxy Foo)
-- "SELECT \"f\".\"id\", \"f\".\"name\", \"f\".\"size\" FROM \"foo\""
--
--
-- -- >>> runSqlBuilder con $ selectEntity (entityFields id id) (Proxy :: Proxy Foo) -- "SELECT \"name\", \"size\" FROM \"foo\"" --selectEntity :: Entity a => (Proxy a -> SqlBuilder) -> Proxy a -> SqlBuilder -- | Same as entityFields but prefixes list of names with id -- field. This is shorthand function for often usage. -- --
-- >>> data Foo = Foo { fName :: Text, fSize :: Int }
--
-- >>> instance Entity Foo where {newtype EntityId Foo = FooId Int ; fieldNames _ = ["name", "size"] ; tableName _ = "foo"}
--
-- >>> runSqlBuilder con $ entityFieldsId id (Proxy :: Proxy Foo)
-- "\"id\", \"name\", \"size\""
--
--
--
-- >>> runSqlBuilder con $ entityFieldsId ("f"<>) (Proxy :: Proxy Foo)
-- "\"f\".\"id\", \"f\".\"name\", \"f\".\"size\""
--
entityFieldsId :: Entity a => (FN -> FN) -> Proxy a -> SqlBuilder
-- | Build entity fields
--
--
-- >>> data Foo = Foo { fName :: Text, fSize :: Int }
--
-- >>> instance Entity Foo where {newtype EntityId Foo = FooId Int ; fieldNames _ = ["name", "size"] ; tableName _ = "foo"}
--
-- >>> runSqlBuilder con $ entityFields id id (Proxy :: Proxy Foo)
-- "\"name\", \"size\""
--
--
--
-- >>> runSqlBuilder con $ entityFields ("id":) id (Proxy :: Proxy Foo)
-- "\"id\", \"name\", \"size\""
--
--
--
-- >>> runSqlBuilder con $ entityFields (\l -> ("id":l) ++ ["created"]) id (Proxy :: Proxy Foo)
-- "\"id\", \"name\", \"size\", \"created\""
--
--
--
-- >>> runSqlBuilder con $ entityFields id ("f"<>) (Proxy :: Proxy Foo)
-- "\"f\".\"name\", \"f\".\"size\""
--
--
--
-- >>> runSqlBuilder con $ entityFields ("f.id":) ("f"<>) (Proxy :: Proxy Foo)
-- "\"f\".\"id\", \"f\".\"name\", \"f\".\"size\""
--
entityFields :: Entity a => ([FN] -> [FN]) -> (FN -> FN) -> Proxy a -> SqlBuilder
-- | Perform repsert of the same row, first trying "update where" then
-- "insert" with concatenated fields. Which means that if you run
--
--
-- pgRepsertRow "emails" (MR [("user_id", mkValue uid)]) (MR [("email", mkValue email)])
--
--
-- Then firstly will be performed
--
-- -- UPDATE "emails" SET email = 'foo@bar.com' WHERE "user_id" = 1234 ---- -- And if no one row is affected (which is returned by pgExecute), -- then -- --
-- INSERT INTO "emails" ("user_id", "email") VALUES (1234, 'foo@bar.com')
--
--
-- will be performed
pgRepsertRow :: (MonadPostgres m, MonadLogger m, ToMarkedRow wrow, ToMarkedRow urow) => FN -> wrow -> urow -> m ()
-- | Calls sequently deriveFromRow deriveToRow
-- deriveEntity. E.g. code like this:
--
--
-- data Agent = Agent
-- { aName :: !Text
-- , aAttributes :: !HStoreMap
-- , aLongWeirdName :: !Int
-- } deriving (Ord, Eq, Show)
--
-- $(deriveEverything
-- def { eoIdType = ''Id
-- , eoTableName = textFN . toUnderscore'
-- , eoColumnNames = textFN . toUnderscore' . drop 1
-- , eoDeriveClasses =
-- [''Show, ''Read, ''Ord, ''Eq
-- , ''FromField, ''ToField, ''PathPiece]
-- }
-- ''Agent )
--
--
-- will generate that:
--
--
-- instance ToRow Agent where
-- toRow (Agent a_aE3w a_aE3x a_aE3y)
-- = [toField a_aE3w, toField a_aE3x, toField a_aE3y]
-- instance FromRow Agent where
-- fromRow
-- = Agent $ Database.PostgreSQL.Simple.FromRow.field
-- * Database.PostgreSQL.Simple.FromRow.field
-- * Database.PostgreSQL.Simple.FromRow.field
-- instance Database.PostgreSQL.Query.Entity Agent where
-- newtype EntityId Agent
-- = AgentId {getAgentId :: Id}
-- deriving (Show, Read, Ord, Eq, FromField, ToField, PathPiece)
-- tableName _ = "agent"
-- fieldNames _ = ["name", "attributes", "long_weird_name"]
-- type AgentId = EntityId Agent
--
deriveEverything :: EntityOptions -> Name -> Q [Dec]
-- | Derives instance for Entity using type name and field names.
-- Also generates type synonim for ID. E.g. code like this:
--
--
-- data Agent = Agent
-- { aName :: !Text
-- , aAttributes :: !HStoreMap
-- , aLongWeirdName :: !Int
-- } deriving (Ord, Eq, Show)
--
-- $(deriveEntity
-- def { eoIdType = ''Id
-- , eoTableName = textFN . toUnderscore'
-- , eoColumnNames = textFN . toUnderscore' . drop 1
-- , eoDeriveClasses =
-- [''Show, ''Read, ''Ord, ''Eq
-- , ''FromField, ''ToField, ''PathPiece]
-- }
-- ''Agent )
--
--
-- Will generate code like this:
--
--
-- instance Database.PostgreSQL.Query.Entity Agent where
-- newtype EntityId Agent
-- = AgentId {getAgentId :: Id}
-- deriving (Show, Read, Ord, Eq, FromField, ToField, PathPiece)
-- tableName _ = "agent"
-- fieldNames _ = ["name", "attributes", "long_weird_name"]
-- type AgentId = EntityId Agent
--
--
-- So, you dont need to write it by hands any more.
--
-- NOTE: toUnderscore is from package inflections here
deriveEntity :: EntityOptions -> Name -> Q [Dec]
-- | Options for deriving Entity
data EntityOptions
EntityOptions :: (Text -> FN) -> (Text -> FN) -> [Name] -> Name -> EntityOptions
-- | Type name to table name converter
[eoTableName] :: EntityOptions -> Text -> FN
-- | Record field to column name converter
[eoColumnNames] :: EntityOptions -> Text -> FN
-- | Typeclasses to derive for Id
[eoDeriveClasses] :: EntityOptions -> [Name]
-- | Base type for Id
[eoIdType] :: EntityOptions -> Name
-- | Id type for this entity
data family EntityId a
-- | Auxiliary typeclass for data types which can map to rows of some
-- table. This typeclass is used inside functions like
-- pgSelectEntities to generate queries.
class Entity a where {
-- | Id type for this entity
data family EntityId a;
}
-- | Table name of this entity
tableName :: Entity a => Proxy a -> FN
-- | Field names without id and created. The order of field
-- names must match with order of fields in ToRow and
-- FromRow instances of this type.
fieldNames :: Entity a => Proxy a -> [FN]
-- | Entity with it's id
type Ent a = (EntityId a, a)
-- | If your monad have instance of HasPostgres you maybe dont need
-- this function, unless your instance use withPGPool which
-- acquires connection from pool for each query. If you want to run
-- sequence of queries using same connection you need this function
launchPG :: HasPostgres m => PgMonadT m a -> m a
runPgMonadT :: Connection -> PgMonadT m a -> m a
-- | Turns marked row to query intercalating it with other builder
--
--
-- >>> runSqlBuilder c $ mrToBuilder "AND" $ MR [("name", mkValue "petr"), ("email", mkValue "foo@bar.com")]
-- " \"name\" = 'petr' AND \"email\" = 'foo@bar.com' "
--
mrToBuilder :: SqlBuilder -> MarkedRow -> SqlBuilder
-- | Single field to FN
--
-- -- >>> textFN "hello" -- FN ["hello"] ---- --
-- >>> textFN "user.name" -- FN ["user.name"] ---- -- Note that it does not split string to parts by point like instance of -- IsString does textFN :: Text -> FN -- | Marked row is list of pairs of field name and some sql expression. -- Used to generate queries like: -- --
-- name = name AND size = 10 AND length = 20 ---- -- or -- --
-- UPDATE tbl SET name = name, size = 10, lenght = 20 --newtype MarkedRow MR :: [(FN, SqlBuilder)] -> MarkedRow [unMR] :: MarkedRow -> [(FN, SqlBuilder)] class ToMarkedRow a -- | generate list of pairs (field name, field value) toMarkedRow :: ToMarkedRow a => a -> MarkedRow type MonadPostgres (m :: Type -> Type) = (HasPostgres m, MonadLogger m) -- | Instances of this typeclass can acquire connection and pass it to -- computation. It can be reader of pool of connections or just reader of -- connection class MonadBase IO m => HasPostgres (m :: Type -> Type) withPGConnection :: HasPostgres m => (Connection -> m a) -> m a -- | Empty typeclass signing monad in which transaction is safe. i.e. -- PgMonadT have this instance, but some other monad giving -- connection from e.g. connection pool is not. class TransactionSafe (m :: Type -> Type) -- | Reader of connection. Has instance of HasPostgres. So if you -- have a connection you can run queries in this monad using -- runPgMonadT. Or you can use this transformer to run sequence of -- queries using same connection with launchPG. newtype PgMonadT (m :: Type -> Type) a PgMonadT :: ReaderT Connection m a -> PgMonadT (m :: Type -> Type) a [unPgMonadT] :: PgMonadT (m :: Type -> Type) a -> ReaderT Connection m a -- | Special constructor to perform old-style query interpolation data Qp Qp :: Query -> row -> Qp -- | type to put and get from db inet and cidr typed -- postgresql fields. This should be in postgresql-simple in fact. newtype InetText InetText :: Text -> InetText [unInetText] :: InetText -> Text -- | Dot-separated field name. Each element in nested list will be properly -- quoted and separated by dot. It also have instance of -- ToSqlBuilder and IsString so you can: -- --
-- >>> let a = "hello" :: FN -- -- >>> a -- FN ["hello"] ---- --
-- >>> let b = "user.name" :: FN -- -- >>> b -- FN ["user","name"] ---- --
-- >>> let n = "u.name" :: FN -- -- >>> runSqlBuilder c $ toSqlBuilder n -- "\"u\".\"name\"" ---- --
-- >>> ("user" <> "name") :: FN
-- FN ["user","name"]
--
--
--
-- >>> let a = "name" :: FN
--
-- >>> let b = "email" :: FN
--
-- >>> runSqlBuilder c [sqlExp|^{"u" <> a} = 'name', ^{"e" <> b} = 'email'|]
-- "\"u\".\"name\" = 'name', \"e\".\"email\" = 'email'"
--
newtype FN
FN :: [Text] -> FN
-- | Just like sqlExpEmbed but uses pattern instead of file name.
-- So, code
--
-- -- let query = $(sqlExpFile "foo/bar") ---- -- is just the same as -- --
-- let query = $(sqlExpEmbed "sqlfoobar.sql") ---- -- This function inspired by Yesod's widgetFile sqlExpFile :: String -> Q Exp -- | Embed sql template and perform interpolation -- --
-- let name = "name" -- foo = "bar" -- query = $(sqlExpEmbed "sqlfoobar.sql") -- using foo and bar inside --sqlExpEmbed :: String -> Q Exp -- | Build expression of type SqlBuilder from SQL query with -- interpolation sqlQExp :: String -> Q Exp -- | Removes sequential occurencies of RLit constructors. Also -- removes commentaries and squash sequences of spaces to single space -- symbol squashRope :: [Rope] -> [Rope] ropeParser :: Parser [Rope] parseRope :: String -> [Rope] -- | Maybe the main feature of all library. Quasiquoter which builds -- SqlBuilder from string query. Removes line comments and block -- comments (even nested) and sequences of spaces. Correctly works -- handles string literals and quoted identifiers. Here is examples of -- usage -- --
-- >>> let name = "name"
--
-- >>> let val = "some 'value'"
--
-- >>> runSqlBuilder c [sqlExp|SELECT * FROM tbl WHERE ^{mkIdent name} = #{val}|]
-- "SELECT * FROM tbl WHERE \"name\" = 'some ''value'''"
--
--
-- And more comples example:
--
--
-- >>> let name = Just "name"
--
-- >>> let size = Just 10
--
-- >>> let active = Nothing :: Maybe Bool
--
-- >>> let condlist = catMaybes [ fmap (\a -> [sqlExp|name = #{a}|]) name, fmap (\a -> [sqlExp|size = #{a}|]) size, fmap (\a -> [sqlExp|active = #{a}|]) active]
--
-- >>> let cond = if L.null condlist then mempty else [sqlExp| WHERE ^{mconcat $ L.intersperse " AND " $ condlist} |]
--
-- >>> runSqlBuilder c [sqlExp|SELECT * FROM tbl ^{cond} -- line comment|]
-- "SELECT * FROM tbl WHERE name = 'name' AND size = 10 "
--
sqlExp :: QuasiQuoter
-- | Internal type. Result of parsing sql string
data Rope
-- | Part of raw sql
RLit :: Text -> Rope
-- | Sql comment
RComment :: Text -> Rope
-- | Sequence of spaces
RSpaces :: Int -> Rope
-- | String with haskell expression inside #{..} or #?{..}
RInt :: FieldOption -> Text -> Rope
-- | String with haskell expression inside ^{..}
RPaste :: Text -> Rope
-- | derives ToRow instance for datatype like
--
--
-- data Entity = Entity
-- { eField :: Text
-- , eField2 :: Int
-- , efield3 :: Bool }
--
--
-- it will derive instance like that:
--
-- -- instance ToRow Entity where -- toRow (Entity e1 e2 e3) = -- [ toField e1 -- , toField e2 -- , toField e3 ] --deriveToRow :: Name -> Q [Dec] -- | Derive FromRow instance. i.e. you have type like that -- --
-- data Entity = Entity
-- { eField :: Text
-- , eField2 :: Int
-- , efield3 :: Bool }
--
--
-- then deriveFromRow will generate this instance: instance
-- FromRow Entity where
--
-- -- instance FromRow Entity where -- fromRow = Entity -- <$> field -- <*> field -- <*> field ---- -- Datatype must have just one constructor with arbitrary count of fields deriveFromRow :: Name -> Q [Dec] -- | derives FromField and ToField instances for a sum-type -- enum like -- --
-- data Entity = Red | Green | Blue --derivePgEnum :: InflectorFunc -> Name -> DecsQ -- | Function to transform constructor name into its PG enum conterpart. type InflectorFunc = String -> String -- | Things which always can be transformed to SqlBuilder class ToSqlBuilder a toSqlBuilder :: ToSqlBuilder a => a -> SqlBuilder -- | Unsafe function to make SqlBuilder from arbitrary ByteString. Does not -- perform any checks. Dont use it directly in your code unless you know -- what you are doing. sqlBuilderFromByteString :: ByteString -> SqlBuilder -- | Lift pure bytestring builder to SqlBuilder. This is unsafe to -- use directly in your code. sqlBuilderPure :: Builder -> SqlBuilder sqlBuilderFromField :: ToField a => FieldOption -> a -> SqlBuilder -- | Shorthand function to convert single masked field value (which should -- not be shown in log) mkMaskedValue :: ToField a => a -> SqlBuilder -- | Shorthand function to convert single field value to builder mkValue :: ToField a => a -> SqlBuilder -- | Typed synonym of mempty emptyB :: SqlBuilder -- | Returns query string with log bytestring runSqlBuilder :: Connection -> LogMasker -> SqlBuilder -> IO (Query, ByteString) -- | Builder wich can be effectively concatenated. Requires -- Connection inside for string quoting implemented in -- libpq. Builds two strings: query string and log string which -- may differ. newtype SqlBuilder SqlBuilder :: (Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder [sqlBuild] :: SqlBuilder -> Connection -> LogMasker -> IO SqlBuilderResult -- | Masks fields which size is bigger than given argument in bytes. hugeFieldsMasker :: Int -> LogMasker -- | Simply replaces masked fields with placeholder. defaultLogMasker :: LogMasker -- | Function modifying query parameter value before pasting it to log. type LogMasker = FieldOption -> Builder -> Builder builderResultPure :: Builder -> SqlBuilderResult -- | Result if SqlBuilder. Contains separated builder for query and log. data SqlBuilderResult SqlBuilderResult :: Builder -> Builder -> SqlBuilderResult [sbQueryString] :: SqlBuilderResult -> Builder [sbLogString] :: SqlBuilderResult -> Builder -- | Option for field instructing LogMasker what to do with field -- when logging data FieldOption -- | Do nothing. Field should be pasted as is FieldDefault :: FieldOption -- | Mask field in logs with placeholder. FieldMasked :: FieldOption -- | Represents a VALUES table literal, usable as an alternative -- to executeMany and returning. The main advantage is that -- you can parametrize more than just a single VALUES -- expression. For example, here's a query to insert a thing into one -- table and some attributes of that thing into another, returning the -- new id generated by the database: -- --
-- query c [sql|
-- WITH new_thing AS (
-- INSERT INTO thing (name) VALUES (?) RETURNING id
-- ), new_attributes AS (
-- INSERT INTO thing_attributes
-- SELECT new_thing.id, attrs.*
-- FROM new_thing JOIN ? attrs ON TRUE
-- ) SELECT * FROM new_thing
-- |] ("foo", Values [ "int4", "text" ]
-- [ ( 1 , "hello" )
-- , ( 2 , "world" ) ])
--
--
-- (Note this example uses writable common table expressions, which were
-- added in PostgreSQL 9.1)
--
-- The second parameter gets expanded into the following SQL syntax:
--
-- -- (VALUES (1::"int4",'hello'::"text"),(2,'world')) ---- -- When the list of attributes is empty, the second parameter expands to: -- --
-- (VALUES (null::"int4",null::"text") LIMIT 0) ---- -- By contrast, executeMany and returning don't issue -- the query in the empty case, and simply return 0 and -- [] respectively. This behavior is usually correct given their -- intended use cases, but would certainly be wrong in the example above. -- -- The first argument is a list of postgresql type names. Because this is -- turned into a properly quoted identifier, the type name is case -- sensitive and must be as it appears in the pg_type table. -- Thus, you must write timestamptz instead of timestamp -- with time zone, int4 instead of integer or -- serial, _int8 instead of bigint[], -- etcetera. -- -- You may omit the type names, however, if you do so the list of values -- must be non-empty, and postgresql must be able to infer the types of -- the columns from the surrounding context. If the first condition is -- not met, postgresql-simple will throw an exception without issuing the -- query. In the second case, the postgres server will return an error -- which will be turned into a SqlError exception. -- -- See https://www.postgresql.org/docs/9.5/static/sql-values.html -- for more information. data Values a Values :: [QualifiedIdentifier] -> [a] -> Values a -- | A composite type to parse your custom data structures without having -- to define dummy newtype wrappers every time. -- --
-- instance FromRow MyData where ... ---- --
-- instance FromRow MyData2 where ... ---- -- then I can do the following for free: -- --
-- res <- query' c "..."
-- forM res $ \(MyData{..} :. MyData2{..}) -> do
-- ....
--
data h :. t
(:.) :: h -> t -> (:.) h t
infixr 3 :.
infixr 3 :.
-- | Wrap a list for use as a PostgreSQL array.
newtype PGArray a
PGArray :: [a] -> PGArray a
[fromPGArray] :: PGArray a -> [a]
-- | Wrap a list of values for use in an IN clause. Replaces a
-- single "?" character with a parenthesized list of rendered
-- values.
--
-- Example:
--
-- -- query c "select * from whatever where id in ?" (Only (In [3,4,5])) ---- -- Note that In [] expands to (null), which works as -- expected in the query above, but evaluates to the logical null value -- on every row instead of TRUE. This means that changing the -- query above to ... id NOT in ? and supplying the empty list -- as the parameter returns zero rows, instead of all of them as one -- would expect. -- -- Since postgresql doesn't seem to provide a syntax for actually -- specifying an empty list, which could solve this completely, there are -- two workarounds particularly worth mentioning, namely: -- --
query c "select * from -- whatever where id not in ?" (Only (Values ["int4"] [] :: Values (Only -- Int)))
query c "select * from whatever -- where coalesce(id NOT in ?, TRUE)" (Only (In [] :: In -- [Int]))
query c "select * from whatever where coalesce(id IN -- ?, FALSE)" (Only (In [] :: In [Int]))Note that at as of -- PostgreSQL 9.4, the query planner cannot see inside the -- COALESCE operator, so if you have an index on id -- then you probably don't want to write the last example with -- COALESCE, which would result in a table scan. There are -- further caveats if id can be null or you want null treated -- sensibly as a component of IN or NOT IN.
-- {-# LANGUAGE OverloadedStrings #-}
--
-- import Database.PostgreSQL.Simple
--
-- q :: Query
-- q = "select ?"
--
--
-- The underlying type is a ByteString, and literal Haskell
-- strings that contain Unicode characters will be correctly transformed
-- to UTF-8.
newtype Query
Query :: ByteString -> Query
[fromQuery] :: Query -> ByteString
-- | A type that may be used as a single parameter to a SQL query.
class ToField a
-- | Prepare a value for substitution into a query string.
toField :: ToField a => a -> Action
-- | A collection type that can be turned into a list of rendering
-- Actions.
--
-- Instances should use the toField method of the ToField
-- class to perform conversion of each element of the collection.
--
-- You can derive ToRow for your data type using GHC generics,
-- like this:
--
--
-- {-# LANGUAGE DeriveAnyClass #-}
-- {-# LANGUAGE DeriveGeneric #-}
--
-- import GHC.Generics (Generic)
-- import Database.PostgreSQL.Simple (ToRow)
--
-- data User = User { name :: String, fileQuota :: Int }
-- deriving (Generic, ToRow)
--
--
-- Note that this only works for product types (e.g. records) and does
-- not support sum types or recursive types.
class ToRow a
toRow :: ToRow a => a -> [Action]
data ConnectInfo
ConnectInfo :: String -> Word16 -> String -> String -> String -> ConnectInfo
[connectHost] :: ConnectInfo -> String
[connectPort] :: ConnectInfo -> Word16
[connectUser] :: ConnectInfo -> String
[connectPassword] :: ConnectInfo -> String
[connectDatabase] :: ConnectInfo -> String
data Connection
-- | Default information for setting up a connection.
--
-- Defaults are as follows:
--
--
-- connect defaultConnectInfo { connectHost = "db.example.com" }
--
defaultConnectInfo :: ConnectInfo
-- | Connect with the given username to the given database. Will throw an
-- exception if it cannot connect.
connect :: ConnectInfo -> IO Connection
-- | Attempt to make a connection based on a libpq connection string. See
-- https://www.postgresql.org/docs/9.5/static/libpq-connect.html#LIBPQ-CONNSTRING
-- for more information. Also note that environment variables also affect
-- parameters not provided, parameters provided as the empty string, and
-- a few other things; see
-- https://www.postgresql.org/docs/9.5/static/libpq-envars.html
-- for details. Here is an example with some of the most commonly used
-- parameters:
--
-- -- host='db.somedomain.com' port=5432 ... ---- -- This attempts to connect to db.somedomain.com:5432. Omitting -- the port will normally default to 5432. -- -- On systems that provide unix domain sockets, omitting the host -- parameter will cause libpq to attempt to connect via unix domain -- sockets. The default filesystem path to the socket is constructed from -- the port number and the DEFAULT_PGSOCKET_DIR constant defined -- in the pg_config_manual.h header file. Connecting via unix -- sockets tends to use the peer authentication method, which is -- very secure and does not require a password. -- -- On Windows and other systems without unix domain sockets, omitting the -- host will default to localhost. -- --
-- ... dbname='postgres' user='postgres' password='secret \' \\ pw' ---- -- This attempts to connect to a database named postgres with -- user postgres and password secret ' \ pw. Backslash -- characters will have to be double-quoted in literal Haskell strings, -- of course. Omitting dbname and user will both -- default to the system username that the client process is running as. -- -- Omitting password will default to an appropriate password -- found in the pgpass file, or no password at all if a matching -- line is not found. The path of the pgpass file may be -- specified by setting the PGPASSFILE environment variable. See -- https://www.postgresql.org/docs/9.5/static/libpq-pgpass.html -- for more information regarding this file. -- -- As all parameters are optional and the defaults are sensible, the -- empty connection string can be useful for development and exploratory -- use, assuming your system is set up appropriately. -- -- On Unix, such a setup would typically consist of a local postgresql -- server listening on port 5432, as well as a system user, database -- user, and database sharing a common name, with permissions granted to -- the user on the database. -- -- On Windows, in addition you will either need pg_hba.conf to -- specify the use of the trust authentication method for the -- connection, which may not be appropriate for multiuser or production -- machines, or you will need to use a pgpass file with the -- password or md5 authentication methods. -- -- See -- https://www.postgresql.org/docs/9.5/static/client-authentication.html -- for more information regarding the authentication process. -- -- SSL/TLS will typically "just work" if your postgresql server supports -- or requires it. However, note that libpq is trivially vulnerable to a -- MITM attack without setting additional SSL connection parameters. In -- particular, sslmode needs to be set to require, -- verify-ca, or verify-full in order to perform -- certificate validation. When sslmode is require, -- then you will also need to specify a sslrootcert file, -- otherwise no validation of the server's identity will be performed. -- Client authentication via certificates is also possible via the -- sslcert and sslkey parameters. See -- https://www.postgresql.org/docs/9.5/static/libpq-ssl.html for -- detailed information regarding libpq and SSL. connectPostgreSQL :: ByteString -> IO Connection -- | A type that may be converted from a SQL type. class FromField a -- | Convert a SQL value to a Haskell value. -- -- Returns a list of exceptions if the conversion fails. In the case of -- library instances, this will usually be a single ResultError, -- but may be a UnicodeException. -- -- Note that retaining any reference to the Field argument causes -- the entire LibPQ.Result to be retained. Thus, -- implementations of fromField should return results that do not -- refer to this value after the result have been evaluated to WHNF. -- -- Note that as of postgresql-simple-0.4.0.0, the -- ByteString value has already been copied out of the -- LibPQ.Result before it has been passed to -- fromField. This is because for short strings, it's cheaper to -- copy the string than to set up a finalizer. fromField :: FromField a => FieldParser a -- | A collection type that can be converted from a sequence of fields. -- Instances are provided for tuples up to 10 elements and lists of any -- length. -- -- Note that instances can be defined outside of postgresql-simple, which -- is often useful. For example, here's an instance for a user-defined -- pair: -- --
-- data User = User { name :: String, fileQuota :: Int }
--
-- instance FromRow User where
-- fromRow = User <$> field <*> field
--
--
-- The number of calls to field must match the number of fields
-- returned in a single row of the query result. Otherwise, a
-- ConversionFailed exception will be thrown.
--
-- You can also derive FromRow for your data type using GHC
-- generics, like this:
--
--
-- {-# LANGUAGE DeriveAnyClass #-}
-- {-# LANGUAGE DeriveGeneric #-}
--
-- import GHC.Generics (Generic)
-- import Database.PostgreSQL.Simple (FromRow)
--
-- data User = User { name :: String, fileQuota :: Int }
-- deriving (Generic, FromRow)
--
--
-- Note that this only works for product types (e.g. records) and does
-- not support sum types or recursive types.
--
-- Note that field evaluates its result to WHNF, so the caveats
-- listed in mysql-simple and very early versions of postgresql-simple no
-- longer apply. Instead, look at the caveats associated with
-- user-defined implementations of fromField.
class FromRow a
fromRow :: FromRow a => RowParser a
newtype HStoreMap
HStoreMap :: Map Text Text -> HStoreMap
[fromHStoreMap] :: HStoreMap -> Map Text Text
newtype HStoreList
HStoreList :: [(Text, Text)] -> HStoreList
[fromHStoreList] :: HStoreList -> [(Text, Text)]
-- | Represents escape text, ready to be the key or value to a hstore value
data HStoreText
class ToHStoreText a
toHStoreText :: ToHStoreText a => a -> HStoreText
-- | Represents valid hstore syntax.
data HStoreBuilder
class ToHStore a
toHStore :: ToHStore a => a -> HStoreBuilder
hstore :: (ToHStoreText a, ToHStoreText b) => a -> b -> HStoreBuilder
parseHStoreList :: ByteString -> Either String HStoreList
defaultTransactionMode :: TransactionMode
-- | Of the four isolation levels defined by the SQL standard, these are
-- the three levels distinguished by PostgreSQL as of version 9.0. See
-- https://www.postgresql.org/docs/9.5/static/transaction-iso.html
-- for more information. Note that prior to PostgreSQL 9.0,
-- RepeatableRead was equivalent to Serializable.
data IsolationLevel
-- | the isolation level will be taken from PostgreSQL's per-connection
-- default_transaction_isolation variable, which is initialized
-- according to the server's config. The default configuration is
-- ReadCommitted.
DefaultIsolationLevel :: IsolationLevel
ReadCommitted :: IsolationLevel
RepeatableRead :: IsolationLevel
Serializable :: IsolationLevel
data ReadWriteMode
-- | the read-write mode will be taken from PostgreSQL's per-connection
-- default_transaction_read_only variable, which is initialized
-- according to the server's config. The default configuration is
-- ReadWrite.
DefaultReadWriteMode :: ReadWriteMode
ReadWrite :: ReadWriteMode
ReadOnly :: ReadWriteMode
data TransactionMode
TransactionMode :: !IsolationLevel -> !ReadWriteMode -> TransactionMode
[isolationLevel] :: TransactionMode -> !IsolationLevel
[readWriteMode] :: TransactionMode -> !ReadWriteMode
module Database.PostgreSQL.Tx.Query.Internal
-- | Analogous to TxM but allows for IO. Useful so we can
-- have instances which are required to run postgresql-query
-- functions. See unsafeToPgQueryIO and unsafeFromPgQueryIO
-- for converting to/from TxM.
newtype UnsafePgQueryIO r a
UnsafePgQueryIO :: ReaderT r IO a -> UnsafePgQueryIO r a
-- | Re-export of Logger.
type Logger = Logger
-- | Monad type alias for running postgresql-query via
-- postgresql-tx.
type PgQueryM a = forall r. (PgQueryEnv r) => TxM r a
-- | Runtime environment needed to run postgresql-query via
-- postgresql-tx.
type PgQueryEnv r = (TxEnvs '[Connection, Logger] r) :: Constraint
unsafeToPgQueryIO :: HasCallStack => TxM r a -> UnsafePgQueryIO r a
unsafeFromPgQueryIO :: HasCallStack => UnsafePgQueryIO r a -> TxM r a
unsafeRunPgQueryTransaction :: (PgQueryEnv r, HasCallStack) => (HasCallStack => UnsafePgQueryIO r a -> UnsafePgQueryIO r a) -> r -> TxM r a -> IO a
instance Control.Monad.Catch.MonadThrow (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance Control.Monad.Catch.MonadMask (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance Control.Monad.Catch.MonadCatch (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance Database.PostgreSQL.Query.Types.TransactionSafe (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance Control.Monad.Trans.Control.MonadBaseControl GHC.Types.IO (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance Control.Monad.Base.MonadBase GHC.Types.IO (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance Control.Monad.IO.Class.MonadIO (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance GHC.Base.Monad (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance GHC.Base.Applicative (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance GHC.Base.Functor (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance Database.PostgreSQL.Tx.Query.Internal.PgQueryEnv r => Database.PostgreSQL.Query.Types.HasPostgres (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance Database.PostgreSQL.Tx.Query.Internal.PgQueryEnv r => Control.Monad.Logger.MonadLogger (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
instance Database.PostgreSQL.Tx.Query.Internal.PgQueryEnv r => Control.Monad.Logger.MonadLoggerIO (Database.PostgreSQL.Tx.Query.Internal.UnsafePgQueryIO r)
module Database.PostgreSQL.Tx.Query
-- | Runtime environment needed to run postgresql-query via
-- postgresql-tx.
type PgQueryEnv r = (TxEnvs '[Connection, Logger] r) :: Constraint
-- | Monad type alias for running postgresql-query via
-- postgresql-tx.
type PgQueryM a = forall r. (PgQueryEnv r) => TxM r a
-- | Re-export of Logger.
type Logger = Logger
-- | Analogue of pgWithTransaction.
pgWithTransaction :: (PgQueryEnv r, HasCallStack) => r -> (HasCallStack => TxM r a) -> IO a
-- | Analogue of pgWithTransactionMode.
pgWithTransactionMode :: (PgQueryEnv r, HasCallStack) => TransactionMode -> r -> (HasCallStack => TxM r a) -> IO a
-- | Analogue of pgWithTransactionSerializable Unlike
-- postgresql-query, uses shouldRetryTx to also retry
-- on deadlock_detected, not just
-- serialization_failure.
--
-- Note that any IO that occurs inside the TxM may be
-- executed multiple times.
pgWithTransactionSerializable :: (PgQueryEnv r, HasCallStack) => r -> (HasCallStack => TxM r a) -> IO a
-- | Analogue of pgWithTransactionModeRetry. You should generally
-- prefer pgWithTransactionSerializable.
--
-- Note that any IO that occurs inside the TxM may be
-- executed multiple times.
pgWithTransactionModeRetry :: (PgQueryEnv r, Exception e, HasCallStack) => TransactionMode -> (e -> Bool) -> r -> (HasCallStack => TxM r a) -> IO a
-- | Analogue of pgWithSavepoint.
pgWithSavepoint :: (HasCallStack => PgQueryM a) -> PgQueryM a
-- | Analogue of pgQuery.
pgQuery :: (ToSqlBuilder q, FromRow r, HasCallStack) => q -> PgQueryM [r]
-- | Analogue of pgQueryWithMasker.
pgQueryWithMasker :: (ToSqlBuilder q, FromRow r, HasCallStack) => LogMasker -> q -> PgQueryM [r]
-- | Analogue of pgExecute.
pgExecute :: (ToSqlBuilder q, HasCallStack) => q -> PgQueryM Int64
-- | Analogue of pgExecuteWithMasker.
pgExecuteWithMasker :: (ToSqlBuilder q, HasCallStack) => LogMasker -> q -> PgQueryM Int64