Skip to content
This repository was archived by the owner on Jan 25, 2024. It is now read-only.

Commit 3d64a40

Browse files
authored
Merge pull request #1 from robx/binary-cstring
Avoid copies when passing binary parameters
2 parents 7441ca8 + e7b0be3 commit 3d64a40

File tree

2 files changed

+89
-89
lines changed

2 files changed

+89
-89
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
- Avoid copies when passing binary parameters
2+
13
0.9.4.3
24
-------
35

src/Database/PostgreSQL/LibPQ.hsc

Lines changed: 87 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,70 @@ newtype Oid = Oid CUInt deriving (Eq, Ord, Read, Show, Storable, Typeable)
746746
invalidOid :: Oid
747747
invalidOid = Oid (#const InvalidOid)
748748

749+
750+
-- | Prepare the given parameter bytestring for passing on to libpq,
751+
-- without copying for binary parameters.
752+
--
753+
-- This safe to use to pass parameters to libpq considering:
754+
-- * libpq treats the parameter data as read-only
755+
-- * 'ByteString' uses pinned memory
756+
-- * the reference to the 'CString' doesn't escape
757+
unsafeUseParamAsCString :: (B.ByteString, Format) -> (CString -> IO a) -> IO a
758+
unsafeUseParamAsCString (bs, format) =
759+
case format of
760+
Binary -> B.unsafeUseAsCString bs
761+
Text -> B.useAsCString bs
762+
763+
-- | Convert a list of parameters to the format expected by libpq FFI calls.
764+
withParams :: [Maybe (Oid, B.ByteString, Format)]
765+
-> (CInt -> Ptr Oid -> Ptr CString -> Ptr CInt -> Ptr CInt -> IO a)
766+
-> IO a
767+
withParams params action =
768+
withArray oids $ \ts ->
769+
withMany (maybeWith unsafeUseParamAsCString) values $ \c_values ->
770+
withArray c_values $ \vs ->
771+
withArray c_lengths $ \ls ->
772+
withArrayLen formats $ \n fs ->
773+
action (toEnum n) ts vs ls fs
774+
where
775+
(oids, values, c_lengths, formats) =
776+
foldl' accum ([],[],[],[]) $ reverse params
777+
778+
accum (!a,!b,!c,!d) Nothing = ( invalidOid:a
779+
, Nothing:b
780+
, 0:c
781+
, 0:d
782+
)
783+
accum (!a,!b,!c,!d) (Just (t,v,f)) = ( t:a
784+
, (Just (v,f)):b
785+
, (toEnum $ B.length v):c
786+
, (toEnum $ fromEnum f):d
787+
)
788+
789+
-- | Convert a list of parameters to the format expected by libpq FFI calls,
790+
-- prepared statement variant.
791+
withParamsPrepared :: [Maybe (B.ByteString, Format)]
792+
-> (CInt -> Ptr CString -> Ptr CInt -> Ptr CInt -> IO a)
793+
-> IO a
794+
withParamsPrepared params action =
795+
withMany (maybeWith unsafeUseParamAsCString) values $ \c_values ->
796+
withArray c_values $ \vs ->
797+
withArray c_lengths $ \ls ->
798+
withArrayLen formats $ \n fs ->
799+
action (toEnum n) vs ls fs
800+
where
801+
(values, c_lengths, formats) = foldl' accum ([],[],[]) $ reverse params
802+
803+
accum (!a,!b,!c) Nothing = ( Nothing:a
804+
, 0:b
805+
, 0:c
806+
)
807+
accum (!a,!b,!c) (Just (v, f)) = ( (Just (v,f)):a
808+
, (toEnum $ B.length v):b
809+
, (toEnum $ fromEnum f):c
810+
)
811+
812+
749813
-- | Submits a command to the server and waits for the result.
750814
--
751815
-- Returns a 'Result' or possibly 'Nothing'. A 'Result' will generally
@@ -812,31 +876,12 @@ execParams :: Connection -- ^ connection
812876
-> Format -- ^ result format
813877
-> IO (Maybe Result) -- ^ result
814878
execParams connection statement params rFmt =
815-
do let (oids, values, lengths, formats) =
816-
foldl' accum ([],[],[],[]) $ reverse params
817-
!c_lengths = map toEnum lengths :: [CInt]
818-
!n = toEnum $ length params
819-
!f = toEnum $ fromEnum rFmt
820-
resultFromConn connection $ \c ->
821-
B.useAsCString statement $ \s ->
822-
withArray oids $ \ts ->
823-
withMany (maybeWith B.useAsCString) values $ \c_values ->
824-
withArray c_values $ \vs ->
825-
withArray c_lengths $ \ls ->
826-
withArray formats $ \fs ->
827-
c_PQexecParams c s n ts vs ls fs f
828-
829-
where
830-
accum (!a,!b,!c,!d) Nothing = ( invalidOid:a
831-
, Nothing:b
832-
, 0:c
833-
, 0:d
834-
)
835-
accum (!a,!b,!c,!d) (Just (t,v,f)) = ( t:a
836-
, (Just v):b
837-
, (B.length v):c
838-
, (toEnum $ fromEnum f):d
839-
)
879+
resultFromConn connection $ \c ->
880+
B.useAsCString statement $ \s ->
881+
withParams params $ \n ts vs ls fs ->
882+
c_PQexecParams c s n ts vs ls fs f
883+
where
884+
!f = toEnum $ fromEnum rFmt
840885

841886

842887
-- | Submits a request to create a prepared statement with the given
@@ -911,28 +956,13 @@ execPrepared :: Connection -- ^ connection
911956
-> [Maybe (B.ByteString, Format)] -- ^ parameters
912957
-> Format -- ^ result format
913958
-> IO (Maybe Result) -- ^ result
914-
execPrepared connection stmtName mPairs rFmt =
915-
do let (values, lengths, formats) = foldl' accum ([],[],[]) $ reverse mPairs
916-
!c_lengths = map toEnum lengths :: [CInt]
917-
!n = toEnum $ length mPairs
918-
!f = toEnum $ fromEnum rFmt
919-
resultFromConn connection $ \c ->
920-
B.useAsCString stmtName $ \s ->
921-
withMany (maybeWith B.useAsCString) values $ \c_values ->
922-
withArray c_values $ \vs ->
923-
withArray c_lengths $ \ls ->
924-
withArray formats $ \fs ->
925-
c_PQexecPrepared c s n vs ls fs f
926-
959+
execPrepared connection stmtName params rFmt =
960+
resultFromConn connection $ \c ->
961+
B.useAsCString stmtName $ \s ->
962+
withParamsPrepared params $ \n vs ls fs ->
963+
c_PQexecPrepared c s n vs ls fs f
927964
where
928-
accum (!a,!b,!c) Nothing = ( Nothing:a
929-
, 0:b
930-
, 0:c
931-
)
932-
accum (!a,!b,!c) (Just (v, f)) = ( (Just v):a
933-
, (B.length v):b
934-
, (toEnum $ fromEnum f):c
935-
)
965+
!f = toEnum $ fromEnum rFmt
936966

937967

938968
-- | Submits a request to obtain information about the specified
@@ -1676,31 +1706,13 @@ sendQueryParams :: Connection
16761706
-> Format
16771707
-> IO Bool
16781708
sendQueryParams connection statement params rFmt =
1679-
do let (oids, values, lengths, formats) =
1680-
foldl' accum ([],[],[],[]) $ reverse params
1681-
!c_lengths = map toEnum lengths :: [CInt]
1682-
!n = toEnum $ length params
1683-
!f = toEnum $ fromEnum rFmt
1684-
enumFromConn connection $ \c ->
1685-
B.useAsCString statement $ \s ->
1686-
withArray oids $ \ts ->
1687-
withMany (maybeWith B.useAsCString) values $ \c_values ->
1688-
withArray c_values $ \vs ->
1689-
withArray c_lengths $ \ls ->
1690-
withArray formats $ \fs ->
1691-
c_PQsendQueryParams c s n ts vs ls fs f
1709+
enumFromConn connection $ \c ->
1710+
B.useAsCString statement $ \s ->
1711+
withParams params $ \n ts vs ls fs ->
1712+
c_PQsendQueryParams c s n ts vs ls fs f
16921713

16931714
where
1694-
accum (!a,!b,!c,!d) Nothing = ( invalidOid:a
1695-
, Nothing:b
1696-
, 0:c
1697-
, 0:d
1698-
)
1699-
accum (!a,!b,!c,!d) (Just (t,v,f)) = ( t:a
1700-
, (Just v):b
1701-
, (B.length v):c
1702-
, (toEnum $ fromEnum f):d
1703-
)
1715+
!f = toEnum $ fromEnum rFmt
17041716

17051717

17061718
-- | Sends a request to create a prepared statement with the given
@@ -1726,28 +1738,14 @@ sendQueryPrepared :: Connection
17261738
-> [Maybe (B.ByteString, Format)]
17271739
-> Format
17281740
-> IO Bool
1729-
sendQueryPrepared connection stmtName mPairs rFmt =
1730-
do let (values, lengths, formats) = foldl' accum ([],[],[]) $ reverse mPairs
1731-
!c_lengths = map toEnum lengths :: [CInt]
1732-
!n = toEnum $ length mPairs
1733-
!f = toEnum $ fromEnum rFmt
1734-
enumFromConn connection $ \c ->
1735-
B.useAsCString stmtName $ \s ->
1736-
withMany (maybeWith B.useAsCString) values $ \c_values ->
1737-
withArray c_values $ \vs ->
1738-
withArray c_lengths $ \ls ->
1739-
withArray formats $ \fs ->
1740-
c_PQsendQueryPrepared c s n vs ls fs f
1741+
sendQueryPrepared connection stmtName params rFmt =
1742+
enumFromConn connection $ \c ->
1743+
B.useAsCString stmtName $ \s ->
1744+
withParamsPrepared params $ \n vs ls fs ->
1745+
c_PQsendQueryPrepared c s n vs ls fs f
17411746

17421747
where
1743-
accum (!a,!b,!c) Nothing = ( Nothing:a
1744-
, 0:b
1745-
, 0:c
1746-
)
1747-
accum (!a,!b,!c) (Just (v, f)) = ( (Just v):a
1748-
, (B.length v):b
1749-
, (toEnum $ fromEnum f):c
1750-
)
1748+
!f = toEnum $ fromEnum rFmt
17511749

17521750

17531751
-- | Submits a request to obtain information about the specified

0 commit comments

Comments
 (0)