HDBC-postgresql 2.3.2.8 → 2.5.0.2
raw patch · 10 files changed
Files
- CHANGELOG.md +12/−0
- Database/HDBC/PostgreSQL/Connection.hsc +16/−24
- Database/HDBC/PostgreSQL/Statement.hsc +6/−17
- Database/HDBC/PostgreSQL/Types.hs +2/−4
- Database/HDBC/PostgreSQL/Utils.hsc +8/−19
- HDBC-postgresql.cabal +10/−12
- Setup.hs +11/−3
- hdbc-postgresql-helper.c +0/−65
- hdbc-postgresql-helper.h +0/−16
- testsrc/TestSbasics.hs +14/−1
CHANGELOG.md view
@@ -1,5 +1,17 @@ # Changelog +## 2.5.0.1++* Bump time upper bound to 1.14, add round-trip test for 0-padded date.++## 2.5.0.0++* Add explicit call to `finalizeForeignPtr` on connection disconnects. Ensures disconnects happen at the moment close is call, as opposed to when the GC frees the ForeignPtr.++## 2.4.0.0++* Remove custom reference counting logic. Thanks to David Johnson.+ ### 2.3.2.8 * Compatiblity with Cabal 3.2
Database/HDBC/PostgreSQL/Connection.hsc view
@@ -1,5 +1,4 @@ -- -*- mode: haskell; -*--{-# CFILES hdbc-postgresql-helper.c #-} -- Above line for hugs {-# LANGUAGE FlexibleContexts #-} @@ -28,7 +27,7 @@ import Control.Concurrent.MVar import System.IO (stderr, hPutStrLn) import System.IO.Unsafe (unsafePerformIO)-import Control.Exception(bracket) +import Control.Exception(bracket) import Data.Convertible (Convertible) #include <libpq-fe.h>@@ -64,8 +63,7 @@ return globalConnLock else newMVar () status <- pqstatus ptr- wrappedptr <- wrapconn ptr nullPtr- fptr <- newForeignPtr pqfinishptr wrappedptr+ fptr <- newForeignPtr pqfinish ptr case status of #{const CONNECTION_OK} -> mkConn auto_transaction args (connLock,fptr) _ -> raiseError "connectPostgreSQL" status ptr@@ -74,7 +72,7 @@ -- for clone. mkConn :: Bool -> String -> Conn -> IO Impl.Connection mkConn auto_transaction args conn = withConn conn $- \cconn -> + \cconn -> do children <- newMVar [] when auto_transaction $ begin_transaction conn children protover <- pqprotocolVersion cconn@@ -141,7 +139,7 @@ fgetTables :: (Convertible SqlValue a) => Conn -> ChildList -> IO [a] fgetTables conn children =- do sth <- newSth conn children + do sth <- newSth conn children "select table_name from information_schema.tables where \ \table_schema != 'pg_catalog' AND table_schema != \ \'information_schema'"@@ -155,7 +153,7 @@ fdescribeSchemaTable :: Conn -> ChildList -> Maybe String -> String -> IO [(String, SqlColDesc)] fdescribeSchemaTable o cl maybeSchema table =- do sth <- newSth o cl + do sth <- newSth o cl ("SELECT attname, atttypid, attlen, format_type(atttypid, atttypmod), attnotnull " ++ "FROM pg_attribute, pg_class, pg_namespace ns " ++ "WHERE relname = ? and attnum > 0 and attisdropped IS FALSE " ++@@ -167,37 +165,31 @@ return $ map desccol res where desccol [attname, atttypid, attlen, formattedtype, attnotnull] =- (fromSql attname, + (fromSql attname, colDescForPGAttr (fromSql atttypid) (fromSql attlen) (fromSql formattedtype) (fromSql attnotnull == False)) desccol x = error $ "Got unexpected result from pg_attribute: " ++ show x- fdisconnect :: Conn -> ChildList -> IO ()-fdisconnect conn mchildren = - do closeAllChildren mchildren- withRawConn conn $ pqfinish+fdisconnect (lock, fptr) childList = do+ closeAllChildren childList+ modifyMVar_ lock $ \_ ->+ finalizeForeignPtr fptr foreign import ccall safe "libpq-fe.h PQconnectdb" pqconnectdb :: CString -> IO (Ptr CConn) -foreign import ccall unsafe "hdbc-postgresql-helper.h wrapobjpg"- wrapconn :: Ptr CConn -> Ptr WrappedCConn -> IO (Ptr WrappedCConn)--foreign import ccall unsafe "libpq-fe.h PQstatus"+foreign import ccall safe "libpq-fe.h PQstatus" pqstatus :: Ptr CConn -> IO #{type ConnStatusType} -foreign import ccall unsafe "hdbc-postgresql-helper.h PQfinish_app"- pqfinish :: Ptr WrappedCConn -> IO ()--foreign import ccall unsafe "hdbc-postgresql-helper.h &PQfinish_finalizer"- pqfinishptr :: FunPtr (Ptr WrappedCConn -> IO ())+foreign import ccall safe "libpq-fe.h &PQfinish"+ pqfinish :: FunPtr (Ptr CConn -> IO ()) -foreign import ccall unsafe "libpq-fe.h PQprotocolVersion"+foreign import ccall safe "libpq-fe.h PQprotocolVersion" pqprotocolVersion :: Ptr CConn -> IO CInt -foreign import ccall unsafe "libpq-fe.h PQserverVersion"+foreign import ccall safe "libpq-fe.h PQserverVersion" pqserverVersion :: Ptr CConn -> IO CInt -foreign import ccall unsafe "libpq.fe.h PQisthreadsafe"+foreign import ccall safe "libpq.fe.h PQisthreadsafe" pqisThreadSafe :: Ptr CConn -> IO Int
Database/HDBC/PostgreSQL/Statement.hsc view
@@ -1,8 +1,5 @@--- -*- mode: haskell; -*--{-# CFILES hdbc-postgresql-helper.c #-}--- Above line for hugs- module Database.HDBC.PostgreSQL.Statement where+ import Database.HDBC.Types import Database.HDBC import Database.HDBC.PostgreSQL.Types@@ -103,7 +100,7 @@ _ <- handleResultStatus cconn resptr sstate =<< pqresultStatus resptr :: IO Int return () -handleResultStatus :: (Num a, Read a) => Ptr CConn -> WrappedCStmt -> SState -> ResultStatus -> IO a+handleResultStatus :: (Num a, Read a) => Ptr CConn -> Ptr CStmt -> SState -> ResultStatus -> IO a handleResultStatus cconn resptr sstate status = case status of #{const PGRES_EMPTY_QUERY} ->@@ -126,9 +123,7 @@ numrows <- pqntuples resptr if numrows < 1 then (pqclear_raw resptr >> return 0) else do- wrappedptr <- withRawConn (dbo sstate)- (\rawconn -> wrapstmt resptr rawconn)- fresptr <- newForeignPtr pqclearptr wrappedptr+ fresptr <- newForeignPtr pqclearptr resptr _ <- swapMVar (nextrowmv sstate) 0 _ <- swapMVar (stomv sstate) (Just fresptr) return 0@@ -224,7 +219,7 @@ worker (Just sth) = ffinish sth >> return Nothing ffinish :: Stmt -> IO ()-ffinish p = withRawStmt p $ pqclear+ffinish _ = pure () foreign import ccall unsafe "libpq-fe.h PQresultStatus" pqresultStatus :: (Ptr CStmt) -> IO #{type ExecStatusType}@@ -241,17 +236,11 @@ foreign import ccall safe "libpq-fe.h PQexec" pqexec :: (Ptr CConn) -> CString -> IO (Ptr CStmt) -foreign import ccall unsafe "hdbc-postgresql-helper.h PQclear_app"- pqclear :: Ptr WrappedCStmt -> IO ()--foreign import ccall unsafe "hdbc-postgresql-helper.h &PQclear_finalizer"- pqclearptr :: FunPtr (Ptr WrappedCStmt -> IO ())+foreign import ccall unsafe "libpq-fe.h &PQclear"+ pqclearptr :: FunPtr (Ptr CStmt -> IO ()) foreign import ccall unsafe "libpq-fe.h PQclear" pqclear_raw :: Ptr CStmt -> IO ()--foreign import ccall unsafe "hdbc-postgresql-helper.h wrapobjpg"- wrapstmt :: Ptr CStmt -> Ptr WrappedCConn -> IO (Ptr WrappedCStmt) foreign import ccall unsafe "libpq-fe.h PQcmdTuples" pqcmdTuples :: Ptr CStmt -> IO CString
Database/HDBC/PostgreSQL/Types.hs view
@@ -7,11 +7,9 @@ type ConnLock = MVar () data CConn = CConn-type WrappedCConn = Ptr CConn-type Conn = (ConnLock, ForeignPtr WrappedCConn)+type Conn = (ConnLock, ForeignPtr CConn) data CStmt = CStmt-type WrappedCStmt = Ptr CStmt-type Stmt = ForeignPtr WrappedCStmt+type Stmt = ForeignPtr CStmt type ResultStatus = Word32
Database/HDBC/PostgreSQL/Utils.hsc view
@@ -1,7 +1,5 @@-{- -*- mode: haskell; -*- --}- module Database.HDBC.PostgreSQL.Utils where+ import Foreign.C.String import Foreign.ForeignPtr import Foreign.Ptr@@ -24,8 +22,6 @@ import qualified Data.ByteString.Unsafe as B #endif -#include "hdbc-postgresql-helper.h"- raiseError :: String -> Word32 -> (Ptr CConn) -> IO a raiseError msg code cconn = do rc <- pqerrorMessage cconn@@ -47,30 +43,30 @@ Ditto for statements. -} withConn :: Conn -> (Ptr CConn -> IO b) -> IO b-withConn (_lock,conn) = genericUnwrap conn+withConn (_lock,conn) = withForeignPtr conn -- Perform the associated action with the connection lock held. -- Care must be taken with the use of this as it is *not* re-entrant. Calling it--- a second time in the same thread will cause dead-lock. +-- a second time in the same thread will cause dead-lock. -- (A better approach would be to use RLock from concurrent-extra) withConnLocked :: Conn -> (Ptr CConn -> IO b) -> IO b withConnLocked c@(lock,_) a = withConn c (\cconn -> withMVar lock (\_ -> a cconn)) -withRawConn :: Conn -> (Ptr WrappedCConn -> IO b) -> IO b+withRawConn :: Conn -> (Ptr CConn -> IO b) -> IO b withRawConn (_lock,conn) = withForeignPtr conn withStmt :: Stmt -> (Ptr CStmt -> IO b) -> IO b-withStmt = genericUnwrap+withStmt = withForeignPtr -withRawStmt :: Stmt -> (Ptr WrappedCStmt -> IO b) -> IO b+withRawStmt :: Stmt -> (Ptr CStmt -> IO b) -> IO b withRawStmt = withForeignPtr withCStringArr0 :: [SqlValue] -> (Ptr CString -> IO a) -> IO a withCStringArr0 inp action = withAnyArr0 convfunc freefunc inp action where convfunc SqlNull = return nullPtr {-- convfunc y@(SqlZonedTime _) = convfunc (SqlString $ - "TIMESTAMP WITH TIME ZONE '" ++ + convfunc y@(SqlZonedTime _) = convfunc (SqlString $+ "TIMESTAMP WITH TIME ZONE '" ++ fromSql y ++ "'") -} convfunc y@(SqlUTCTime _) = convfunc (SqlZonedTime (fromSql y))@@ -110,12 +106,5 @@ -- return ptr return res -genericUnwrap :: ForeignPtr (Ptr a) -> (Ptr a -> IO b) -> IO b-genericUnwrap fptr action = withForeignPtr fptr (\structptr ->- do objptr <- #{peek finalizeonce, encapobj} structptr- action objptr- )- foreign import ccall unsafe "libpq-fe.h PQerrorMessage" pqerrorMessage :: Ptr CConn -> IO CString-
HDBC-postgresql.cabal view
@@ -1,14 +1,14 @@ Name: HDBC-postgresql-Version: 2.3.2.8+Version: 2.5.0.2 License: BSD3-Maintainer: Nicolas Wu <nicolas.wu@gmail.com>+Maintainer: Nicolas Wu <nicolas.wu@gmail.com>, David Johnson <code@dmj.io> Author: John Goerzen Copyright: Copyright (c) 2005-2011 John Goerzen license-file: LICENSE-extra-source-files: LICENSE, hdbc-postgresql-helper.h,+extra-source-files: LICENSE pgtypes_internal.h- Makefile,- README.txt,+ Makefile+ README.txt testsrc/TestTime.hs CHANGELOG.md homepage: http://github.com/hdbc/hdbc-postgresql@@ -21,7 +21,7 @@ Cabal-Version: >=1.10 custom-setup- setup-depends: Cabal >= 1.8 && < 3.3, base < 5+ setup-depends: Cabal >= 1.8 && < 3.17, base < 5 Flag splitBase description: Choose the new smaller, split-up package.@@ -45,14 +45,13 @@ Build-Depends: base >= 3 && < 5, mtl, HDBC>=2.2.0, parsec, utf8-string, bytestring, old-time, convertible if flag(minTime15)- Build-Depends: time >= 1.5 && < 1.10+ Build-Depends: time >= 1.5 && < 1.17 CPP-Options: -DMIN_TIME_15 else Build-Depends: time < 1.5, old-locale if impl(ghc >= 6.9) Build-Depends: base >= 4 Extra-Libraries: pq- C-Sources: hdbc-postgresql-helper.c Include-Dirs: . GHC-Options: -O2 -Wall Default-Language: Haskell2010@@ -60,11 +59,11 @@ Executable runtests if flag(buildtests) Buildable: True- Build-Depends: HUnit, QuickCheck, testpack, containers,+ Build-Depends: HUnit, QuickCheck, containers, convertible, parsec, utf8-string, bytestring, old-time, base >= 4.6 && < 5.0, HDBC>=2.2.6 if flag(minTime15)- Build-Depends: time >= 1.5 && < 1.9+ Build-Depends: time >= 1.5 && < 1.17 CPP-Options: -DMIN_TIME_15 else Build-Depends: time < 1.5, old-locale@@ -86,11 +85,10 @@ TestUtils, Testbasics, Tests- C-Sources: hdbc-postgresql-helper.c include-dirs: . Extra-Libraries: pq Hs-Source-Dirs: ., testsrc- GHC-Options: -O2+ GHC-Options: -O2 -threaded Default-Language: Haskell2010 source-repository head
Setup.hs view
@@ -1,5 +1,5 @@ #!/usr/bin/env runhaskell-{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}+{-# LANGUAGE CPP, MultiParamTypeClasses, FlexibleInstances #-} import Distribution.Simple import Distribution.PackageDescription@@ -8,6 +8,9 @@ import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Program import Distribution.Verbosity+#if MIN_VERSION_Cabal(3,14,0)+import Distribution.Utils.Path+#endif import Data.Char (isSpace) import Data.List (dropWhile,reverse)@@ -61,9 +64,14 @@ libDir <- pgconfig ["--libdir"] return emptyBuildInfo {- extraLibDirs = [strip libDir],- includeDirs = [strip incDir]+ extraLibDirs = [toPath $ strip libDir],+ includeDirs = [toPath $ strip incDir] } where+#if MIN_VERSION_Cabal(3,14,0)+ toPath = makeSymbolicPath+#else+ toPath = id+#endif verbosity = normal -- honestly, this is a hack strip x = dropWhile isSpace $ reverse $ dropWhile isSpace $ reverse x
− hdbc-postgresql-helper.c
@@ -1,65 +0,0 @@-#include <libpq-fe.h>-#include <stdio.h>-#include <stdlib.h>-#include "hdbc-postgresql-helper.h"--/* Things can't finalize more than once. -We'd like to let people call them from the app.-Yet we'd also like to be able to have a ForeignPtr finalize them.--So, here's a little wrapper for things. */--void PQfinish_conditional_finalizer(finalizeonce *conn);--finalizeonce *wrapobjpg(void *obj, finalizeonce *parentobj) {- finalizeonce *newobj;- newobj = malloc(sizeof(finalizeonce));- if (newobj == NULL) {- fprintf(stderr, "HDBC: could not allocate wrapper!\n");- return NULL;- }- newobj->isfinalized = 0;- newobj->refcount = 1;- newobj->encapobj = obj;- newobj->parent = parentobj;- if (parentobj != NULL) - parentobj->refcount++;- return newobj;-}- -void PQfinish_app(finalizeonce *conn) {- if (conn->isfinalized)- return;- PQfinish((PGconn *) (conn->encapobj));- conn->encapobj = NULL;- conn->isfinalized = 1;-}--void PQfinish_finalizer(finalizeonce *conn) {- (conn->refcount)--;- PQfinish_conditional_finalizer(conn);-}--void PQfinish_conditional_finalizer(finalizeonce *conn) {- if (conn->refcount < 1) {- PQfinish_app(conn);- free(conn);- }-}--void PQclear_app(finalizeonce *res) {- if (res->isfinalized)- return;- PQclear((PGresult *) (res->encapobj));- res->isfinalized = 1;-}--void PQclear_finalizer(finalizeonce *res) {- PQclear_app(res);- (res->refcount)--; /* Not really important since this is never a - parent */- (res->parent->refcount)--;- PQfinish_conditional_finalizer(res->parent);- free(res);-}-
− hdbc-postgresql-helper.h
@@ -1,16 +0,0 @@-#include <libpq-fe.h>--typedef struct TAG_finalizeonce {- void *encapobj;- int refcount;- int isfinalized;- struct TAG_finalizeonce *parent;-} finalizeonce;--extern finalizeonce *wrapobjpg(void *obj, finalizeonce *parentobj);--extern void PQfinish_app(finalizeonce *conn);-extern void PQfinish_finalizer(finalizeonce *conn);--extern void PQclear_app(finalizeonce *res);-extern void PQclear_finalizer(finalizeonce *res);
testsrc/TestSbasics.hs view
@@ -1,6 +1,8 @@ module TestSbasics(tests) where import Test.HUnit+import Data.Convertible (convert) import Data.List+import Data.Time (UTCTime(UTCTime), fromGregorian) import Database.HDBC import TestUtils import System.IO@@ -153,6 +155,16 @@ ) where rows = map (\x -> [Just . show $ x]) [1..9] +testFirstCentury = dbTestCase $ \dbh ->+ do -- we use UTC to avoid Local Mean Time which the 'time' library can't handle+ runRaw dbh "SET TIMEZONE TO 'UTC';"+ select <- prepare dbh "SELECT ? :: timestamptz;"+ let time = UTCTime (fromGregorian 99 12 25) 0+ execute select [toSql (convert time :: SqlValue)]+ result <- fetchAllRows select+ assertEqual "first century year can roundtrip"+ time (fromSql $ head $ head result)+ tests = TestList [ TestLabel "openClosedb" openClosedb,@@ -167,5 +179,6 @@ TestLabel "sFetchAllRows" testsFetchAllRows, TestLabel "basicTransactions" basicTransactions, TestLabel "withTransaction" testWithTransaction,- TestLabel "dropTable" dropTable+ TestLabel "dropTable" dropTable,+ TestLabel "firstCentury" testFirstCentury ]