git.iko.soy/typeable/postgresql-simple
on 153ef46 help sign in
753 lines 27 kB history raw
1{-# LANGUAGE BangPatterns, DeriveDataTypeable, OverloadedStrings, DoAndIfThenElse, RecordWildCards, NamedFieldPuns #-}
3-- |
4-- Module: Database.PostgreSQL.Simple
5-- Copyright: (c) 2011 MailRank, Inc.
6-- (c) 2011 Leon P Smith
7-- License: BSD3
8-- Maintainer: Leon P Smith <leon@melding-monads.com>
9-- Stability: experimental
10-- Portability: portable
11--
12-- A mid-level client library for the MySQL database, aimed at ease of
13-- use and high performance.
15module Database.PostgreSQL.Simple
16 (
17 -- * Writing queries
18 -- $use
20 -- ** The Query type
21 -- $querytype
23 -- ** Parameter substitution
24 -- $subst
26 -- *** Type inference
27 -- $inference
29 -- ** Substituting a single parameter
30 -- $only_param
32 -- ** Representing a list of values
33 -- $in
35 -- ** Modifying multiple rows at once
36 -- $many
38 -- * Extracting results
39 -- $result
41 -- ** Handling null values
42 -- $null
44 -- ** Type conversions
45 -- $types
47 -- * Types
48 Base.ConnectInfo(..)
49 , Connection
50 , Query
51 , In(..)
52 , Binary(..)
53 , Only(..)
54 -- ** Exceptions
55 , FormatError(fmtMessage, fmtQuery, fmtParams)
56 , QueryError(qeMessage, qeQuery)
57 , ResultError(errSQLType, errHaskellType, errMessage)
58 -- * Connection management
59 , Base.connect
60 , Base.connectPostgreSQL
61 , Base.postgreSQLConnectionString
62 , Base.defaultConnectInfo
63 , Base.close
64 -- * Queries that return results
65 , query
66 , query_
67{--
68 -- * Queries that stream results
69 , fold
70 , fold_
71 , forEach
72 , forEach_
73--}
74 -- * Statements that do not return results
75 , execute
76 , execute_
77 , executeMany
78-- , Base.insertID
79 -- * Transaction handling
80 , withTransaction
81-- , Base.autocommit
82 , begin
83 , commit
84 , rollback
85 -- * Helper functions
86 , formatMany
87 , formatQuery
88 ) where
90import Blaze.ByteString.Builder (Builder, fromByteString, toByteString)
91import Blaze.ByteString.Builder.Char8 (fromChar)
92import Control.Applicative ((<$>), pure)
93import Control.Concurrent.MVar
94import Control.Exception (Exception, bracket, onException, throw, throwIO)
95import Control.Monad (forM)
96import Control.Monad.Fix (fix)
97import Data.ByteString (ByteString)
98-- import qualified Data.ByteString as B (unpack)
99import Data.Char(ord)
100import Data.Int (Int64)
101import qualified Data.IntMap as IntMap
102import Data.List (intersperse)
103import Data.Monoid (mappend, mconcat)
104import Data.Typeable (Typeable)
105--import Database.MySQL.Base (Connection, Result)
106--import Database.MySQL.Base.Types (Field)
107import Database.PostgreSQL.Simple.BuiltinTypes (oid2builtin, builtin2typname)
108import Database.PostgreSQL.Simple.Param (Action(..), inQuotes)
109import Database.PostgreSQL.Simple.QueryParams (QueryParams(..))
110import Database.PostgreSQL.Simple.Result (ResultError(..))
111import Database.PostgreSQL.Simple.QueryResults (QueryResults(..))
112-- import Database.PostgreSQL.Simple.Result (ResultError(..))
113import Database.PostgreSQL.Simple.Types (Binary(..), In(..), Only(..), Query(..))
114import Database.PostgreSQL.Simple.Internal as Base
115import qualified Database.PostgreSQL.LibPQ as PQ
116import Text.Regex.PCRE.Light (compile, caseless, match)
117import qualified Data.ByteString.Char8 as B
118--import qualified Database.MySQL.Base as Base
120-- | Exception thrown if a 'Query' could not be formatted correctly.
121-- This may occur if the number of \'@?@\' characters in the query
122-- string does not match the number of parameters provided.
123data FormatError = FormatError {
124 fmtMessage :: String
125 , fmtQuery :: Query
126 , fmtParams :: [ByteString]
127 } deriving (Eq, Show, Typeable)
129instance Exception FormatError
131-- | Exception thrown if 'query' is used to perform an @INSERT@-like
132-- operation, or 'execute' is used to perform a @SELECT@-like operation.
133data QueryError = QueryError {
134 qeMessage :: String
135 , qeQuery :: Query
136 } deriving (Eq, Show, Typeable)
138instance Exception QueryError
140-- | Format a query string.
141--
142-- This function is exposed to help with debugging and logging. Do not
143-- use it to prepare queries for execution.
144--
145-- String parameters are escaped according to the character set in use
146-- on the 'Connection'.
147--
148-- Throws 'FormatError' if the query string could not be formatted
149-- correctly.
150formatQuery :: QueryParams q => Connection -> Query -> q -> IO ByteString
151formatQuery conn q@(Query template) qs
152 | null xs && '?' `B.notElem` template = return template
153 | otherwise = toByteString <$> buildQuery conn q template xs
154 where xs = renderParams qs
156-- | Format a query string with a variable number of rows.
157--
158-- This function is exposed to help with debugging and logging. Do not
159-- use it to prepare queries for execution.
160--
161-- The query string must contain exactly one substitution group,
162-- identified by the SQL keyword \"@VALUES@\" (case insensitive)
163-- followed by an \"@(@\" character, a series of one or more \"@?@\"
164-- characters separated by commas, and a \"@)@\" character. White
165-- space in a substitution group is permitted.
166--
167-- Throws 'FormatError' if the query string could not be formatted
168-- correctly.
169formatMany :: (QueryParams q) => Connection -> Query -> [q] -> IO ByteString
170formatMany _ q [] = fmtError "no rows supplied" q []
171formatMany conn q@(Query template) qs = do
172 case match re template [] of
173 Just [_,before,qbits,after] -> do
174 bs <- mapM (buildQuery conn q qbits . renderParams) qs
175 return . toByteString . mconcat $ fromByteString before :
176 intersperse (fromChar ',') bs ++
177 [fromByteString after]
178 _ -> error "foo"
179 where
180 re = compile "^([^?]+\\bvalues\\s*)\
181 \(\\(\\s*[?](?:\\s*,\\s*[?])*\\s*\\))\
182 \([^?]*)$"
183 [caseless]
185escapeStringConn conn s = withConnection conn $ \c -> do
186 PQ.escapeStringConn c s
188buildQuery :: Connection -> Query -> ByteString -> [Action] -> IO Builder
189buildQuery conn q template xs = zipParams (split template) <$> mapM sub xs
190 where quote = inQuotes . fromByteString . maybe undefined id
191 sub (Plain b) = pure b
192 sub (Escape s) = quote <$> escapeStringConn conn s
193 sub (Many ys) = mconcat <$> mapM sub ys
194 split s = fromByteString h : if B.null t then [] else split (B.tail t)
195 where (h,t) = B.break (=='?') s
196 zipParams (t:ts) (p:ps) = t `mappend` p `mappend` zipParams ts ps
197 zipParams [t] [] = t
198 zipParams _ _ = fmtError (show (B.count '?' template) ++
199 " '?' characters, but " ++
200 show (length xs) ++ " parameters") q xs
202-- | Execute an @INSERT@, @UPDATE@, or other SQL query that is not
203-- expected to return results.
204--
205-- Returns the number of rows affected.
206--
207-- Throws 'FormatError' if the query could not be formatted correctly.
208execute :: (QueryParams q) => Connection -> Query -> q -> IO Int64
209execute conn template qs = do
210 result <- exec conn =<< formatQuery conn template qs
211 finishExecute conn template result
213-- | A version of 'execute' that does not perform query substitution.
214execute_ :: Connection -> Query -> IO Int64
215execute_ conn q@(Query stmt) = do
216 result <- exec conn stmt
217 finishExecute conn q result
219-- | Execute a multi-row @INSERT@, @UPDATE@, or other SQL query that is not
220-- expected to return results.
221--
222-- Returns the number of rows affected.
223--
224-- Throws 'FormatError' if the query could not be formatted correctly.
225executeMany :: (QueryParams q) => Connection -> Query -> [q] -> IO Int64
226executeMany _ _ [] = return 0
227executeMany conn q qs = do
228 result <- exec conn =<< formatMany conn q qs
229 finishExecute conn q result
231finishExecute :: Connection -> Query -> PQ.Result -> IO Int64
232finishExecute conn q result = do
233 status <- PQ.resultStatus result
234 case status of
235 PQ.EmptyQuery -> fail "empty query" -- FIXME: error message
236 PQ.CommandOk -> do
237 ncols <- PQ.nfields result
238 if ncols /= 0
239 then throwIO $ QueryError ("execute resulted in " ++ show ncols ++
240 "-column result") q
241 else do
242 nstr <- PQ.cmdTuples result
243 return $ case nstr of
244 Nothing -> 0 -- is this appropriate?
245 Just str -> toInteger str
246 PQ.TuplesOk -> do
247 ncols <- PQ.nfields result
248 throwIO $ QueryError ("execute resulted in " ++ show ncols ++
249 "-column result") q
250 PQ.CopyIn -> fail "FIXME: postgresql-simple does not currently support COPY IN"
251 PQ.CopyOut -> fail "FIXME: postgresql-simple does not currently support COPY OUT"
252 _ -> do
253 errormsg <- maybe "" id <$> PQ.resultErrorMessage result
254 statusmsg <- PQ.resStatus status
255 state <- maybe "" id <$> PQ.resultErrorField result PQ.DiagSqlstate
256 throw $ SqlError { sqlState = state
257 , sqlNativeError = 0 -- FIXME
258 , sqlErrorMsg = B.concat [ "execute: ", statusmsg
259 , ": ", errormsg ]}
260 where
261 toInteger str = B.foldl' delta 0 str
262 where
263 delta acc c =
264 if '0' <= c && c <= '9'
265 then 10 * acc + fromIntegral (ord c - ord '0')
266 else error ("finishExecute: not an int: " ++ B.unpack str)
270-- | Perform a @SELECT@ or other SQL query that is expected to return
271-- results. All results are retrieved and converted before this
272-- function returns.
273--
274-- When processing large results, this function will consume a lot of
275-- client-side memory. Consider using 'fold' instead.
276--
277-- Exceptions that may be thrown:
278--
279-- * 'FormatError': the query string could not be formatted correctly.
280--
281-- * 'QueryError': the result contains no columns (i.e. you should be
282-- using 'execute' instead of 'query').
283--
284-- * 'ResultError': result conversion failed.
285query :: (QueryParams q, QueryResults r)
286 => Connection -> Query -> q -> IO [r]
287query conn template qs = do
288 result <- exec conn =<< formatQuery conn template qs
289 finishQuery conn template result
291-- | A version of 'query' that does not perform query substitution.
292query_ :: (QueryResults r) => Connection -> Query -> IO [r]
293query_ conn q@(Query que) = do
294 result <- exec conn que
295 finishQuery conn q result
296{--
297-- | Perform a @SELECT@ or other SQL query that is expected to return
298-- results. Results are streamed incrementally from the server, and
299-- consumed via a left fold.
300--
301-- The result consumer must be carefully written to execute
302-- quickly. If the consumer is slow, server resources will be tied up,
303-- and other clients may not be able to update the tables from which
304-- the results are being streamed.
305--
306-- When dealing with small results, it may be simpler (and perhaps
307-- faster) to use 'query' instead.
308--
309-- This fold is /not/ strict. The stream consumer is responsible for
310-- forcing the evaluation of its result to avoid space leaks.
311--
312-- Exceptions that may be thrown:
313--
314-- * 'FormatError': the query string could not be formatted correctly.
315--
316-- * 'QueryError': the result contains no columns (i.e. you should be
317-- using 'execute' instead of 'query').
318--
319-- * 'ResultError': result conversion failed.
320fold :: (QueryParams q, QueryResults r) =>
321 Connection
322 -> Query -- ^ Query template.
323 -> q -- ^ Query parameters.
324 -> a -- ^ Initial state for result consumer.
325 -> (a -> r -> IO a) -- ^ Result consumer.
326 -> IO a
327fold conn template qs z f = do
328 Base.query conn =<< formatQuery conn template qs
329 finishFold conn template z f
330--}
331{--
332-- | A version of 'fold' that does not perform query substitution.
333fold_ :: (QueryResults r) =>
334 Connection
335 -> Query -- ^ Query.
336 -> a -- ^ Initial state for result consumer.
337 -> (a -> r -> IO a) -- ^ Result consumer.
338 -> IO a
339fold_ conn q@(Query que) z f = do
340 Base.query conn que
341 finishFold conn q z f
342--}
343{--
344-- | A version of 'fold' that does not transform a state value.
345forEach :: (QueryParams q, QueryResults r) =>
346 Connection
347 -> Query -- ^ Query template.
348 -> q -- ^ Query parameters.
349 -> (r -> IO ()) -- ^ Result consumer.
350 -> IO ()
351forEach conn template qs = fold conn template qs () . const
352{-# INLINE forEach #-}
354-- | A version of 'forEach' that does not perform query substitution.
355forEach_ :: (QueryResults r) =>
356 Connection
357 -> Query -- ^ Query template.
358 -> (r -> IO ()) -- ^ Result consumer.
359 -> IO ()
360forEach_ conn template = fold_ conn template () . const
361{-# INLINE forEach_ #-}
362--}
364forM' :: (Ord n, Num n) => n -> n -> (n -> IO a) -> IO [a]
365forM' lo hi m = loop hi []
366 where
367 loop !n !as
368 | n < lo = return as
369 | otherwise = do
370 a <- m n
371 loop (n-1) (a:as)
373finishQuery :: (QueryResults r) => Connection -> Query -> PQ.Result -> IO [r]
374finishQuery conn q result = do
375 status <- PQ.resultStatus result
376 case status of
377 PQ.EmptyQuery -> fail "empty query" -- FIXME: error message
378 PQ.CommandOk -> do
379 throwIO $ QueryError "query resulted in a command response" q
380 PQ.TuplesOk -> do
381 ncols <- PQ.nfields result
382 fields <- forM' 0 (ncols-1) $ \column -> do
383 type_oid <- PQ.ftype result column
384 typename <- getTypename conn type_oid
385 return Field{..}
386 nrows <- PQ.ntuples result
387 forM' 0 (nrows-1) $ \row -> do
388 values <- forM' 0 (ncols-1) (\col -> PQ.getvalue' result row col)
389 case convertResults fields values of
390 Left err -> throwIO err
391 Right a -> return a
392 PQ.CopyIn -> fail "FIXME: postgresql-simple does not currently support COPY IN"
393 PQ.CopyOut -> fail "FIXME: postgresql-simple does not currently support COPY OUT"
394 _ -> do
395 errormsg <- maybe "" id <$> PQ.resultErrorMessage result
396 statusmsg <- PQ.resStatus status
397 state <- maybe "" id <$> PQ.resultErrorField result PQ.DiagSqlstate
398 throw $ SqlError { sqlState = state
399 , sqlNativeError = 0 -- FIXME
400 , sqlErrorMsg = B.concat [ "execute: ", statusmsg
401 , ": ", errormsg ]}
403{--
404withResult (Base.storeResult conn) q $ \r fs -> do
405 status <-
406/ flip fix [] $ \loop acc -> do
407 row <- Base.fetchRow r
408 case row of
409 [] -> return (reverse acc)
410 _ -> let !c = convertResults fs row
411 in loop (c:acc)
412--}
413{--
414finishFold :: (QueryResults r) =>
415 Connection -> Query -> a -> (a -> r -> IO a) -> IO a
416finishFold conn q z0 f = withResult (Base.useResult conn) q $ \r fs ->
417 flip fix z0 $ \loop z -> do
418 row <- Base.fetchRow r
419 case row of
420 [] -> return z
421 _ -> (f z $! convertResults fs row) >>= loop
422--}
423{--
424withResult :: (IO Result) -> Query -> (Result -> [Field] -> IO a) -> IO a
425withResult fetchResult q act = bracket fetchResult Base.freeResult $ \r -> do
426 ncols <- Base.fieldCount (Right r)
427 if ncols == 0
428 then throwIO $ QueryError "query resulted in zero-column result" q
429 else act r =<< Base.fetchFields r
430--}
431-- | Execute an action inside a SQL transaction.
432--
433-- This function initiates a transaction with a \"@begin
434-- transaction@\" statement, then executes the supplied action. If
435-- the action succeeds, the transaction will be completed with
436-- 'Base.commit' before this function returns.
437--
438-- If the action throws /any/ kind of exception (not just a
439-- MySQL-related exception), the transaction will be rolled back using
440-- 'Base.rollback', then the exception will be rethrown.
441withTransaction :: Connection -> IO a -> IO a
442withTransaction conn act = do
443 _ <- begin conn
444 r <- act `onException` rollback conn
445 commit conn
446 return r
448-- | Rollback a transaction.
449-- rollback :: (MonadCatchIO m,MonadIO m) => Connection -> m ()
450rollback :: Connection -> IO ()
451rollback conn = do
452 _ <- execute_ conn "ABORT;"
453 return ()
455-- | Commit a transaction.
456-- commit :: (MonadCatchIO m,MonadIO m) => Connection -> m ()
457commit :: Connection -> IO ()
458commit conn = do
459 _ <- execute_ conn "COMMIT;"
460 return ()
462-- | Begin a transaction.
463-- begin :: (MonadCatchIO m,MonadIO m) => Connection -> m ()
464begin :: Connection -> IO ()
465begin conn = do
466 _ <- execute_ conn "BEGIN;"
467 return ()
469fmtError :: String -> Query -> [Action] -> a
470fmtError msg q xs = throw FormatError {
471 fmtMessage = msg
472 , fmtQuery = q
473 , fmtParams = map twiddle xs
474 }
475 where twiddle (Plain b) = toByteString b
476 twiddle (Escape s) = s
477 twiddle (Many ys) = B.concat (map twiddle ys)
479-- $use
480--
481-- SQL-based applications are somewhat notorious for their
482-- susceptibility to attacks through the injection of maliciously
483-- crafted data. The primary reason for widespread vulnerability to
484-- SQL injections is that many applications are sloppy in handling
485-- user data when constructing SQL queries.
486--
487-- This library provides a 'Query' type and a parameter substitution
488-- facility to address both ease of use and security.
490-- $querytype
491--
492-- A 'Query' is a @newtype@-wrapped 'ByteString'. It intentionally
493-- exposes a tiny API that is not compatible with the 'ByteString'
494-- API; this makes it difficult to construct queries from fragments of
495-- strings. The 'query' and 'execute' functions require queries to be
496-- of type 'Query'.
497--
498-- To most easily construct a query, enable GHC's @OverloadedStrings@
499-- language extension and write your query as a normal literal string.
500--
501-- > {-# LANGUAGE OverloadedStrings #-}
502-- >
503-- > import Database.MySQL.Simple
504-- >
505-- > hello = do
506-- > conn <- connect defaultConnectInfo
507-- > query conn "select 2 + 2"
508--
509-- A 'Query' value does not represent the actual query that will be
510-- executed, but is a template for constructing the final query.
512-- $subst
513--
514-- Since applications need to be able to construct queries with
515-- parameters that change, this library provides a query substitution
516-- capability.
517--
518-- The 'Query' template accepted by 'query' and 'execute' can contain
519-- any number of \"@?@\" characters. Both 'query' and 'execute'
520-- accept a third argument, typically a tuple. When constructing the
521-- real query to execute, these functions replace the first \"@?@\" in
522-- the template with the first element of the tuple, the second
523-- \"@?@\" with the second element, and so on. If necessary, each
524-- tuple element will be quoted and escaped prior to substitution;
525-- this defeats the single most common injection vector for malicious
526-- data.
527--
528-- For example, given the following 'Query' template:
529--
530-- > select * from user where first_name = ? and age > ?
531--
532-- And a tuple of this form:
533--
534-- > ("Boris" :: String, 37 :: Int)
535--
536-- The query to be executed will look like this after substitution:
537--
538-- > select * from user where first_name = 'Boris' and age > 37
539--
540-- If there is a mismatch between the number of \"@?@\" characters in
541-- your template and the number of elements in your tuple, a
542-- 'FormatError' will be thrown.
543--
544-- Note that the substitution functions do not attempt to parse or
545-- validate your query. It's up to you to write syntactically valid
546-- SQL, and to ensure that each \"@?@\" in your query template is
547-- matched with the right tuple element.
549-- $inference
550--
551-- Automated type inference means that you will often be able to avoid
552-- supplying explicit type signatures for the elements of a tuple.
553-- However, sometimes the compiler will not be able to infer your
554-- types. Consider a care where you write a numeric literal in a
555-- parameter tuple:
556--
557-- > query conn "select ? + ?" (40,2)
558--
559-- The above query will be rejected by the compiler, because it does
560-- not know the specific numeric types of the literals @40@ and @2@.
561-- This is easily fixed:
562--
563-- > query conn "select ? + ?" (40 :: Double, 2 :: Double)
564--
565-- The same kind of problem can arise with string literals if you have
566-- the @OverloadedStrings@ language extension enabled. Again, just
567-- use an explicit type signature if this happens.
569-- $only_param
570--
571-- Haskell lacks a single-element tuple type, so if you have just one
572-- value you want substituted into a query, what should you do?
573--
574-- The obvious approach would appear to be something like this:
575--
576-- > instance (Param a) => QueryParam a where
577-- > ...
578--
579-- Unfortunately, this wreaks havoc with type inference, so we take a
580-- different tack. To represent a single value @val@ as a parameter, write
581-- a singleton list @[val]@, use 'Just' @val@, or use 'Only' @val@.
582--
583-- Here's an example using a singleton list:
584--
585-- > execute conn "insert into users (first_name) values (?)"
586-- > ["Nuala"]
588-- $in
589--
590-- Suppose you want to write a query using an @IN@ clause:
591--
592-- > select * from users where first_name in ('Anna', 'Boris', 'Carla')
593--
594-- In such cases, it's common for both the elements and length of the
595-- list after the @IN@ keyword to vary from query to query.
596--
597-- To address this case, use the 'In' type wrapper, and use a single
598-- \"@?@\" character to represent the list. Omit the parentheses
599-- around the list; these will be added for you.
600--
601-- Here's an example:
602--
603-- > query conn "select * from users where first_name in ?" $
604-- > In ["Anna", "Boris", "Carla"]
605--
606-- If your 'In'-wrapped list is empty, the string @\"(null)\"@ will be
607-- substituted instead, to ensure that your clause remains
608-- syntactically valid.
610-- $many
611--
612-- If you know that you have many rows of data to insert into a table,
613-- it is much more efficient to perform all the insertions in a single
614-- multi-row @INSERT@ statement than individually.
615--
616-- The 'executeMany' function is intended specifically for helping
617-- with multi-row @INSERT@ and @UPDATE@ statements. Its rules for
618-- query substitution are different than those for 'execute'.
619--
620-- What 'executeMany' searches for in your 'Query' template is a
621-- single substring of the form:
622--
623-- > values (?,?,?)
624--
625-- The rules are as follows:
626--
627-- * The keyword @VALUES@ is matched case insensitively.
628--
629-- * There must be no other \"@?@\" characters anywhere in your
630-- template.
631--
632-- * There must one or more \"@?@\" in the parentheses.
633--
634-- * Extra white space is fine.
635--
636-- The last argument to 'executeMany' is a list of parameter
637-- tuples. These will be substituted into the query where the @(?,?)@
638-- string appears, in a form suitable for use in a multi-row @INSERT@
639-- or @UPDATE@.
640--
641-- Here is an example:
642--
643-- > executeMany conn
644-- > "insert into users (first_name,last_name) values (?,?)"
645-- > [("Boris","Karloff"),("Ed","Wood")]
646--
647-- The query that will be executed here will look like this
648-- (reformatted for tidiness):
649--
650-- > insert into users (first_name,last_name) values
651-- > ('Boris','Karloff'),('Ed','Wood')
653-- $result
654--
655-- The 'query' and 'query_' functions return a list of values in the
656-- 'QueryResults' typeclass. This class performs automatic extraction
657-- and type conversion of rows from a query result.
658--
659-- Here is a simple example of how to extract results:
660--
661-- > import qualified Data.Text as Text
662-- >
663-- > xs <- query_ conn "select name,age from users"
664-- > forM_ xs $ \(name,age) ->
665-- > putStrLn $ Text.unpack name ++ " is " ++ show (age :: Int)
666--
667-- Notice two important details about this code:
668--
669-- * The number of columns we ask for in the query template must
670-- exactly match the number of elements we specify in a row of the
671-- result tuple. If they do not match, a 'ResultError' exception
672-- will be thrown.
673--
674-- * Sometimes, the compiler needs our help in specifying types. It
675-- can infer that @name@ must be a 'Text', due to our use of the
676-- @unpack@ function. However, we have to tell it the type of @age@,
677-- as it has no other information to determine the exact type.
679-- $null
680--
681-- The type of a result tuple will look something like this:
682--
683-- > (Text, Int, Int)
684--
685-- Although SQL can accommodate @NULL@ as a value for any of these
686-- types, Haskell cannot. If your result contains columns that may be
687-- @NULL@, be sure that you use 'Maybe' in those positions of of your
688-- tuple.
689--
690-- > (Text, Maybe Int, Int)
691--
692-- If 'query' encounters a @NULL@ in a row where the corresponding
693-- Haskell type is not 'Maybe', it will throw a 'ResultError'
694-- exception.
696-- $only_result
697--
698-- To specify that a query returns a single-column result, use the
699-- 'Only' type.
700--
701-- > xs <- query_ conn "select id from users"
702-- > forM_ xs $ \(Only dbid) -> {- ... -}
704-- $types
705--
706-- Conversion of SQL values to Haskell values is somewhat
707-- permissive. Here are the rules.
708--
709-- * For numeric types, any Haskell type that can accurately represent
710-- all values of the given MySQL type is considered \"compatible\".
711-- For instance, you can always extract a MySQL @TINYINT@ column to
712-- a Haskell 'Int'. The Haskell 'Float' type can accurately
713-- represent MySQL integer types of size up to @INT24@, so it is
714-- considered compatble with those types.
715--
716-- * A numeric compatibility check is based only on the type of a
717-- column, /not/ on its values. For instance, a MySQL @LONG_LONG@
718-- column will be considered incompatible with a Haskell 'Int8',
719-- even if it contains the value @1@.
720--
721-- * If a numeric incompatibility is found, 'query' will throw a
722-- 'ResultError'.
723--
724-- * The 'String' and 'Text' types are assumed to be encoded as
725-- UTF-8. If you use some other encoding, decoding may fail or give
726-- wrong results. In such cases, write a @newtype@ wrapper and a
727-- custom 'Result' instance to handle your encoding.
729getTypename :: Connection -> PQ.Oid -> IO ByteString
730getTypename conn@Connection{..} oid =
731 case oid2builtin oid of
732 Just builtin -> return $! builtin2typname builtin
733 Nothing -> do
734 oidmap <- takeMVar connectionObjects
735 case IntMap.lookup (oid2int oid) oidmap of
736 Just name -> putMVar connectionObjects oidmap >> return name
737 Nothing -> do
738 names <- query conn "SELECT typname FROM pg_type WHERE oid=?"
739 (Only oid)
740 name <- case names of
741 [] -> return $ throw SqlError {
742 sqlNativeError = -1,
743 sqlErrorMsg = "invalid type oid",
744 sqlState = ""
745 }
746 [Only x] -> return x
747 _ -> fail "typename query returned more than one result"
748 -- oid is a primary key, so the query should
749 -- never return more than one result
750 putMVar connectionObjects (IntMap.insert (oid2int oid) name oidmap)
751 return name
752 `onException` do
753 putMVar connectionObjects oidmap