forked from tnarg/haskell-libpq
-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathLibPQ.hsc
2661 lines (2236 loc) · 102 KB
/
LibPQ.hsc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-----------------------------------------------------------------------------
-- |
-- Module : Database.PostgreSQL.LibPQ
-- Copyright : (c) 2010 Grant Monroe,
-- (c) 2011 Leon P Smith
-- License : BSD3
--
-- Maintainer : [email protected]
-- Stability : experimental
--
-- This is a binding to libpq: the C application programmer's
-- interface to PostgreSQL. libpq is a set of library functions that
-- allow client programs to pass queries to the PostgreSQL backend
-- server and to receive the results of these queries.
--
-- This is intended to be a very low-level interface to libpq. It
-- provides memory management and a somewhat more consistent interface
-- to error conditions. Application code should typically use a
-- higher-level PostgreSQL binding.
--
-- This interface is not safe, because libpq unfortunately conflates
-- explicit disconnects with memory management. A use-after-free memory
-- fault will result if a connection is used in any way after 'finish' is
-- called. This will likely cause a segfault, or return an error if memory
-- has not yet been reused. Other more bizarre behaviors are possible,
-- though unlikely by chance. Higher-level bindings need to be aware of
-- this issue and need to ensure that application code cannot cause the
-- functions in this module to be called on an 'finish'ed connection.
--
-- One possibility is to represent a connection in a higher-level interface
-- as @MVar (Maybe Connection)@, using @Nothing@ to represent an explicitly
-- disconnected state. This was done in an earlier incarnation of this
-- library, however this was removed because a higher level binding is
-- likely to use a similar construct to deal with other issues. Thus
-- incorporating that in this module results in extra layers of indirection
-- for relatively little functionality.
--
-----------------------------------------------------------------------------
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Database.PostgreSQL.LibPQ
(
-- * Database Connection Control Functions
-- $dbconn
Connection
, connectdb
, connectStart
, connectPoll
, newNullConnection
, isNullConnection
--, conndefaults
--, conninfoParse
, reset
, resetStart
, resetPoll
, PollingStatus(..)
, finish
-- * Connection Status Functions
-- $connstatus
, db
, user
, pass
, host
, port
, options
, ConnStatus(..)
, status
, TransactionStatus(..)
, transactionStatus
, parameterStatus
, protocolVersion
, serverVersion
, errorMessage
, socket
, backendPID
, connectionNeedsPassword
, connectionUsedPassword
--, getssl
-- * Command Execution Functions
-- $commandexec
, Result
, exec
, Format(..)
, Oid(..)
, invalidOid
, execParams
, prepare
, execPrepared
, describePrepared
, describePortal
, ExecStatus(..)
, resultStatus
, resStatus
, resultErrorMessage
, FieldCode(..)
, resultErrorField
, unsafeFreeResult
-- * Retrieving Query Result Information
-- $queryresultinfo
, ntuples
, nfields
, Row(..)
, Column(..)
, toRow
, toColumn
, fname
, fnumber
, ftable
, ftablecol
, fformat
, ftype
, fmod
, fsize
, getvalue
, getvalue'
, getisnull
, getlength
, nparams
, paramtype
-- Retrieving Result Information for Other Commands
-- $othercommands
, cmdStatus
, cmdTuples
-- * Escaping Strings for Inclusion in SQL Commands
, escapeStringConn
-- * Escaping Binary Strings for Inclusion in SQL Commands
, escapeByteaConn
, unescapeBytea
-- * Escaping Identifiers for Inclusion in SQL Commands
, escapeIdentifier
-- * Using COPY
-- $copy
, CopyInResult(..)
, putCopyData
, putCopyEnd
, CopyOutResult(..)
, getCopyData
-- * Asynchronous Command Processing
-- $asynccommand
, sendQuery
, sendQueryParams
, sendPrepare
, sendQueryPrepared
, sendDescribePrepared
, sendDescribePortal
, getResult
, consumeInput
, isBusy
, setnonblocking
, isnonblocking
, FlushStatus(..)
, flush
-- * Cancelling Queries in Progress
-- $cancel
, Cancel
, getCancel
, cancel
-- * Asynchronous Notification
-- $asyncnotification
, Notify(..)
, notifies
-- * Control Functions
-- $control
, clientEncoding
, setClientEncoding
, Verbosity(..)
, setErrorVerbosity
-- * Nonfatal Error Reporting
, disableNoticeReporting
, enableNoticeReporting
, getNotice
-- * Large Objects
-- $largeobjects
, LoFd(..)
, loCreat
, loCreate
, loImport
, loImportWithOid
, loExport
, loOpen
, loWrite
, loRead
, loSeek
, loTell
, loTruncate
, loClose
, loUnlink
)
where
#include <libpq-fe.h>
#include <libpq/libpq-fs.h>
#include "noticehandlers.h"
import Prelude hiding ( print )
import Foreign
import Foreign.C.Types
import Foreign.C.String
#if __GLASGOW_HASKELL__ >= 702
import qualified Foreign.ForeignPtr.Unsafe as Unsafe
#endif
import qualified Foreign.Concurrent as FC
import System.Posix.Types ( Fd(..) )
import Data.List ( foldl' )
import System.IO ( IOMode(..), SeekMode(..) )
#if __GLASGOW_HASKELL__ >= 700
import GHC.Conc ( closeFdWith ) -- Won't work with GHC 7.0.1
#endif
import System.Posix.Types ( CPid )
import Data.ByteString.Char8 ()
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Internal as B ( fromForeignPtr
, c_strlen
, createAndTrim
, ByteString(..)
)
import qualified Data.ByteString as B
import Control.Concurrent.MVar
import Data.Typeable
#if __GLASGOW_HASKELL__ >= 700
import Control.Exception (mask_)
#else
import qualified Control.Exception
mask_ = Control.Exception.block
#endif
-- $dbconn
-- The following functions deal with making a connection to a
-- PostgreSQL backend server. An application program can have several
-- backend connections open at one time. (One reason to do that is to
-- access more than one database.) Each connection is represented by a
-- 'Connection', which is obtained from the function 'connectdb', or
-- 'connectStart'. The 'status' function should be called to check
-- whether a connection was successfully made before queries are sent
-- via the connection object.
-- | 'Connection' encapsulates a connection to the backend.
data Connection = Conn {-# UNPACK #-} !(ForeignPtr PGconn)
{-# UNPACK #-} !(MVar NoticeBuffer)
instance Eq Connection where
(Conn c _) == (Conn d _) = c == d
(Conn c _) /= (Conn d _) = c /= d
data PGconn
-- | Makes a new connection to the database server.
--
-- This function opens a new database connection using the parameters
-- taken from the string conninfo. Its nonblocking analogues are
-- 'connectStart' and 'connectPoll'.
--
-- The passed string can be empty to use all default parameters, or it
-- can contain one or more parameter settings separated by
-- whitespace. Each parameter setting is in the form keyword =
-- value. Spaces around the equal sign are optional. To write an empty
-- value or a value containing spaces, surround it with single quotes,
-- e.g., keyword = 'a value'. Single quotes and backslashes within the
-- value must be escaped with a backslash, i.e., \' and \\.
connectdb :: B.ByteString -- ^ Connection Info
-> IO Connection
connectdb conninfo =
mask_ $ do
connPtr <- B.useAsCString conninfo c_PQconnectdb
if connPtr == nullPtr
then fail "libpq failed to allocate a PGconn structure"
else do
noticeBuffer <- newMVar nullPtr
connection <- newForeignPtrOnce connPtr (pqfinish connPtr noticeBuffer)
return $! Conn connection noticeBuffer
-- | Make a connection to the database server in a nonblocking manner.
connectStart :: B.ByteString -- ^ Connection Info
-> IO Connection
connectStart connStr =
mask_ $ do
connPtr <- B.useAsCString connStr c_PQconnectStart
if connPtr == nullPtr
then fail "libpq failed to allocate a PGconn structure"
else do
noticeBuffer <- newMVar nullPtr
connection <- newForeignPtrOnce connPtr (pqfinish connPtr noticeBuffer)
return $! Conn connection noticeBuffer
pqfinish :: Ptr PGconn -> MVar NoticeBuffer -> IO ()
pqfinish conn noticeBuffer = do
#if __GLASGOW_HASKELL__ >= 700
-- This covers the case when a connection is closed while other Haskell
-- threads are using GHC's IO manager to wait on the descriptor. This is
-- commonly the case with asynchronous notifications, for example. Since
-- libpq is responsible for opening and closing the file descriptor, GHC's
-- IO manager needs to be informed that the file descriptor has been
-- closed. The IO manager will then raise an exception in those threads.
mfd <- c_PQsocket conn
case mfd of
-1 -> -- This can happen if the connection is bad/lost
-- This case may be worth investigating further
c_PQfinish conn
fd -> closeFdWith (\_ -> c_PQfinish conn) (Fd fd)
#else
c_PQfinish conn
#endif
nb <- swapMVar noticeBuffer nullPtr
c_free_noticebuffer nb
-- | Workaround for bug in 'FC.newForeignPtr' before base 4.6. Ensure the
-- finalizer is only run once, to prevent a segfault. See GHC ticket #7170
--
-- Note that 'getvalue' and 'maybeBsFromForeignPtr' do not need this
-- workaround, since their finalizers are just 'touchForeignPtr' calls.
newForeignPtrOnce :: Ptr a -> IO () -> IO (ForeignPtr a)
newForeignPtrOnce ptr fin = do
mv <- newMVar fin
FC.newForeignPtr ptr $ tryTakeMVar mv >>= maybe (return ()) id
-- | Allocate a Null Connection, which all libpq functions
-- should safely fail on.
newNullConnection :: IO Connection
newNullConnection = do
connection <- newForeignPtr_ nullPtr
noticeBuffer <- newMVar nullPtr
return $! Conn connection noticeBuffer
-- | Test if a connection is the Null Connection.
isNullConnection :: Connection -> Bool
#if __GLASGOW_HASKELL__ >= 702
isNullConnection (Conn x _) = Unsafe.unsafeForeignPtrToPtr x == nullPtr
#else
isNullConnection (Conn x _) = unsafeForeignPtrToPtr x == nullPtr
#endif
{-# INLINE isNullConnection #-}
-- | If 'connectStart' succeeds, the next stage is to poll libpq so
-- that it can proceed with the connection sequence. Use 'socket' to
-- obtain the 'Fd' of the socket underlying the database
-- connection. Loop thus: If 'connectPoll' last returned
-- 'PollingReading', wait until the socket is ready to read (as
-- indicated by select(), poll(), or similar system function). Then
-- call 'connectPoll' again. Conversely, if 'connectPoll' last
-- returned 'PollingWriting', wait until the socket is ready to write,
-- then call 'connectPoll' again. If you have yet to call
-- 'connectPoll', i.e., just after the call to 'connectStart', behave
-- as if it last returned 'PollingWriting'. Continue this loop until
-- 'connectPoll' returns 'PollingFailed', indicating the connection
-- procedure has failed, or 'PollingOk', indicating the connection has
-- been successfully made.
connectPoll :: Connection
-> IO PollingStatus
connectPoll = pollHelper c_PQconnectPoll
-- PQconndefaults
-- Returns the default connection options.
-- PQconninfoOption *PQconndefaults(void);
-- typedef struct
-- {
-- char *keyword; /* The keyword of the option */
-- char *envvar; /* Fallback environment variable name */
-- char *compiled; /* Fallback compiled in default value */
-- char *val; /* Option's current value, or NULL */
-- char *label; /* Label for field in connect dialog */
-- char *dispchar; /* Indicates how to display this field
-- in a connect dialog. Values are:
-- "" Display entered value as is
-- "*" Password field - hide value
-- "D" Debug option - don't show by default */
-- int dispsize; /* Field size in characters for dialog */
-- } PQconninfoOption;
-- Returns a connection options array. This can be used to determine all possible PQconnectdb options and their current default values. The return value points to an array of PQconninfoOption structures, which ends with an entry having a null keyword pointer. The null pointer is returned if memory could not be allocated. Note that the current default values (val fields) will depend on environment variables and other context. Callers must treat the connection options data as read-only.
-- After processing the options array, free it by passing it to PQconninfoFree. If this is not done, a small amount of memory is leaked for each call to PQconndefaults.
-- PQconninfoParse
-- Returns parsed connection options from the provided connection string.
-- PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg);
-- Parses a connection string and returns the resulting options as an array; or returns NULL if there is a problem with the connection string. This can be used to determine the PQconnectdb options in the provided connection string. The return value points to an array of PQconninfoOption structures, which ends with an entry having a null keyword pointer.
-- Note that only options explicitly specified in the string will have values set in the result array; no defaults are inserted.
-- If errmsg is not NULL, then *errmsg is set to NULL on success, else to a malloc'd error string explaining the problem. (It is also possible for *errmsg to be set to NULL even when NULL is returned; this indicates an out-of-memory situation.)
-- After processing the options array, free it by passing it to PQconninfoFree. If this is not done, some memory is leaked for each call to PQconninfoParse. Conversely, if an error occurs and errmsg is not NULL, be sure to free the error string using PQfreemem.
-- | Resets the communication channel to the server.
--
-- This function will close the connection to the server and attempt
-- to reestablish a new connection to the same server, using all the
-- same parameters previously used. This might be useful for error
-- recovery if a working connection is lost.
reset :: Connection
-> IO ()
reset connection = withConn connection c_PQreset
-- | Reset the communication channel to the server, in a nonblocking manner.
resetStart :: Connection
-> IO Bool
resetStart connection =
enumFromConn connection c_PQresetStart
-- | To initiate a connection reset, call 'resetStart'. If it returns
-- 'False', the reset has failed. If it returns 'True', poll the reset
-- using 'resetPoll' in exactly the same way as you would create the
-- connection using 'connectPoll'.
resetPoll :: Connection
-> IO PollingStatus
resetPoll = pollHelper c_PQresetPoll
data PollingStatus
= PollingFailed
| PollingReading
| PollingWriting
| PollingOk deriving (Eq, Show)
pollHelper :: (Ptr PGconn -> IO CInt)
-> Connection
-> IO PollingStatus
pollHelper poller connection =
do code <- withConn connection poller
case code of
(#const PGRES_POLLING_READING) -> return PollingReading
(#const PGRES_POLLING_OK) -> return PollingOk
(#const PGRES_POLLING_WRITING) -> return PollingWriting
(#const PGRES_POLLING_FAILED) -> return PollingFailed
_ -> fail $ "unexpected polling status " ++ show code
-- | Closes the connection to the server.
--
-- Note that the 'Connection' must not be used again after 'finish'
-- has been called.
finish :: Connection
-> IO ()
finish (Conn fp _) =
do finalizeForeignPtr fp
-- $connstatus
-- These functions can be used to interrogate the status of an
-- existing database connection object.
-- | Returns the database name of the connection.
db :: Connection
-> IO (Maybe B.ByteString)
db = statusString c_PQdb
-- | Returns the user name of the connection.
user :: Connection
-> IO (Maybe B.ByteString)
user = statusString c_PQuser
-- | Returns the password of the connection.
pass :: Connection
-> IO (Maybe B.ByteString)
pass = statusString c_PQpass
-- | Returns the server host name of the connection.
host :: Connection
-> IO (Maybe B.ByteString)
host = statusString c_PQhost
-- | Returns the port of the connection.
port :: Connection
-> IO (Maybe B.ByteString)
port = statusString c_PQport
-- | Returns the command-line options passed in the connection request.
options :: Connection
-> IO (Maybe B.ByteString)
options = statusString c_PQoptions
-- | Helper function that checks for nullPtrs and returns the empty
-- string.
statusString :: (Ptr PGconn -> IO CString)
-> Connection
-> IO (Maybe B.ByteString)
statusString f connection =
withConn connection $ \ptr ->
do cstr <- f ptr
if cstr == nullPtr
then return Nothing
else Just `fmap` B.packCString cstr
data ConnStatus
= ConnectionOk -- ^ The 'Connection' is ready.
| ConnectionBad -- ^ The connection procedure has failed.
| ConnectionStarted -- ^ Waiting for connection to be made.
| ConnectionMade -- ^ Connection OK; waiting to send.
| ConnectionAwaitingResponse -- ^ Waiting for a response from the server.
| ConnectionAuthOk -- ^ Received authentication;
-- waiting for backend start-up to
-- finish.
| ConnectionSetEnv -- ^ Negotiating environment-driven
-- parameter settings.
| ConnectionSSLStartup -- ^ Negotiating SSL encryption.
deriving (Eq, Show)
-- | Returns the status of the connection.
--
-- The status can be one of a number of values. However, only two of
-- these are seen outside of an asynchronous connection procedure:
-- 'ConnectionOk' and 'ConnectionBad'. A good connection to the
-- database has the status 'ConnectionOk'. A failed connection attempt
-- is signaled by status 'ConnectionBad'. Ordinarily, an OK status
-- will remain so until 'finish', but a communications failure might
-- result in the status changing to 'ConnectionBad' prematurely. In
-- that case the application could try to recover by calling 'reset'.
--
-- See the entry for 'connectStart' and 'connectPoll' with regards to
-- other status codes that might be seen.
status :: Connection
-> IO ConnStatus
status connection = do
stat <- withConn connection c_PQstatus
case stat of
(#const CONNECTION_OK) -> return ConnectionOk
(#const CONNECTION_BAD) -> return ConnectionBad
(#const CONNECTION_STARTED) -> return ConnectionStarted
(#const CONNECTION_MADE) -> return ConnectionMade
(#const CONNECTION_AWAITING_RESPONSE)-> return ConnectionAwaitingResponse
(#const CONNECTION_AUTH_OK) -> return ConnectionAuthOk
(#const CONNECTION_SETENV) -> return ConnectionSetEnv
(#const CONNECTION_SSL_STARTUP) -> return ConnectionSSLStartup
--(#const CONNECTION_NEEDED) -> ConnectionNeeded
c -> fail $ "Unknown connection status " ++ show c
data TransactionStatus = TransIdle -- ^ currently idle
| TransActive -- ^ a command is in progress
| TransInTrans -- ^ idle, in a valid transaction block
| TransInError -- ^ idle, in a failed transaction block
| TransUnknown -- ^ the connection is bad
deriving (Eq, Show)
-- | Returns the current in-transaction status of the server.
--
-- 'TransActive' is reported only when a query has been sent to the
-- server and not yet completed.
transactionStatus :: Connection
-> IO TransactionStatus
transactionStatus connection = do
stat <- withConn connection c_PQtransactionStatus
case stat of
(#const PQTRANS_IDLE) -> return TransIdle
(#const PQTRANS_ACTIVE) -> return TransActive
(#const PQTRANS_INTRANS) -> return TransInTrans
(#const PQTRANS_INERROR) -> return TransInError
(#const PQTRANS_UNKNOWN) -> return TransUnknown
c -> fail $ "Unknown transaction status " ++ show c
-- | Looks up a current parameter setting of the server.
--
-- Certain parameter values are reported by the server automatically
-- at connection startup or whenever their values
-- change. 'parameterStatus' can be used to interrogate these
-- settings. It returns the current value of a parameter if known, or
-- 'Nothing' if the parameter is not known.
parameterStatus :: Connection
-> B.ByteString -- ^ paramName
-> IO (Maybe B.ByteString)
parameterStatus connection paramName =
withConn connection $ \connPtr ->
B.useAsCString paramName $ \paramNamePtr ->
do cstr <- c_PQparameterStatus connPtr paramNamePtr
if cstr == nullPtr
then return Nothing
else Just `fmap` B.packCString cstr
-- | Interrogates the frontend/backend protocol being used.
--
-- Applications might wish to use this to determine whether certain
-- features are supported. Currently, the possible values are 2 (2.0
-- protocol), 3 (3.0 protocol), or zero (connection bad). This will
-- not change after connection startup is complete, but it could
-- theoretically change during a connection reset. The 3.0 protocol
-- will normally be used when communicating with PostgreSQL 7.4 or
-- later servers; pre-7.4 servers support only protocol 2.0. (Protocol
-- 1.0 is obsolete and not supported by libpq.)
protocolVersion :: Connection
-> IO Int
protocolVersion connection =
fmap fromIntegral $ withConn connection c_PQprotocolVersion
-- | Returns an integer representing the backend version.
--
-- Applications might use this to determine the version of the
-- database server they are connected to. The number is formed by
-- converting the major, minor, and revision numbers into
-- two-decimal-digit numbers and appending them together. For example,
-- version 8.1.5 will be returned as 80105, and version 8.2 will be
-- returned as 80200 (leading zeroes are not shown). Zero is returned
-- if the connection is bad.
serverVersion :: Connection
-> IO Int
serverVersion connection =
fmap fromIntegral $ withConn connection c_PQserverVersion
-- | Returns the error message most recently generated by an operation
-- on the connection.
--
-- Nearly all libpq functions will set a message for 'errorMessage' if
-- they fail. Note that by libpq convention, a nonempty 'errorMessage'
-- result can be multiple lines, and will include a trailing
-- newline. The result string should not be expected to remain the
-- same across operations on the 'Connection'.
errorMessage :: Connection
-> IO (Maybe B.ByteString)
errorMessage = statusString c_PQerrorMessage
-- | Obtains the file descriptor number of the connection socket to
-- the server. (This will not change during normal operation, but
-- could change during connection setup or reset.)
socket :: Connection
-> IO (Maybe Fd)
socket connection =
do cFd <- withConn connection c_PQsocket
case cFd of
-1 -> return Nothing
_ -> return $ Just $ Fd cFd
-- | Returns the process 'CPid' of the backend server process
-- handling this connection.
--
-- The backend PID is useful for debugging purposes and for comparison
-- to NOTIFY messages (which include the PID of the notifying backend
-- process). Note that the PID belongs to a process executing on the
-- database server host, not the local host!
backendPID :: Connection
-> IO CPid
backendPID connection =
fmap fromIntegral $ withConn connection c_PQbackendPID
-- | Returns 'True' if the connection authentication method required a
-- password, but none was available. Returns 'False' if not.
--
-- This function can be applied after a failed connection attempt to
-- decide whether to prompt the user for a password.
connectionNeedsPassword :: Connection
-> IO Bool
connectionNeedsPassword connection =
enumFromConn connection c_PQconnectionNeedsPassword
-- | Returns 'True' if the connection authentication method used a
-- password. Returns 'False' if not.
--
-- This function can be applied after either a failed or successful
-- connection attempt to detect whether the server demanded a
-- password.
connectionUsedPassword :: Connection
-> IO Bool
connectionUsedPassword connection =
enumFromConn connection c_PQconnectionUsedPassword
-- TODO: getSSL :: Connection -> IO SSL
-- $commandexec
-- Once a connection to a database server has been successfully
-- established, the functions described here are used to perform SQL
-- queries and commands.
-- | 'Result' encapsulates the result of a query (or more precisely,
-- of a single SQL command --- a query string given to 'sendQuery' can
-- contain multiple commands and thus return multiple instances of
-- 'Result'.
newtype Result = Result (ForeignPtr PGresult) deriving (Eq, Show)
data PGresult
data Format = Text | Binary deriving (Eq, Ord, Show, Enum)
newtype Oid = Oid CUInt deriving (Eq, Ord, Read, Show, Storable, Typeable)
invalidOid :: Oid
invalidOid = Oid (#const InvalidOid)
-- | Submits a command to the server and waits for the result.
--
-- Returns a 'Result' or possibly 'Nothing'. A 'Result' will generally
-- be returned except in out-of-memory conditions or serious errors
-- such as inability to send the command to the server. If a 'Nothing'
-- is returned, it should be treated like a 'FatalError' result. Use
-- 'errorMessage' to get more information about such errors.
--
-- It is allowed to include multiple SQL commands (separated by
-- semicolons) in the command string. Multiple queries sent in a
-- single 'exec' call are processed in a single transaction, unless
-- there are explicit BEGIN/COMMIT commands included in the query
-- string to divide it into multiple transactions. Note however that
-- the returned 'Result' structure describes only the result of the
-- last command executed from the string. Should one of the commands
-- fail, processing of the string stops with it and the returned
-- 'Result' describes the error condition.
exec :: Connection -- ^ connection
-> B.ByteString -- ^ statement
-> IO (Maybe Result) -- ^ result
exec connection query =
resultFromConn connection $ \p ->
B.useAsCString query $ c_PQexec p
-- | Submits a command to the server and waits for the result, with
-- the ability to pass parameters separately from the SQL command
-- text.
--
-- 'execParams' is like 'exec', but offers additional functionality:
-- parameter values can be specified separately from the command
-- string proper, and query results can be requested in either text or
-- binary format. 'execParams' is supported only in protocol 3.0 and
-- later connections; it will fail when using protocol 2.0.
--
-- The primary advantage of 'execParams' over 'exec' is that parameter
-- values can be separated from the command string, thus avoiding the
-- need for tedious and error-prone quoting and escaping.
--
-- Unlike 'exec', 'execParams' allows at most one SQL command in the
-- given string. (There can be semicolons in it, but not more than one
-- nonempty command.) This is a limitation of the underlying protocol,
-- but has some usefulness as an extra defense against SQL-injection
-- attacks.
--
-- Tip: Specifying parameter types via OIDs is tedious, particularly
-- if you prefer not to hard-wire particular OID values into your
-- program. However, you can avoid doing so even in cases where the
-- server by itself cannot determine the type of the parameter, or
-- chooses a different type than you want. In the SQL command text,
-- attach an explicit cast to the parameter symbol to show what data
-- type you will send. For example:
-- SELECT * FROM mytable WHERE x = $1::bigint;
-- This forces parameter $1 to be treated as bigint, whereas by
-- default it would be assigned the same type as x. Forcing the
-- parameter type decision, either this way or by specifying a numeric
-- type OID, is strongly recommended when sending parameter values in
-- binary format, because binary format has less redundancy than text
-- format and so there is less chance that the server will detect a
-- type mismatch mistake for you.
execParams :: Connection -- ^ connection
-> B.ByteString -- ^ statement
-> [Maybe (Oid, B.ByteString, Format)] -- ^ parameters
-> Format -- ^ result format
-> IO (Maybe Result) -- ^ result
execParams connection statement params rFmt =
do let (oids, values, lengths, formats) =
foldl' accum ([],[],[],[]) $ reverse params
!c_lengths = map toEnum lengths :: [CInt]
!n = toEnum $ length params
!f = toEnum $ fromEnum rFmt
resultFromConn connection $ \c ->
B.useAsCString statement $ \s ->
withArray oids $ \ts ->
withMany (maybeWith B.useAsCString) values $ \c_values ->
withArray c_values $ \vs ->
withArray c_lengths $ \ls ->
withArray formats $ \fs ->
c_PQexecParams c s n ts vs ls fs f
where
accum (!a,!b,!c,!d) Nothing = ( invalidOid:a
, Nothing:b
, 0:c
, 0:d
)
accum (!a,!b,!c,!d) (Just (t,v,f)) = ( t:a
, (Just v):b
, (B.length v):c
, (toEnum $ fromEnum f):d
)
-- | Submits a request to create a prepared statement with the given
-- parameters, and waits for completion.
--
-- 'prepare' creates a prepared statement for later execution with
-- 'execPrepared'. This feature allows commands that will be used
-- repeatedly to be parsed and planned just once, rather than each
-- time they are executed. 'prepare' is supported only in protocol 3.0
-- and later connections; it will fail when using protocol 2.0.
--
-- The function creates a prepared statement named stmtName from the
-- query string, which must contain a single SQL command. stmtName can
-- be \"\" to create an unnamed statement, in which case any
-- pre-existing unnamed statement is automatically replaced; otherwise
-- it is an error if the statement name is already defined in the
-- current session. If any parameters are used, they are referred to
-- in the query as $1, $2, etc. paramTypes specifies, by 'Oid', the
-- data types to be assigned to the parameter symbols. If paramTypes
-- is 'Nothing', or any particular element in the array is zero, the
-- server assigns a data type to the parameter symbol in the same way
-- it would do for an untyped literal string. Also, the query can use
-- parameter symbols with numbers higher than the length of
-- paramTypes; data types will be inferred for these symbols as
-- well. (See 'describePrepared' for a means to find out what data
-- types were inferred.)
--
-- As with 'exec', the result is normally a 'Result' whose contents
-- indicate server-side success or failure. A 'Nothing' result
-- indicates out-of-memory or inability to send the command at
-- all. Use 'errorMessage' to get more information about such errors.
--
-- Prepared statements for use with 'execPrepared' can also be created
-- by executing SQL PREPARE statements. (But 'prepare' is more
-- flexible since it does not require parameter types to be
-- pre-specified.) Also, although there is no libpq function for
-- deleting a prepared statement, the SQL DEALLOCATE statement can be
-- used for that purpose.
prepare :: Connection -- ^ connection
-> B.ByteString -- ^ stmtName
-> B.ByteString -- ^ query
-> Maybe [Oid] -- ^ paramTypes
-> IO (Maybe Result) -- ^ result
prepare connection stmtName query mParamTypes =
resultFromConn connection $ \c ->
B.useAsCString stmtName $ \s ->
B.useAsCString query $ \q ->
maybeWith withArray mParamTypes $ \o ->
let l = maybe 0 (toEnum . length) mParamTypes
in c_PQprepare c s q l o
-- | Sends a request to execute a prepared statement with given
-- parameters, and waits for the result.
--
-- 'execPrepared' is like 'execParams', but the command to be executed
-- is specified by naming a previously-prepared statement, instead of
-- giving a query string. This feature allows commands that will be
-- used repeatedly to be parsed and planned just once, rather than
-- each time they are executed. The statement must have been prepared
-- previously in the current session. 'execPrepared' is supported only
-- in protocol 3.0 and later connections; it will fail when using
-- protocol 2.0.
--
-- The parameters are identical to 'execParams', except that the name
-- of a prepared statement is given instead of a query string, and the
-- paramTypes parameter is not present (it is not needed since the
-- prepared statement's parameter types were determined when it was
-- created).
execPrepared :: Connection -- ^ connection
-> B.ByteString -- ^ stmtName
-> [Maybe (B.ByteString, Format)] -- ^ parameters
-> Format -- ^ result format
-> IO (Maybe Result) -- ^ result
execPrepared connection stmtName mPairs rFmt =
do let (values, lengths, formats) = foldl' accum ([],[],[]) $ reverse mPairs
!c_lengths = map toEnum lengths :: [CInt]
!n = toEnum $ length mPairs
!f = toEnum $ fromEnum rFmt
resultFromConn connection $ \c ->
B.useAsCString stmtName $ \s ->
withMany (maybeWith B.useAsCString) values $ \c_values ->
withArray c_values $ \vs ->
withArray c_lengths $ \ls ->
withArray formats $ \fs ->
c_PQexecPrepared c s n vs ls fs f
where
accum (!a,!b,!c) Nothing = ( Nothing:a
, 0:b
, 0:c
)
accum (!a,!b,!c) (Just (v, f)) = ( (Just v):a
, (B.length v):b
, (toEnum $ fromEnum f):c
)
-- | Submits a request to obtain information about the specified
-- prepared statement, and waits for completion.
--
-- 'describePrepared' allows an application to obtain information
-- about a previously prepared statement. 'describePrepared' is
-- supported only in protocol 3.0 and later connections; it will fail
-- when using protocol 2.0.
--
-- stmtName can be empty to reference the unnamed statement, otherwise
-- it must be the name of an existing prepared statement. On success,
-- a 'Result' with status 'CommandOk' is returned. The functions
-- 'nparams' and 'paramtype' can be applied to this 'Result' to obtain
-- information about the parameters of the prepared statement, and the
-- functions 'nfields', 'fname', 'ftype', etc provide information
-- about the result columns (if any) of the statement.
describePrepared :: Connection
-> B.ByteString -- ^ stmtName
-> IO (Maybe Result)
describePrepared connection stmtName =
resultFromConn connection $ \c ->
B.useAsCString stmtName $ \s -> c_PQdescribePrepared c s
-- | Submits a request to obtain information about the specified
-- portal, and waits for completion.
--
-- 'describePortal' allows an application to obtain information about
-- a previously created portal. (libpq does not provide any direct
-- access to portals, but you can use this function to inspect the
-- properties of a cursor created with a DECLARE CURSOR SQL command.)
-- 'describePortal' is supported only in protocol 3.0 and later
-- connections; it will fail when using protocol 2.0.
--
-- portalName can be empty to reference the unnamed portal, otherwise
-- it must be the name of an existing portal. On success, a 'Result'
-- with status 'CommandOk' is returned. The functions 'nfields',
-- 'fname', 'ftype', etc can be applied to the 'Result' to obtain
-- information about the result columns (if any) of the portal.
describePortal :: Connection
-> B.ByteString -- ^ portalName
-> IO (Maybe Result)
describePortal connection portalName =
resultFromConn connection $ \c ->
B.useAsCString portalName $ \p ->
c_PQdescribePortal c p
data ExecStatus = EmptyQuery -- ^ The string sent to the server was empty.
| CommandOk -- ^ Successful completion of a
-- command returning no data.
| TuplesOk -- ^ Successful completion of a
-- command returning data (such as a
-- SELECT or SHOW).
| CopyOut -- ^ Copy Out (from server) data
-- transfer started.
| CopyIn -- ^ Copy In (to server) data transfer
-- started.
| BadResponse -- ^ The server's response was not understood.
| NonfatalError -- ^ A nonfatal error (a notice or
-- warning) occurred.
| FatalError -- ^ A fatal error occurred.
deriving (Eq, Show)
instance Enum ExecStatus where
toEnum (#const PGRES_EMPTY_QUERY) = EmptyQuery
toEnum (#const PGRES_COMMAND_OK) = CommandOk
toEnum (#const PGRES_TUPLES_OK) = TuplesOk
toEnum (#const PGRES_COPY_OUT) = CopyOut
toEnum (#const PGRES_COPY_IN) = CopyIn
toEnum (#const PGRES_BAD_RESPONSE) = BadResponse
toEnum (#const PGRES_NONFATAL_ERROR) = NonfatalError
toEnum (#const PGRES_FATAL_ERROR) = FatalError
toEnum _ = error "Database.PQ.Enum.ExecStatus.toEnum: bad argument"
fromEnum EmptyQuery = (#const PGRES_EMPTY_QUERY)
fromEnum CommandOk = (#const PGRES_COMMAND_OK)
fromEnum TuplesOk = (#const PGRES_TUPLES_OK)
fromEnum CopyOut = (#const PGRES_COPY_OUT)
fromEnum CopyIn = (#const PGRES_COPY_IN)
fromEnum BadResponse = (#const PGRES_BAD_RESPONSE)
fromEnum NonfatalError = (#const PGRES_NONFATAL_ERROR)
fromEnum FatalError = (#const PGRES_FATAL_ERROR)
-- | Returns the result status of the command.
resultStatus :: Result
-> IO ExecStatus
resultStatus result = enumFromResult result c_PQresultStatus