HDBC-2.4.0.2/0000755000000000000000000000000013146574642010557 5ustar0000000000000000HDBC-2.4.0.2/CHANGELOG.md0000644000000000000000000000021613146574642012367 0ustar0000000000000000# Changelog #### 2.4.0.2 * Compatibility with GHC 8.2/time 1.8 #### 2.4.0.1 * Compatibility with time 1.5. * Minor documentation fixes. HDBC-2.4.0.2/HDBC.cabal0000644000000000000000000000566513146574642012257 0ustar0000000000000000Name: HDBC Version: 2.4.0.2 License: BSD3 Maintainer: Nicolas Wu Author: John Goerzen, Nicolas Wu homepage: https://github.com/hdbc/hdbc Copyright: Copyright (c) 2005-2011 John Goerzen license-file: LICENSE extra-source-files: LICENSE, Makefile, README.md, CHANGELOG.md Category: Database synopsis: Haskell Database Connectivity Description: HDBC provides an abstraction layer between Haskell programs and SQL relational databases. This lets you write database code once, in Haskell, and have it work with any number of backend SQL databases (MySQL, Oracle, PostgreSQL, ODBC-compliant databases, etc.) Stability: Stable Build-Type: Simple Cabal-Version: >=1.8 source-repository head type: git location: https://github.com/hdbc/hdbc.git flag splitBase description: Choose the new smaller, split-up base package. flag buildtests description: Build the executable to run unit tests default: False flag minTime15 description: Use time 1.5 or higher. default: True library if flag(splitBase) Build-Depends: base>=3 && <5, old-time, bytestring, containers if flag(minTime15) Build-Depends: time >= 1.5 && < 1.9 CPP-Options: -DMIN_TIME_15 else Build-Depends: time < 1.5, old-locale else Build-Depends: base<3 Build-Depends: mtl, convertible >= 1.1.0.0, text, utf8-string -- Hack for cabal-install weirdness. cabal-install forces base 3, -- though it works fine for Setup.lhs manually. Fix. if impl(ghc >= 6.9) build-depends: base >= 4 GHC-Options: -O2 -Wall -fno-warn-orphans Exposed-Modules: Database.HDBC, Database.HDBC.Types, Database.HDBC.DriverUtils, Database.HDBC.ColTypes, Database.HDBC.Statement, Database.HDBC.SqlValue, Database.HDBC.Locale Other-Modules: Database.HDBC.Utils Extensions: ExistentialQuantification, CPP, MultiParamTypeClasses, FlexibleContexts, TypeSynonymInstances, TypeOperators, RankNTypes, FlexibleInstances, DeriveDataTypeable Executable runtests if flag(buildtests) Buildable: True Build-Depends: HUnit, QuickCheck (>= 2.0), testpack (>= 2.0) if flag(splitBase) Build-Depends: base>=3 && <5, old-time, bytestring, containers if flag(minTime15) Build-Depends: time >= 1.5 && < 1.9 else Build-Depends: time < 1.5, old-locale else Build-Depends: base<3 Build-Depends: mtl, convertible >= 1.1.0.0, utf8-string, text -- Hack for cabal-install weirdness. cabal-install forces base 3, -- though it works fine for Setup.lhs manually. Fix. if impl(ghc >= 6.9) build-depends: base >= 4 else Buildable: False Main-Is: runtests.hs Other-Modules: TestSqlValue TestInfrastructure Hs-Source-Dirs: ., testsrc GHC-Options: -O2 Extensions: ExistentialQuantification, CPP, MultiParamTypeClasses, FlexibleContexts, TypeSynonymInstances, TypeOperators, RankNTypes, FlexibleInstances HDBC-2.4.0.2/LICENSE0000644000000000000000000000271213146574642011566 0ustar0000000000000000Copyright (c) 2011, John Goerzen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of John Goerzen nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. HDBC-2.4.0.2/Makefile0000644000000000000000000000125413146574642012221 0ustar0000000000000000all: setup @echo "Please use Cabal to build this package; not make." ./setup configure ./setup build setup: Setup.lhs ghc --make -o setup Setup.lhs install: setup ./setup install clean: ./Setup.lhs clean .PHONY: test test: test-ghc test-hugs @echo "" @echo "All tests pass." test-hugs: setup @echo " ****** Running hugs tests" ./setup configure -f buildtests --hugs ./setup build runhugs -98 +o -P$(PWD)/dist/scratch:$(PWD)/dist/scratch/programs/runtests: \ dist/scratch/programs/runtests/Main.hs test-ghc: setup @echo " ****** Building GHC tests" ./setup configure -f buildtests ./setup build @echo " ****** Running GHC tests" ./dist/build/runtests/runtests HDBC-2.4.0.2/README.md0000644000000000000000000000137313146574642012042 0ustar0000000000000000HDBC ==== Welcome to HDBC, Haskell Database Connectivity. HDBC is modeled loosely on Perl's DBI interface, though it has also been influenced by Python's DB-API v2, JDBC in Java, and HSQL in Haskell. Please see the HDBC [wiki](https://github.com/hdbc/hdbc/wiki) for an introduction to HDBC and its various features. Installation ------------ You'll need either GHC 6.4.1 or above, or Hugs 2005xx or above. The steps to install are: ghc --make -o setup Setup.lhs ./setup configure ./setup build sudo ./setup install If you're on Windows, you can omit the leading "./". Usage ----- To use with hugs, you'll want to use hugs -98. To use with GHC, you'll want to use -package HDBC in your programs. Or, with Cabal, use Build-Depends: HDBC. HDBC-2.4.0.2/Setup.lhs0000644000000000000000000000011713146574642012366 0ustar0000000000000000#!/usr/bin/env runhaskell > import Distribution.Simple > main = defaultMain HDBC-2.4.0.2/Database/0000755000000000000000000000000013146574642012263 5ustar0000000000000000HDBC-2.4.0.2/Database/HDBC.hs0000644000000000000000000002055413146574642013325 0ustar0000000000000000{- | Module : Database.HDBC Copyright : Copyright (C) 2005-2011 John Goerzen License : BSD3 Maintainer : John Goerzen Stability : provisional Portability: portable Welcome to HDBC, the Haskell Database Connectivity library. Written by John Goerzen, jgoerzen\@complete.org -} module Database.HDBC (-- * Introduction -- $introduction -- ** Features -- $features -- ** Available Drivers -- $drivers -- * Typing of transfer data SqlValue(..), toSql, fromSql, safeFromSql, nToSql, iToSql, posixToSql, -- * Database Connections IConnection, disconnect, clone, -- ** Wrapped Connections ConnWrapper(..), withWConn, -- ** Preparing Queries run, runRaw, sRun, prepare, quickQuery', quickQuery, -- ** Transaction Handling -- $transactions commit, rollback, withTransaction, -- ** Connection Inquiries hdbcDriverName, hdbcClientVer, proxiedClientName, proxiedClientVer, dbServerVer, dbTransactionSupport, getTables, describeTable, -- * Statements Statement, -- ** Execution execute, executeRaw, sExecute, executeMany, sExecuteMany, -- ** Fetching Results fetchRow, fetchRowAL, fetchRowMap, sFetchRow, fetchAllRows, fetchAllRows', fetchAllRowsAL, fetchAllRowsAL', fetchAllRowsMap, fetchAllRowsMap', sFetchAllRows, sFetchAllRows', getColumnNames, -- ** Statement Inquires describeResult, -- ** Miscellaneous finish, originalQuery, -- * Exceptions SqlError(..), throwSqlError, catchSql, handleSql, sqlExceptions, handleSqlError, -- * Column Types -- | These are defined in "Database.HDBC.ColTypes" but are -- available to programs importing "Database.HDBC" by default as well. -- See "Database.HDBC.ColTypes" for documentation. module Database.HDBC.ColTypes -- * Threading -- $threading ) where import Database.HDBC.Utils(catchSql, handleSql, sqlExceptions, handleSqlError, withTransaction, sFetchAllRows, fetchAllRows, sFetchAllRows', fetchAllRows', sRun, sExecute, sExecuteMany, sFetchRow, quickQuery, fetchRowMap, fetchAllRowsMap, quickQuery', fetchAllRowsMap', fetchRowAL, fetchAllRowsAL, fetchAllRowsAL', throwSqlError) import Database.HDBC.Types import Database.HDBC.ColTypes {- $introduction Welcome to HDBC, Haskell Database Connectivity. HDBC provides an abstraction layer between Haskell programs and SQL relational databases. This lets you write database code once, in Haskell, and have it work with any number of backend SQL databases (MySQL, Oracle, PostgreSQL, ODBC-compliant databases, etc.) HDBC is modeled loosely on Perl's DBI interface , though it has also been influenced by Python's DB-API v2, JDBC in Java, and HSQL in Haskell. HDBC is a from-scratch effort. It is not a reimplementation of HSQL, though its purpose is the same. -} {- $features Features of HDBC include: * Ability to use replacable parameters to let one query be executed multiple times (eliminates the need for an escape function) * Ability to access returned rows by column number * Ability to read data from the SQL server on-demand rather than reading the entire result set up front * HUnit testsuite for each backend driver * Well-defined standard API and easy backend driver implementation * Lazy reading of the entire result set (think hGetContents, but for the results of SELECT) (see 'sFetchAllRows') * Support for translation between Haskell and SQL types * Support for querying database server properties * Add-on package (hdbc-missingh) to integrate with MissingH, providing a database backend for AnyDBM * Support for querying metadata such as column names * Support for querying additional metadata (column types, etc.) -} {- $drivers Here is a list of known drivers as of January 26, 2009: [@Sqlite v3@] Available from . Or, to participate in development, use @git clone @ [@PostgreSQL@] Available from . Or, to participate in development, use @git clone @ [@ODBC@] Available from . Or, to partitipace in development, use @git clone @ [@MySQL@] MySQL users have two choices: the first is the ODBC driver, which works and has been tested against MySQL on both Linux\/Unix and Windows platforms. There is also an alpha-quality native MySQL driver available for download at with a homepage at . In addition, there is one integration package: /hdbc-anydbm/. This integrates with the AnyDBM library . It lets any HDBC database act as a backend for the AnyDBM interface. Available from . Or, to participate in development, use @darcs get --partial @ The latest version of HDBC itself is available from . Or, to participate in development, use @git clone @. -} {- $transactions This section concerns itself with writing (updating) a database. In HDBC, as with many RDBMS implementations, every write to the database occurs within a transaction. No changes are visible (outside the current transaction) until a commit operation occurs, in which case all changes since the transaction started are atomically committed. Also, there is a rollback operation that can undo all changes since the transaction started. HDBC does everything within a transaction. A transaction is implicitly entered when a connection to a database is established, and a transaction is implicitly entered after each call to 'commit' or 'rollback' as well. The practical effect of this is that you must call 'commit' after making changes to a database in order for those changes to become visible. You don't have to call 'commit' after /every/ change, just after a batch of them. (Exceptions exist for databases that don't offer a high level of transaction isolation; but you should always play it safe and commit anyway.) Database developers will also be experienced with the atomicity benefits of transactions, an explanation of which is outside the scope of this manual. Errors occuring at the database level can leave a transaction in an indeterminate state, depending on the database. Some databases will refuse all queries until the next 'commit' or 'rollback'. The safe thing to do is to issue a 'commit' or 'rollback' after trapping any 'SqlError'. Alternatively, you could use 'withTransaction', which will automatically handle this detail for you. -} {- $threading FIXME: this is draft information Thread support in a generalized interface such as HDBC can be complicated because support for threading varies across database interfaces. However, applications using HDBC should be able to rely upon at least a few basic guarantees: * The HDBC modules may freely be imported and used across all threads. * HDBC modules may also freely share database connections and statements; the database or HDBC driver will be responsible for locking if necessary. I use \"share\" in the same sense as Python's DB-API: multiple threads may use the resource without wrapping it in any lock. However, there are some caveats to the above: * Not all databases support more than one active statement for a single connection. Therefore, for maximum portability, you should use a different connection to the database for each simultaneous query you wish to use. FIXME: describe when a statement is active. * Not all databases may support the level of multithreading described above. For those that don't, safe access will be restriced in the HDBC driver by using locks. Therefore, you can write portable code, but you only get real multithreading when databases really support it. Details of thread support should be documented in the HDBC driver for each specific database. -} HDBC-2.4.0.2/Database/HDBC/0000755000000000000000000000000013146574642012763 5ustar0000000000000000HDBC-2.4.0.2/Database/HDBC/ColTypes.hs0000644000000000000000000001106113146574642015060 0ustar0000000000000000{- | Module : Database.HDBC.ColTypes Copyright : Copyright (C) 2006-2011 John Goerzen License : BSD3 Maintainer : John Goerzen Stability : provisional Portability: portable Definitions of, and utilities for, specifying what type of data is represented by a column. Written by John Goerzen, jgoerzen\@complete.org -} {-# LANGUAGE DeriveDataTypeable #-} module Database.HDBC.ColTypes (SqlColDesc(..), SqlTypeId(..), SqlInterval(..) ) where import Data.Dynamic {- | The description of a column. Fields are Nothing if the database backend cannot supply the requested information. The colSize field works like this: For character types, the maximum width of the column. For numeric types, the total number of digits allowed. See the ODBC manual for more. The colOctetLength field is defined for character and binary types, and gives the number of bytes the column requires, regardless of encoding. -} data SqlColDesc = SqlColDesc { colType :: SqlTypeId -- ^ Type of data stored here ,colSize :: Maybe Int -- ^ The size of a column ,colOctetLength :: Maybe Int -- ^ The maximum size in octets ,colDecDigits :: Maybe Int -- ^ Digits to the right of the period ,colNullable :: Maybe Bool -- ^ Whether NULL is acceptable } deriving (Eq, Read, Show, Typeable) {- | The type identifier for a given column. This represents the type of data stored in the column in the underlying SQL engine. It does not form the entire column type; see 'SqlColDesc' for that. These types correspond mainly to those defined by ODBC. -} data SqlTypeId = SqlCharT -- ^ Fixed-width character strings | SqlVarCharT -- ^ Variable-width character strings | SqlLongVarCharT -- ^ Variable-width character strings, max length implementation dependant | SqlWCharT -- ^ Fixed-width Unicode strings | SqlWVarCharT -- ^ Variable-width Unicode strings | SqlWLongVarCharT -- ^ Variable-width Unicode strings, max length implementation dependant | SqlDecimalT -- ^ Signed exact values | SqlNumericT -- ^ Signed exact integer values | SqlSmallIntT -- ^ 16-bit integer values | SqlIntegerT -- ^ 32-bit integer values | SqlRealT | SqlFloatT -- ^ Signed inexact floating-point values | SqlDoubleT -- ^ Signed inexact double-precision values | SqlBitT -- ^ A single bit | SqlTinyIntT -- ^ 8-bit integer values | SqlBigIntT -- ^ 64-bit integer values | SqlBinaryT -- ^ Fixed-length binary data | SqlVarBinaryT -- ^ Variable-length binary data | SqlLongVarBinaryT -- ^ Variable-length binary data, max length implementation dependant | SqlDateT -- ^ A date | SqlTimeT -- ^ A time, no timezone | SqlTimeWithZoneT -- ^ A time, with timezone | SqlTimestampT -- ^ Combined date and time, no timezone | SqlTimestampWithZoneT -- ^ Combined date and time, with timezone | SqlUTCDateTimeT -- ^ UTC date\/time | SqlUTCTimeT -- ^ UTC time | SqlIntervalT SqlInterval -- ^ A time or date difference | SqlGUIDT -- ^ Global unique identifier | SqlUnknownT String -- ^ A type not represented here; implementation-specific information in the String deriving (Eq, Show, Read, Typeable) {- | The different types of intervals in SQL. -} data SqlInterval = SqlIntervalMonthT -- ^ Difference in months | SqlIntervalYearT -- ^ Difference in years | SqlIntervalYearToMonthT -- ^ Difference in years+months | SqlIntervalDayT -- ^ Difference in days | SqlIntervalHourT -- ^ Difference in hours | SqlIntervalMinuteT -- ^ Difference in minutes | SqlIntervalSecondT -- ^ Difference in seconds | SqlIntervalDayToHourT -- ^ Difference in days+hours | SqlIntervalDayToMinuteT -- ^ Difference in days+minutes | SqlIntervalDayToSecondT -- ^ Difference in days+seconds | SqlIntervalHourToMinuteT -- ^ Difference in hours+minutes | SqlIntervalHourToSecondT -- ^ Difference in hours+seconds | SqlIntervalMinuteToSecondT -- ^ Difference in minutes+seconds deriving (Eq, Show, Read, Typeable) HDBC-2.4.0.2/Database/HDBC/DriverUtils.hs0000644000000000000000000000446713146574642015606 0ustar0000000000000000{- | Module : Database.HDBC.DriverUtils Copyright : Copyright (C) 2006 John Goerzen License : BSD3 Maintainer : John Goerzen Stability : provisional Portability: portable Utilities for database backend drivers. Please note: this module is intended for authors of database driver libraries only. Authors of applications using HDBC should use 'Database.HDBC' exclusively. Written by John Goerzen, jgoerzen\@complete.org -} module Database.HDBC.DriverUtils ( ChildList, closeAllChildren, addChild ) where import Control.Concurrent.MVar import System.Mem.Weak import Database.HDBC.Types import Control.Monad type ChildList = MVar [Weak Statement] {- | Close all children. Intended to be called by the 'disconnect' function in 'Connection'. There may be a potential race condition wherein a call to newSth at the same time as a call to this function may result in the new child not being closed. -} closeAllChildren :: ChildList -> IO () closeAllChildren mcl = do children <- readMVar mcl mapM_ closefunc children where closefunc child = do c <- deRefWeak child case c of Nothing -> return () Just x -> finish x {- | Adds a new child to the existing list. Also takes care of registering a finalizer for it, to remove it from the list when possible. -} addChild :: ChildList -> Statement -> IO () addChild mcl stmt = do weakptr <- mkWeakPtr stmt (Just (childFinalizer mcl)) modifyMVar_ mcl (\l -> return (weakptr : l)) {- | The general finalizer for a child. It is simply a filter that removes any finalized weak pointers from the parent. If the MVar is locked at the start, does nothing to avoid deadlock. Future runs would probably catch it anyway. -} childFinalizer :: ChildList -> IO () childFinalizer mcl = do c <- tryTakeMVar mcl case c of Nothing -> return () Just cl -> do newlist <- filterM filterfunc cl putMVar mcl newlist where filterfunc c = do dc <- deRefWeak c case dc of Nothing -> return False Just _ -> return True HDBC-2.4.0.2/Database/HDBC/Locale.hs0000644000000000000000000000110613146574642014514 0ustar0000000000000000{-# LANGUAGE CPP #-} module Database.HDBC.Locale ( defaultTimeLocale, iso8601DateFormat ) where #ifdef MIN_TIME_15 import Data.Time.Format (defaultTimeLocale) #else import System.Locale (defaultTimeLocale) #endif -- | As the semantic of System.Locale.iso8601DateFormat has changed with -- old-locale-1.0.0.2 in a non-compatible way, we now define our own -- (compatible) version of it. iso8601DateFormat :: Maybe String -> String iso8601DateFormat mTimeFmt = "%Y-%m-%d" ++ case mTimeFmt of Nothing -> "" Just fmt -> ' ' : fmt HDBC-2.4.0.2/Database/HDBC/SqlValue.hs0000644000000000000000000013267413146574642015070 0ustar0000000000000000{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE StandaloneDeriving #-} module Database.HDBC.SqlValue ( -- * SQL value marshalling SqlValue(..), safeFromSql, toSql, fromSql, nToSql, iToSql, posixToSql ) where import Data.Dynamic import qualified Data.ByteString.UTF8 as BUTF8 import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BSL import Data.Char(ord,toUpper) import Data.Word import Data.Int import qualified System.Time as ST import Data.Time ( Day (ModifiedJulianDay), DiffTime, LocalTime, NominalDiffTime, ParseTime , TimeOfDay, TimeZone, UTCTime, ZonedTime, formatTime, localDay, localTimeOfDay , timeOfDayToTime, timeToTimeOfDay, toModifiedJulianDay, utc , utcToZonedTime, zonedTimeToLocalTime, zonedTimeToUTC, zonedTimeZone #if MIN_TIME_15 , parseTimeM #else , parseTime #endif ) import Data.Time.Clock.POSIX import Database.HDBC.Locale (defaultTimeLocale, iso8601DateFormat) import Data.Ratio import Data.Convertible import Data.Fixed import qualified Data.Text as TS import qualified Data.Text.Lazy as TL quickError :: (Typeable a, Convertible SqlValue a) => SqlValue -> ConvertResult a quickError sv = convError "incompatible types" sv {- | Convert a value to an 'SqlValue'. This function is simply a restricted-type wrapper around 'convert'. See extended notes on 'SqlValue'. -} toSql :: Convertible a SqlValue => a -> SqlValue toSql = convert {- | Conversions to and from 'SqlValue's and standard Haskell types. This function converts from an 'SqlValue' to a Haskell value. Many people will use the simpler 'fromSql' instead. This function is simply a restricted-type wrapper around 'safeConvert'. -} safeFromSql :: Convertible SqlValue a => SqlValue -> ConvertResult a safeFromSql = safeConvert {- | Convert from an 'SqlValue' to a Haskell value. Any problem is indicated by calling 'error'. This function is simply a restricted-type wrapper around 'convert'. See extended notes on 'SqlValue'. -} fromSql :: Convertible SqlValue a => SqlValue -> a fromSql = convert {- | Converts any Integral type to a 'SqlValue' by using toInteger. -} nToSql :: Integral a => a -> SqlValue nToSql n = SqlInteger (toInteger n) {- | Convenience function for using numeric literals in your program. -} iToSql :: Int -> SqlValue iToSql = toSql {- | Convenience function for converting 'POSIXTime' to a 'SqlValue', because 'toSql' cannot do the correct thing in this instance. -} posixToSql :: POSIXTime -> SqlValue posixToSql x = SqlPOSIXTime x {- | 'SqlValue' is the main type for expressing Haskell values to SQL databases. /INTRODUCTION TO SQLVALUE/ This type is used to marshall Haskell data to and from database APIs. HDBC driver interfaces will do their best to use the most accurate and efficient way to send a particular value to the database server. Values read back from the server are constructed with the most appropriate 'SqlValue' constructor. 'fromSql' or 'safeFromSql' can then be used to convert them into whatever type is needed locally in Haskell. Most people will use 'toSql' and 'fromSql' instead of manipulating 'SqlValue's directly. /EASY CONVERSIONS BETWEEN HASKELL TYPES/ Conversions are powerful; for instance, you can call 'fromSql' on a SqlInt32 and get a String or a Double out of it. This class attempts to Do The Right Thing whenever possible, and will raise an error when asked to do something incorrect. In particular, when converting to any type except a Maybe, 'SqlNull' as the input will cause an error to be raised. Conversions are implemented in terms of the "Data.Convertible" module, part of the convertible package. You can refer to its documentation, and import that module, if you wish to parse the Left result from 'safeFromSql' yourself, or write your own conversion instances. Here are some notes about conversion: * Fractions of a second are not preserved on time values * There is no @safeToSql@ because 'toSql' never fails. See also 'toSql', 'safeFromSql', 'fromSql', 'nToSql', 'iToSql', 'posixToSql'. /ERROR CONDITIONS/ There may sometimes be an error during conversion. For instance, if you have a 'SqlString' and are attempting to convert it to an Integer, but it doesn't parse as an Integer, you will get an error. This will be indicated as an exception if using 'fromSql', or a Left result if using 'safeFromSql'. /SPECIAL NOTE ON POSIXTIME/ Note that a 'NominalDiffTime' or 'POSIXTime' is converted to 'SqlDiffTime' by 'toSql'. HDBC cannot differentiate between 'NominalDiffTime' and 'POSIXTime' since they are the same underlying type. You must construct 'SqlPOSIXTime' manually or via 'posixToSql', or use 'SqlUTCTime'. /DETAILS ON SQL TYPES/ HDBC database backends are expected to marshal date and time data back and forth using the appropriate representation for the underlying database engine. Databases such as PostgreSQL with builtin date and time types should see automatic conversion between these Haskell types to database types. Other databases will be presented with an integer or a string. Care should be taken to use the same type on the Haskell side as you use on the database side. For instance, if your database type lacks timezone information, you ought not to use ZonedTime, but instead LocalTime or UTCTime. Database type systems are not always as rich as Haskell. For instance, for data stored in a TIMESTAMP WITHOUT TIME ZONE column, HDBC may not be able to tell if it is intended as UTCTime or LocalTime data, and will happily convert it to both, upon your request. It is your responsibility to ensure that you treat timezone issues with due care. This behavior also exists for other types. For instance, many databases do not have a Rational type, so they will just use the show function and store a Rational as a string. The conversion between Haskell types and database types is complex, and generic code in HDBC or its backends cannot possibly accomodate every possible situation. In some cases, you may be best served by converting your Haskell type to a String, and passing that to the database. /UNICODE AND BYTESTRINGS/ Beginning with HDBC v2.0, interactions with a database are presumed to occur in UTF-8. To accomplish this, whenever a ByteString must be converted to or from a String, the ByteString is assumed to be in UTF-8 encoding, and will be decoded or encoded as appropriate. Database drivers will generally present text or string data they have received from the database as a SqlValue holding a ByteString, which 'fromSql' will automatically convert to a String, and thus automatically decode UTF-8, when you need it. In the other direction, database drivers will generally convert a 'SqlString' to a ByteString in UTF-8 encoding before passing it to the database engine. If you are handling some sort of binary data that is not in UTF-8, you can of course work with the ByteString directly, which will bypass any conversion. Due to lack of support by database engines, lazy ByteStrings are not passed to database drivers. When you use 'toSql' on a lazy ByteString, it will be converted to a strict ByteString for storage. Similarly, 'fromSql' will convert a strict ByteString to a lazy ByteString if you demand it. /EQUALITY OF SQLVALUE/ Two SqlValues are considered to be equal if one of these hold. The first comparison that can be made is controlling; if none of these comparisons can be made, then they are not equal: * Both are NULL * Both represent the same type and the encapsulated values are considered equal by applying (==) to them * The values of each, when converted to a string, are equal /STRING VERSIONS OF TIMES/ Default string representations are given as comments below where such are non-obvious. These are used for 'fromSql' when a 'String' is desired. They are also defaults for representing data to SQL backends, though individual backends may override them when a different format is demanded by the underlying database. Date and time formats use ISO8601 date format, with HH:MM:SS added for time, and -HHMM added for timezone offsets. /DEPRECATED CONSTRUCTORS/ 'SqlEpochTime' and 'SqlTimeDiff' are no longer created automatically by any 'toSql' or 'fromSql' functions or database backends. They may still be manually constructed, but are expected to be removed in a future version. Although these two constructures will be removed, support for marshalling to and from the old System.Time data will be maintained as long as System.Time is, simply using the newer data types for conversion. -} data SqlValue = SqlString String | SqlByteString B.ByteString | SqlWord32 Word32 | SqlWord64 Word64 | SqlInt32 Int32 | SqlInt64 Int64 | SqlInteger Integer | SqlChar Char | SqlBool Bool | SqlDouble Double | SqlRational Rational | SqlLocalDate Day -- ^ Local YYYY-MM-DD (no timezone). | SqlLocalTimeOfDay TimeOfDay -- ^ Local HH:MM:SS (no timezone). | SqlZonedLocalTimeOfDay TimeOfDay TimeZone -- ^ Local HH:MM:SS -HHMM. Converts to and from (TimeOfDay, TimeZone). | SqlLocalTime LocalTime -- ^ Local YYYY-MM-DD HH:MM:SS (no timezone). | SqlZonedTime ZonedTime -- ^ Local YYYY-MM-DD HH:MM:SS -HHMM. Considered equal if both convert to the same UTC time. | SqlUTCTime UTCTime -- ^ UTC YYYY-MM-DD HH:MM:SS. | SqlDiffTime NominalDiffTime -- ^ Calendar diff between seconds. Rendered as Integer when converted to String, but greater precision may be preserved for other types or to underlying database. | SqlPOSIXTime POSIXTime -- ^ Time as seconds since midnight Jan 1 1970 UTC. Integer rendering as for 'SqlDiffTime'. | SqlEpochTime Integer -- ^ DEPRECATED Representation of ClockTime or CalendarTime. Use SqlPOSIXTime instead. | SqlTimeDiff Integer -- ^ DEPRECATED Representation of TimeDiff. Use SqlDiffTime instead. | SqlNull -- ^ NULL in SQL or Nothing in Haskell. deriving (Show, Typeable) instance Eq SqlValue where SqlString a == SqlString b = a == b SqlByteString a == SqlByteString b = a == b SqlWord32 a == SqlWord32 b = a == b SqlWord64 a == SqlWord64 b = a == b SqlInt32 a == SqlInt32 b = a == b SqlInt64 a == SqlInt64 b = a == b SqlInteger a == SqlInteger b = a == b SqlChar a == SqlChar b = a == b SqlBool a == SqlBool b = a == b SqlDouble a == SqlDouble b = a == b SqlRational a == SqlRational b = a == b SqlLocalTimeOfDay a == SqlLocalTimeOfDay b = a == b SqlZonedLocalTimeOfDay a b == SqlZonedLocalTimeOfDay c d = a == c && b == d SqlLocalTime a == SqlLocalTime b = a == b SqlLocalDate a == SqlLocalDate b = a == b SqlZonedTime a == SqlZonedTime b = zonedTimeToUTC a == zonedTimeToUTC b SqlUTCTime a == SqlUTCTime b = a == b SqlPOSIXTime a == SqlPOSIXTime b = a == b SqlDiffTime a == SqlDiffTime b = a == b SqlEpochTime a == SqlEpochTime b = a == b SqlTimeDiff a == SqlTimeDiff b = a == b SqlNull == SqlNull = True SqlNull == _ = False _ == SqlNull = False a == b = ((safeFromSql a)::ConvertResult String) == ((safeFromSql b)::ConvertResult String) deriving instance Typeable ST.ClockTime deriving instance Typeable ST.TimeDiff instance Convertible SqlValue SqlValue where safeConvert = return instance Convertible String SqlValue where safeConvert = return . SqlString instance Convertible SqlValue String where safeConvert (SqlString x) = return x safeConvert (SqlByteString x) = return . BUTF8.toString $ x safeConvert (SqlInt32 x) = return . show $ x safeConvert (SqlInt64 x) = return . show $ x safeConvert (SqlWord32 x) = return . show $ x safeConvert (SqlWord64 x) = return . show $ x safeConvert (SqlInteger x) = return . show $ x safeConvert (SqlChar x) = return [x] safeConvert (SqlBool x) = return . show $ x safeConvert (SqlDouble x) = return . show $ x safeConvert (SqlRational x) = return . show $ x safeConvert (SqlLocalDate x) = return . formatTime defaultTimeLocale (iso8601DateFormat Nothing) $ x safeConvert (SqlLocalTimeOfDay x) = return . formatTime defaultTimeLocale "%T%Q" $ x safeConvert (SqlZonedLocalTimeOfDay tod tz) = return $ formatTime defaultTimeLocale "%T%Q " tod ++ formatTime defaultTimeLocale "%z" tz safeConvert (SqlLocalTime x) = return . formatTime defaultTimeLocale (iso8601DateFormat (Just "%T%Q")) $ x safeConvert (SqlZonedTime x) = return . formatTime defaultTimeLocale (iso8601DateFormat (Just "%T%Q %z")) $ x safeConvert (SqlUTCTime x) = return . formatTime defaultTimeLocale (iso8601DateFormat (Just "%T%Q")) $ x safeConvert (SqlDiffTime x) = return $ showFixed True fixedval where fixedval :: Pico fixedval = fromRational . toRational $ x safeConvert (SqlPOSIXTime x) = return $ showFixed True fixedval where fixedval :: Pico fixedval = fromRational . toRational $ x safeConvert (SqlEpochTime x) = return . show $ x safeConvert (SqlTimeDiff x) = return . show $ x safeConvert y@(SqlNull) = quickError y instance Convertible TS.Text SqlValue where safeConvert = return . SqlString . TS.unpack instance Convertible SqlValue TS.Text where safeConvert = fmap TS.pack . safeConvert instance Convertible TL.Text SqlValue where safeConvert = return . SqlString . TL.unpack instance Convertible SqlValue TL.Text where safeConvert = fmap TL.pack . safeConvert #ifdef __HUGS__ instance Typeable B.ByteString where typeOf _ = mkTypeName "ByteString" #endif instance Convertible B.ByteString SqlValue where safeConvert = return . SqlByteString instance Convertible SqlValue B.ByteString where safeConvert (SqlByteString x) = return x safeConvert y@(SqlNull) = quickError y safeConvert x = safeConvert x >>= return . BUTF8.fromString instance Convertible BSL.ByteString SqlValue where safeConvert = return . SqlByteString . B.concat . BSL.toChunks instance Convertible SqlValue BSL.ByteString where safeConvert x = do bs <- safeConvert x return (BSL.fromChunks [bs]) instance Convertible Int SqlValue where safeConvert x = do i <- ((safeConvert x)::ConvertResult Int64) return $ SqlInt64 i instance Convertible SqlValue Int where safeConvert (SqlString x) = read' x safeConvert (SqlByteString x) = (read' . BUTF8.toString) x safeConvert (SqlInt32 x) = safeConvert x safeConvert (SqlInt64 x) = safeConvert x safeConvert (SqlWord32 x) = safeConvert x safeConvert (SqlWord64 x) = safeConvert x safeConvert (SqlInteger x) = safeConvert x safeConvert (SqlChar x) = safeConvert x safeConvert (SqlBool x) = return (if x then 1 else 0) safeConvert (SqlDouble x) = safeConvert x safeConvert (SqlRational x) = safeConvert x safeConvert y@(SqlLocalDate _) = viaInteger y fromIntegral safeConvert y@(SqlLocalTimeOfDay _) = viaInteger y fromIntegral safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = viaInteger y fromIntegral safeConvert y@(SqlZonedTime _) = viaInteger y fromIntegral safeConvert (SqlUTCTime x) = safeConvert x safeConvert (SqlDiffTime x) = safeConvert x safeConvert (SqlPOSIXTime x) = safeConvert x safeConvert (SqlEpochTime x) = safeConvert x safeConvert (SqlTimeDiff x) = safeConvert x safeConvert y@(SqlNull) = quickError y instance Convertible Int32 SqlValue where safeConvert = return . SqlInt32 instance Convertible SqlValue Int32 where safeConvert (SqlString x) = read' x safeConvert (SqlByteString x) = (read' . BUTF8.toString) x safeConvert (SqlInt32 x) = return x safeConvert (SqlInt64 x) = safeConvert x safeConvert (SqlWord32 x) = safeConvert x safeConvert (SqlWord64 x) = safeConvert x safeConvert (SqlInteger x) = safeConvert x safeConvert (SqlChar x) = safeConvert x safeConvert (SqlBool x) = return (if x then 1 else 0) safeConvert (SqlDouble x) = safeConvert x safeConvert (SqlRational x) = safeConvert x safeConvert y@(SqlLocalDate _) = viaInteger y fromIntegral safeConvert y@(SqlLocalTimeOfDay _) = viaInteger y fromIntegral safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = viaInteger y fromIntegral safeConvert y@(SqlZonedTime _) = viaInteger y fromIntegral safeConvert y@(SqlUTCTime _) = viaInteger y fromIntegral safeConvert y@(SqlDiffTime _) = viaInteger y fromIntegral safeConvert y@(SqlPOSIXTime _) = viaInteger y fromIntegral safeConvert (SqlEpochTime x) = safeConvert x safeConvert (SqlTimeDiff x) = safeConvert x safeConvert y@(SqlNull) = quickError y instance Convertible Int64 SqlValue where safeConvert = return . SqlInt64 instance Convertible SqlValue Int64 where safeConvert (SqlString x) = read' x safeConvert (SqlByteString x) = (read' . BUTF8.toString) x safeConvert (SqlInt32 x) = safeConvert x safeConvert (SqlInt64 x) = return x safeConvert (SqlWord32 x) = safeConvert x safeConvert (SqlWord64 x) = safeConvert x safeConvert (SqlInteger x) = safeConvert x safeConvert (SqlChar x) = safeConvert x safeConvert (SqlBool x) = return (if x then 1 else 0) safeConvert (SqlDouble x) = safeConvert x safeConvert (SqlRational x) = safeConvert x safeConvert y@(SqlLocalDate _) = viaInteger y fromIntegral safeConvert y@(SqlLocalTimeOfDay _) = viaInteger y fromIntegral safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = viaInteger y fromIntegral safeConvert y@(SqlZonedTime _) = viaInteger y fromIntegral safeConvert y@(SqlUTCTime _) = viaInteger y fromIntegral safeConvert y@(SqlDiffTime _) = viaInteger y fromIntegral safeConvert y@(SqlPOSIXTime _) = viaInteger y fromIntegral safeConvert (SqlEpochTime x) = safeConvert x safeConvert (SqlTimeDiff x) = safeConvert x safeConvert y@(SqlNull) = quickError y instance Convertible Word32 SqlValue where safeConvert = return . SqlWord32 instance Convertible SqlValue Word32 where safeConvert (SqlString x) = read' x safeConvert (SqlByteString x) = (read' . BUTF8.toString) x safeConvert (SqlInt32 x) = safeConvert x safeConvert (SqlInt64 x) = safeConvert x safeConvert (SqlWord32 x) = return x safeConvert (SqlWord64 x) = safeConvert x safeConvert (SqlInteger x) = safeConvert x safeConvert (SqlChar x) = safeConvert x safeConvert (SqlBool x) = return (if x then 1 else 0) safeConvert (SqlDouble x) = safeConvert x safeConvert (SqlRational x) = safeConvert x safeConvert y@(SqlLocalDate _) = viaInteger y fromIntegral safeConvert y@(SqlLocalTimeOfDay _) = viaInteger y fromIntegral safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = viaInteger y fromIntegral safeConvert y@(SqlZonedTime _) = viaInteger y fromIntegral safeConvert y@(SqlUTCTime _) = viaInteger y fromIntegral safeConvert y@(SqlDiffTime _) = viaInteger y fromIntegral safeConvert y@(SqlPOSIXTime _) = viaInteger y fromIntegral safeConvert (SqlEpochTime x) = safeConvert x safeConvert (SqlTimeDiff x) = safeConvert x safeConvert y@(SqlNull) = quickError y instance Convertible Word64 SqlValue where safeConvert = return . SqlWord64 instance Convertible SqlValue Word64 where safeConvert (SqlString x) = read' x safeConvert (SqlByteString x) = (read' . BUTF8.toString) x safeConvert (SqlInt32 x) = safeConvert x safeConvert (SqlInt64 x) = safeConvert x safeConvert (SqlWord32 x) = safeConvert x safeConvert (SqlWord64 x) = return x safeConvert (SqlInteger x) = safeConvert x safeConvert (SqlChar x) = safeConvert x safeConvert (SqlBool x) = return (if x then 1 else 0) safeConvert (SqlDouble x) = safeConvert x safeConvert (SqlRational x) = safeConvert x safeConvert y@(SqlLocalDate _) = viaInteger y fromIntegral safeConvert y@(SqlLocalTimeOfDay _) = viaInteger y fromIntegral safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = viaInteger y fromIntegral safeConvert y@(SqlZonedTime _) = viaInteger y fromIntegral safeConvert y@(SqlUTCTime _) = viaInteger y fromIntegral safeConvert y@(SqlDiffTime _) = viaInteger y fromIntegral safeConvert y@(SqlPOSIXTime _) = viaInteger y fromIntegral safeConvert (SqlEpochTime x) = safeConvert x safeConvert (SqlTimeDiff x) = safeConvert x safeConvert y@(SqlNull) = quickError y instance Convertible Integer SqlValue where safeConvert = return . SqlInteger instance Convertible SqlValue Integer where safeConvert (SqlString x) = read' x safeConvert (SqlByteString x) = (read' . BUTF8.toString) x safeConvert (SqlInt32 x) = safeConvert x safeConvert (SqlInt64 x) = safeConvert x safeConvert (SqlWord32 x) = safeConvert x safeConvert (SqlWord64 x) = safeConvert x safeConvert (SqlInteger x) = return x safeConvert (SqlChar x) = safeConvert x safeConvert (SqlBool x) = return (if x then 1 else 0) safeConvert (SqlDouble x) = safeConvert x safeConvert (SqlRational x) = safeConvert x safeConvert (SqlLocalDate x) = return . toModifiedJulianDay $ x safeConvert (SqlLocalTimeOfDay x) = return . fromIntegral . fromEnum . timeOfDayToTime $ x safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert (SqlZonedTime x) = return . truncate . utcTimeToPOSIXSeconds . zonedTimeToUTC $ x safeConvert (SqlUTCTime x) = safeConvert x safeConvert (SqlDiffTime x) = safeConvert x safeConvert (SqlPOSIXTime x) = safeConvert x safeConvert (SqlEpochTime x) = return x safeConvert (SqlTimeDiff x) = return x safeConvert y@(SqlNull) = quickError y instance Convertible Bool SqlValue where safeConvert = return . SqlBool instance Convertible SqlValue Bool where safeConvert y@(SqlString x) = case map toUpper x of "TRUE" -> Right True "T" -> Right True "FALSE" -> Right False "F" -> Right False "0" -> Right False "1" -> Right True _ -> convError "Cannot parse given String as Bool" y safeConvert (SqlByteString x) = (safeConvert . SqlString . BUTF8.toString) x safeConvert (SqlInt32 x) = numToBool x safeConvert (SqlInt64 x) = numToBool x safeConvert (SqlWord32 x) = numToBool x safeConvert (SqlWord64 x) = numToBool x safeConvert (SqlInteger x) = numToBool x safeConvert (SqlChar x) = numToBool (ord x) safeConvert (SqlBool x) = return x safeConvert (SqlDouble x) = numToBool x safeConvert (SqlRational x) = numToBool x safeConvert y@(SqlLocalDate _) = quickError y safeConvert y@(SqlLocalTimeOfDay _) = quickError y safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert y@(SqlZonedTime _) = quickError y safeConvert y@(SqlUTCTime _) = quickError y safeConvert y@(SqlDiffTime _) = quickError y safeConvert y@(SqlPOSIXTime _) = quickError y safeConvert (SqlEpochTime x) = numToBool x safeConvert (SqlTimeDiff x) = numToBool x safeConvert y@(SqlNull) = quickError y numToBool :: (Eq a, Num a) => a -> ConvertResult Bool numToBool x = Right (x /= 0) instance Convertible Char SqlValue where safeConvert = return . SqlChar instance Convertible SqlValue Char where safeConvert (SqlString [x]) = return x safeConvert y@(SqlString _) = convError "String length /= 1" y safeConvert (SqlByteString x) = safeConvert . SqlString . BUTF8.toString $ x safeConvert y@(SqlInt32 _) = quickError y safeConvert y@(SqlInt64 _) = quickError y safeConvert y@(SqlWord32 _) = quickError y safeConvert y@(SqlWord64 _) = quickError y safeConvert y@(SqlInteger _) = quickError y safeConvert (SqlChar x) = return x safeConvert (SqlBool x) = return (if x then '1' else '0') safeConvert y@(SqlDouble _) = quickError y safeConvert y@(SqlRational _) = quickError y safeConvert y@(SqlLocalDate _) = quickError y safeConvert y@(SqlLocalTimeOfDay _) = quickError y safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert y@(SqlZonedTime _) = quickError y safeConvert y@(SqlUTCTime _) = quickError y safeConvert y@(SqlDiffTime _) = quickError y safeConvert y@(SqlPOSIXTime _) = quickError y safeConvert y@(SqlEpochTime _) = quickError y safeConvert y@(SqlTimeDiff _) = quickError y safeConvert y@(SqlNull) = quickError y instance Convertible Double SqlValue where safeConvert = return . SqlDouble instance Convertible SqlValue Double where safeConvert (SqlString x) = read' x safeConvert (SqlByteString x) = (read' . BUTF8.toString) x safeConvert (SqlInt32 x) = safeConvert x safeConvert (SqlInt64 x) = safeConvert x safeConvert (SqlWord32 x) = safeConvert x safeConvert (SqlWord64 x) = safeConvert x safeConvert (SqlInteger x) = safeConvert x safeConvert (SqlChar x) = return . fromIntegral . fromEnum $ x safeConvert (SqlBool x) = return (if x then 1.0 else 0.0) safeConvert (SqlDouble x) = return x safeConvert (SqlRational x) = safeConvert x safeConvert y@(SqlLocalDate _) = ((safeConvert y)::ConvertResult Integer) >>= (return . fromIntegral) safeConvert (SqlLocalTimeOfDay x) = return . fromRational . toRational . timeOfDayToTime $ x safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert (SqlZonedTime x) = safeConvert . SqlUTCTime . zonedTimeToUTC $ x safeConvert (SqlUTCTime x) = return . fromRational . toRational . utcTimeToPOSIXSeconds $ x safeConvert (SqlDiffTime x) = safeConvert x safeConvert (SqlPOSIXTime x) = safeConvert x safeConvert (SqlEpochTime x) = safeConvert x safeConvert (SqlTimeDiff x) = safeConvert x safeConvert y@(SqlNull) = quickError y instance Convertible Rational SqlValue where safeConvert = return . SqlRational instance Convertible SqlValue Rational where safeConvert (SqlString x) = read' x safeConvert (SqlByteString x) = (read' . BUTF8.toString) x safeConvert (SqlInt32 x) = safeConvert x safeConvert (SqlInt64 x) = safeConvert x safeConvert (SqlWord32 x) = safeConvert x safeConvert (SqlWord64 x) = safeConvert x safeConvert (SqlInteger x) = safeConvert x safeConvert (SqlChar x) = return . fromIntegral . fromEnum $ x safeConvert (SqlBool x) = return $ if x then fromIntegral (1::Int) else fromIntegral (0::Int) safeConvert (SqlDouble x) = safeConvert x safeConvert (SqlRational x) = return x safeConvert y@(SqlLocalDate _) = ((safeConvert y)::ConvertResult Integer) >>= (return . fromIntegral) safeConvert (SqlLocalTimeOfDay x) = return . toRational . timeOfDayToTime $ x safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert (SqlZonedTime x) = safeConvert . SqlUTCTime . zonedTimeToUTC $ x safeConvert (SqlUTCTime x) = safeConvert x safeConvert (SqlDiffTime x) = safeConvert x safeConvert (SqlPOSIXTime x) = safeConvert x safeConvert (SqlEpochTime x) = return . fromIntegral $ x safeConvert (SqlTimeDiff x) = return . fromIntegral $ x safeConvert y@(SqlNull) = quickError y instance Convertible Day SqlValue where safeConvert = return . SqlLocalDate instance Convertible SqlValue Day where safeConvert (SqlString x) = parseTime' (iso8601DateFormat Nothing) x safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x)) safeConvert (SqlInt32 x) = return $ ModifiedJulianDay {toModifiedJulianDay = fromIntegral x} safeConvert (SqlInt64 x) = return $ ModifiedJulianDay {toModifiedJulianDay = fromIntegral x} safeConvert (SqlWord32 x) = return $ ModifiedJulianDay {toModifiedJulianDay = fromIntegral x} safeConvert (SqlWord64 x) = return $ ModifiedJulianDay {toModifiedJulianDay = fromIntegral x} safeConvert (SqlInteger x) = return $ ModifiedJulianDay {toModifiedJulianDay = x} safeConvert y@(SqlChar _) = quickError y safeConvert y@(SqlBool _) = quickError y safeConvert (SqlDouble x) = return $ ModifiedJulianDay {toModifiedJulianDay = truncate x} safeConvert (SqlRational x) = safeConvert . SqlDouble . fromRational $ x safeConvert (SqlLocalDate x) = return x safeConvert y@(SqlLocalTimeOfDay _) = quickError y safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert (SqlLocalTime x) = return . localDay $ x safeConvert y@(SqlZonedTime _) = safeConvert y >>= return . localDay safeConvert y@(SqlUTCTime _) = safeConvert y >>= return . localDay safeConvert y@(SqlDiffTime _) = quickError y safeConvert y@(SqlPOSIXTime _) = safeConvert y >>= return . localDay safeConvert y@(SqlEpochTime _) = safeConvert y >>= return . localDay safeConvert y@(SqlTimeDiff _) = quickError y safeConvert y@(SqlNull) = quickError y instance Convertible TimeOfDay SqlValue where safeConvert = return . SqlLocalTimeOfDay instance Convertible SqlValue TimeOfDay where safeConvert (SqlString x) = parseTime' "%T%Q" x safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x)) safeConvert (SqlInt32 x) = return . timeToTimeOfDay . fromIntegral $ x safeConvert (SqlInt64 x) = return . timeToTimeOfDay . fromIntegral $ x safeConvert (SqlWord32 x) = return . timeToTimeOfDay . fromIntegral $ x safeConvert (SqlWord64 x) = return . timeToTimeOfDay . fromIntegral $ x safeConvert (SqlInteger x) = return . timeToTimeOfDay . fromInteger $ x safeConvert y@(SqlChar _) = quickError y safeConvert y@(SqlBool _) = quickError y safeConvert (SqlDouble x) = return . timeToTimeOfDay . fromIntegral $ ((truncate x)::Integer) safeConvert (SqlRational x) = safeConvert . SqlDouble . fromRational $ x safeConvert y@(SqlLocalDate _) = quickError y safeConvert (SqlLocalTimeOfDay x) = return x safeConvert (SqlZonedLocalTimeOfDay tod _) = return tod safeConvert (SqlLocalTime x) = return . localTimeOfDay $ x safeConvert y@(SqlZonedTime _) = safeConvert y >>= return . localTimeOfDay safeConvert y@(SqlUTCTime _) = safeConvert y >>= return . localTimeOfDay safeConvert y@(SqlDiffTime _) = quickError y safeConvert y@(SqlPOSIXTime _) = safeConvert y >>= return . localTimeOfDay safeConvert y@(SqlEpochTime _) = safeConvert y >>= return . localTimeOfDay safeConvert y@(SqlTimeDiff _) = quickError y safeConvert y@SqlNull = quickError y instance Convertible (TimeOfDay, TimeZone) SqlValue where safeConvert (tod, tz) = return (SqlZonedLocalTimeOfDay tod tz) instance Convertible SqlValue (TimeOfDay, TimeZone) where safeConvert (SqlString x) = do tod <- parseTime' "%T%Q %z" x #if MIN_TIME_15 tz <- case parseTimeM True defaultTimeLocale "%T%Q %z" x of #else tz <- case parseTime defaultTimeLocale "%T%Q %z" x of #endif Nothing -> convError "Couldn't extract timezone in" (SqlString x) Just y -> Right y return (tod, tz) safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x)) safeConvert y@(SqlInt32 _) = quickError y safeConvert y@(SqlInt64 _) = quickError y safeConvert y@(SqlWord32 _) = quickError y safeConvert y@(SqlWord64 _) = quickError y safeConvert y@(SqlInteger _) = quickError y safeConvert y@(SqlChar _) = quickError y safeConvert y@(SqlBool _) = quickError y safeConvert y@(SqlDouble _) = quickError y safeConvert y@(SqlRational _) = quickError y safeConvert y@(SqlLocalDate _) = quickError y safeConvert y@(SqlLocalTimeOfDay _) = quickError y safeConvert (SqlZonedLocalTimeOfDay x y) = return (x, y) safeConvert y@(SqlLocalTime _) = quickError y safeConvert (SqlZonedTime x) = return (localTimeOfDay . zonedTimeToLocalTime $ x, zonedTimeZone x) safeConvert y@(SqlUTCTime _) = quickError y safeConvert y@(SqlDiffTime _) = quickError y safeConvert y@(SqlPOSIXTime _) = quickError y safeConvert y@(SqlEpochTime _) = quickError y safeConvert y@(SqlTimeDiff _) = quickError y safeConvert y@SqlNull = quickError y instance Convertible LocalTime SqlValue where safeConvert = return . SqlLocalTime instance Convertible SqlValue LocalTime where safeConvert (SqlString x) = parseTime' (iso8601DateFormat (Just "%T%Q")) x safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x)) safeConvert y@(SqlInt32 _) = quickError y safeConvert y@(SqlInt64 _) = quickError y safeConvert y@(SqlWord32 _) = quickError y safeConvert y@(SqlWord64 _) = quickError y safeConvert y@(SqlInteger _) = quickError y safeConvert y@(SqlChar _) = quickError y safeConvert y@(SqlBool _) = quickError y safeConvert y@(SqlDouble _) = quickError y safeConvert y@(SqlRational _) = quickError y safeConvert y@(SqlLocalDate _) = quickError y safeConvert y@(SqlLocalTimeOfDay _) = quickError y safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert (SqlLocalTime x) = return x safeConvert (SqlZonedTime x) = return . zonedTimeToLocalTime $ x safeConvert y@(SqlUTCTime _) = safeConvert y >>= return . zonedTimeToLocalTime safeConvert y@(SqlDiffTime _) = quickError y safeConvert y@(SqlPOSIXTime _) = safeConvert y >>= return . zonedTimeToLocalTime safeConvert y@(SqlEpochTime _) = safeConvert y >>= return . zonedTimeToLocalTime safeConvert y@(SqlTimeDiff _) = quickError y safeConvert y@SqlNull = quickError y instance Convertible ZonedTime SqlValue where safeConvert = return . SqlZonedTime instance Convertible SqlValue ZonedTime where safeConvert (SqlString x) = parseTime' (iso8601DateFormat (Just "%T%Q %z")) x safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x)) safeConvert (SqlInt32 x) = safeConvert (SqlInteger (fromIntegral x)) safeConvert (SqlInt64 x) = safeConvert (SqlInteger (fromIntegral x)) safeConvert (SqlWord32 x) = safeConvert (SqlInteger (fromIntegral x)) safeConvert (SqlWord64 x) = safeConvert (SqlInteger (fromIntegral x)) safeConvert y@(SqlInteger _) = safeConvert y >>= return . utcToZonedTime utc safeConvert y@(SqlChar _) = quickError y safeConvert y@(SqlBool _) = quickError y safeConvert y@(SqlDouble _) = safeConvert y >>= return . utcToZonedTime utc safeConvert y@(SqlRational _) = safeConvert y >>= return . utcToZonedTime utc safeConvert y@(SqlLocalDate _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert y@(SqlLocalTimeOfDay _) = quickError y safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert (SqlZonedTime x) = return x safeConvert (SqlUTCTime x) = return . utcToZonedTime utc $ x safeConvert y@(SqlDiffTime _) = quickError y safeConvert y@(SqlPOSIXTime _) = safeConvert y >>= return . utcToZonedTime utc safeConvert y@(SqlEpochTime _) = safeConvert y >>= return . utcToZonedTime utc safeConvert y@(SqlTimeDiff _) = quickError y safeConvert y@SqlNull = quickError y instance Convertible UTCTime SqlValue where safeConvert = return . SqlUTCTime instance Convertible SqlValue UTCTime where safeConvert (SqlString x) = parseTime' (iso8601DateFormat (Just "%T%Q")) x safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x)) safeConvert y@(SqlInt32 _) = safeConvert y >>= return . posixSecondsToUTCTime safeConvert y@(SqlInt64 _) = safeConvert y >>= return . posixSecondsToUTCTime safeConvert y@(SqlWord32 _) = safeConvert y >>= return . posixSecondsToUTCTime safeConvert y@(SqlWord64 _) = safeConvert y >>= return . posixSecondsToUTCTime safeConvert y@(SqlInteger _) = safeConvert y >>= return . posixSecondsToUTCTime safeConvert y@(SqlChar _) = quickError y safeConvert y@(SqlBool _) = quickError y safeConvert y@(SqlDouble _) = safeConvert y >>= return . posixSecondsToUTCTime safeConvert y@(SqlRational _) = safeConvert y >>= return . posixSecondsToUTCTime safeConvert y@(SqlLocalDate _) = quickError y safeConvert y@(SqlLocalTimeOfDay _) = quickError y safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert (SqlZonedTime x) = return . zonedTimeToUTC $ x safeConvert (SqlUTCTime x) = return x safeConvert y@(SqlDiffTime _) = convError "incompatible types (did you mean SqlPOSIXTime?)" y safeConvert (SqlPOSIXTime x) = return . posixSecondsToUTCTime $ x safeConvert y@(SqlEpochTime _) = safeConvert y >>= return . posixSecondsToUTCTime safeConvert y@(SqlTimeDiff _) = convError "incompatible types (did you mean SqlPOSIXTime?)" y safeConvert y@SqlNull = quickError y stringToPico :: String -> ConvertResult Pico stringToPico s = let (base, fracwithdot) = span (/= '.') s shortfrac = drop 1 fracwithdot -- strip of dot; don't use tail because it may be empty frac = take 12 (rpad 12 '0' shortfrac) rpad :: Int -> a -> [a] -> [a] -- next line lifted from Data.Time rpad n c xs = xs ++ replicate (n - length xs) c mkPico :: Integer -> Integer -> Pico -- next line also lifted from Data.Time mkPico i f = fromInteger i + fromRational (f % 1000000000000) in do parsedBase <- read' base parsedFrac <- read' frac return (mkPico parsedBase parsedFrac) instance Convertible NominalDiffTime SqlValue where safeConvert = return . SqlDiffTime instance Convertible SqlValue NominalDiffTime where safeConvert (SqlString x) = stringToPico x >>= return . realToFrac safeConvert (SqlByteString x) = (stringToPico (BUTF8.toString x)) >>= return . realToFrac safeConvert (SqlInt32 x) = return . fromIntegral $ x safeConvert (SqlInt64 x) = return . fromIntegral $ x safeConvert (SqlWord32 x) = return . fromIntegral $ x safeConvert (SqlWord64 x) = return . fromIntegral $ x safeConvert (SqlInteger x) = return . fromIntegral $ x safeConvert y@(SqlChar _) = quickError y safeConvert y@(SqlBool _) = quickError y safeConvert (SqlDouble x) = return . fromRational . toRational $ x safeConvert (SqlRational x) = return . fromRational $ x safeConvert (SqlLocalDate x) = return . fromIntegral . (\y -> y * 60 * 60 * 24) . toModifiedJulianDay $ x safeConvert (SqlLocalTimeOfDay x) = return . fromRational . toRational . timeOfDayToTime $ x safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert (SqlZonedTime x) = return . utcTimeToPOSIXSeconds . zonedTimeToUTC $ x safeConvert (SqlUTCTime x) = return . utcTimeToPOSIXSeconds $ x safeConvert (SqlDiffTime x) = return x safeConvert (SqlPOSIXTime x) = return x safeConvert (SqlEpochTime x) = return . fromIntegral $ x safeConvert (SqlTimeDiff x) = return . fromIntegral $ x safeConvert y@SqlNull = quickError y instance Convertible ST.ClockTime SqlValue where safeConvert x = safeConvert x >>= return . SqlPOSIXTime instance Convertible SqlValue ST.ClockTime where safeConvert (SqlString x) = do r <- read' x return $ ST.TOD r 0 safeConvert (SqlByteString x) = safeConvert . SqlString . BUTF8.toString $ x safeConvert (SqlInt32 x) = return $ ST.TOD (fromIntegral x) 0 safeConvert (SqlInt64 x) = return $ ST.TOD (fromIntegral x) 0 safeConvert (SqlWord32 x) = return $ ST.TOD (fromIntegral x) 0 safeConvert (SqlWord64 x) = return $ ST.TOD (fromIntegral x) 0 safeConvert (SqlInteger x) = return $ ST.TOD x 0 safeConvert y@(SqlChar _) = quickError y safeConvert y@(SqlBool _) = quickError y safeConvert (SqlDouble x) = return $ ST.TOD (truncate x) 0 safeConvert (SqlRational x) = return $ ST.TOD (truncate x) 0 safeConvert y@(SqlLocalDate _) = quickError y safeConvert y@(SqlLocalTimeOfDay _) = quickError y safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert y@(SqlZonedTime _) = safeConvert y >>= (\z -> return $ ST.TOD z 0) safeConvert y@(SqlUTCTime _) = safeConvert y >>= (\z -> return $ ST.TOD z 0) safeConvert y@(SqlDiffTime _) = quickError y safeConvert y@(SqlPOSIXTime _) = safeConvert y >>= (\z -> return $ ST.TOD z 0) safeConvert (SqlEpochTime x) = return $ ST.TOD x 0 safeConvert y@(SqlTimeDiff _) = quickError y safeConvert y@SqlNull = quickError y instance Convertible ST.TimeDiff SqlValue where safeConvert x = safeConvert x >>= return . SqlDiffTime instance Convertible SqlValue ST.TimeDiff where safeConvert y@(SqlString _) = do r <- safeConvert y safeConvert (SqlDiffTime r) safeConvert (SqlByteString x) = safeConvert . SqlString . BUTF8.toString $ x safeConvert (SqlInt32 x) = secs2td (fromIntegral x) safeConvert (SqlInt64 x) = secs2td (fromIntegral x) safeConvert (SqlWord32 x) = secs2td (fromIntegral x) safeConvert (SqlWord64 x) = secs2td (fromIntegral x) safeConvert (SqlInteger x) = secs2td x safeConvert y@(SqlChar _) = quickError y safeConvert y@(SqlBool _) = quickError y safeConvert (SqlDouble x) = secs2td (truncate x) safeConvert (SqlRational x) = secs2td (truncate x) safeConvert y@(SqlLocalDate _) = quickError y safeConvert y@(SqlLocalTimeOfDay _) = quickError y safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert y@(SqlZonedTime _) = quickError y safeConvert y@(SqlUTCTime _) = quickError y safeConvert y@(SqlPOSIXTime _) = quickError y safeConvert (SqlDiffTime x) = safeConvert x safeConvert y@(SqlEpochTime _) = quickError y safeConvert (SqlTimeDiff x) = secs2td x safeConvert y@SqlNull = quickError y instance Convertible DiffTime SqlValue where safeConvert = return . SqlDiffTime . fromRational . toRational instance Convertible SqlValue DiffTime where safeConvert (SqlString x) = read' x >>= return . fromInteger safeConvert (SqlByteString x) = safeConvert . SqlString . BUTF8.toString $ x safeConvert (SqlInt32 x) = return . fromIntegral $ x safeConvert (SqlInt64 x) = return . fromIntegral $ x safeConvert (SqlWord32 x) = return . fromIntegral $ x safeConvert (SqlWord64 x) = return . fromIntegral $ x safeConvert (SqlInteger x) = return . fromIntegral $ x safeConvert y@(SqlChar _) = quickError y safeConvert y@(SqlBool _) = quickError y safeConvert (SqlDouble x) = return . fromRational . toRational $ x safeConvert (SqlRational x) = return . fromRational $ x safeConvert y@(SqlLocalDate _) = quickError y safeConvert y@(SqlLocalTimeOfDay _) = quickError y safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y safeConvert y@(SqlLocalTime _) = quickError y safeConvert y@(SqlZonedTime _) = quickError y safeConvert y@(SqlUTCTime _) = quickError y safeConvert (SqlDiffTime x) = return . fromRational . toRational $ x safeConvert y@(SqlPOSIXTime _) = quickError y safeConvert y@(SqlEpochTime _) = quickError y safeConvert (SqlTimeDiff x) = return . fromIntegral $ x safeConvert y@SqlNull = quickError y instance Convertible ST.CalendarTime SqlValue where -- convert via ZonedTime safeConvert x = safeConvert x >>= return . SqlZonedTime instance Convertible SqlValue ST.CalendarTime where -- convert via ZonedTime safeConvert = convertVia (undefined::ZonedTime) instance (Convertible a SqlValue) => Convertible (Maybe a) SqlValue where safeConvert Nothing = return SqlNull safeConvert (Just a) = safeConvert a instance (Convertible SqlValue a) => Convertible SqlValue (Maybe a) where safeConvert SqlNull = return Nothing safeConvert a = safeConvert a >>= (return . Just) viaInteger' :: (Convertible SqlValue a, Bounded a, Show a, Convertible a Integer, Typeable a) => SqlValue -> (Integer -> ConvertResult a) -> ConvertResult a viaInteger' sv func = do i <- ((safeConvert sv)::ConvertResult Integer) boundedConversion func i viaInteger :: (Convertible SqlValue a, Bounded a, Show a, Convertible a Integer, Typeable a) => SqlValue -> (Integer -> a) -> ConvertResult a viaInteger sv func = viaInteger' sv (return . func) secs2td :: Integer -> ConvertResult ST.TimeDiff secs2td x = safeConvert x -- | Read a value from a string, and give an informative message -- if it fails. read' :: (Typeable a, Read a, Convertible SqlValue a) => String -> ConvertResult a read' s = case reads s of [(x,"")] -> Right x _ -> convError "Cannot read source value as dest type" (SqlString s) #ifdef __HUGS__ parseTime' :: (Typeable t, Convertible SqlValue t) => String -> String -> ConvertResult t parseTime' _ inpstr = convError "Hugs does not support time parsing" (SqlString inpstr) #else parseTime' :: (Typeable t, Convertible SqlValue t, ParseTime t) => String -> String -> ConvertResult t parseTime' fmtstr inpstr = #if MIN_TIME_15 case parseTimeM True defaultTimeLocale fmtstr inpstr of #else case parseTime defaultTimeLocale fmtstr inpstr of #endif Nothing -> convError ("Cannot parse using default format string " ++ show fmtstr) (SqlString inpstr) Just x -> Right x #endif HDBC-2.4.0.2/Database/HDBC/Statement.hs0000644000000000000000000001056313146574642015270 0ustar0000000000000000{-# LANGUAGE DeriveDataTypeable #-} module Database.HDBC.Statement ( Statement(..), SqlError(..), -- * Re-exported from "Database.HDBC.SqlValue"; this re-exporting is deprecated nToSql, iToSql, posixToSql, fromSql, safeFromSql, toSql, SqlValue(..) ) where import Data.Dynamic import Database.HDBC.ColTypes import Database.HDBC.SqlValue import Control.Exception data Statement = Statement { {- | Execute the prepared statement, passing in the given positional parameters (that should take the place of the question marks in the call to 'prepare'). For non-SELECT queries, the return value is the number of rows modified, if known. If no rows were modified, you get 0. If the value is unknown, you get -1. All current HDBC drivers support this function and should never return -1. For SELECT queries, you will always get 0. This function should automatically call finish() to finish the previous execution, if necessary. -} execute :: [SqlValue] -> IO Integer, {- | Execute the statement as-is, without supplying any positional parameters. This is intended for statements for which the results aren't interesting or present (e.g., DDL or DML commands). If your query contains placeholders, this will certainly fail; use 'execute' instead. -} executeRaw :: IO (), {- | Execute the query with many rows. The return value is the return value from the final row as if you had called 'execute' on it. Due to optimizations that are possible due to different databases and driver designs, this can often be significantly faster than using 'execute' multiple times since queries need to be compiled only once. This is most useful for non-SELECT statements. -} executeMany :: [[SqlValue]] -> IO (), {- | Abort a query in progress -- usually not needed. -} finish :: IO (), {- | Fetches one row from the DB. Returns 'Nothing' if there are no more rows. Will automatically call 'finish' when the last row is read. -} fetchRow :: IO (Maybe [SqlValue]), {- | Returns a list of the column names in the result. For maximum portability, you should not assume that information is available until after an 'execute' function has been run. Information is returned here directly as returned by the underlying database layer. Note that different databases have different rules about capitalization of return values and about representation of names of columns that are not simple columns. For this reason, it is suggested that you treat this information for display purposes only. Failing that, you should convert to lower (or upper) case, and use @AS@ clauses for anything other than simple columns. A simple getColumnNames implementation could simply apply @map fst@ to the return value of 'describeResult'. -} getColumnNames :: IO [String], {- | The original query that this 'Statement' was prepared with. -} originalQuery :: String, {- | Obtain information about the columns in the result set. Must be run only after 'execute'. The String in the result set is the column name. You should expect this to be returned in the same manner as a result from 'Database.HDBC.fetchAllRows''. All results should be converted to lowercase for you before you see them. Please see caveats under 'getColumnNames' for information on the column name field here. -} describeResult :: IO [(String, SqlColDesc)] } {- | The main HDBC exception object. As much information as possible is passed from the database through to the application through this object. Errors generated in the Haskell layer will have seNativeError set to -1. -} data SqlError = SqlError {seState :: String, seNativeError :: Int, seErrorMsg :: String} deriving (Eq, Show, Read, Typeable) #if __GLASGOW_HASKELL__ >= 610 --data SqlException instance Exception SqlError where {- toException = SomeException fromException (SomeException e) = Just e fromException _ = Nothing -} #endif HDBC-2.4.0.2/Database/HDBC/Types.hs0000644000000000000000000002222313146574642014424 0ustar0000000000000000{- | Module : Database.HDBC.Types Copyright : Copyright (C) 2005-2011 John Goerzen License : BSD3 Maintainer : John Goerzen Stability : provisional Portability: portable Types for HDBC. Please note: this module is intended for authors of database driver libraries only. Authors of applications using HDBC should use 'Database.HDBC' exclusively. Written by John Goerzen, jgoerzen\@complete.org -} module Database.HDBC.Types (IConnection(..), Statement(..), SqlError(..), nToSql, iToSql, posixToSql, fromSql, safeFromSql, toSql, SqlValue(..), ConnWrapper(..), withWConn ) where import Database.HDBC.Statement import Database.HDBC.ColTypes import Control.Exception ( finally ) {- | Main database handle object. An 'IConnection' object is created by specific functions in the module for an individual database. That is, the connect function -- which creates this object -- is not standardized through the HDBC interface. A connection is closed by a call to 'disconnect'. A call to 'commit' is required to make sure that your changes get committed to the database. In other words, HDBC has /no support for autocommit/, which we consider an outdated notion. -} class IConnection conn where {- | Disconnect from the remote database. You do not need to explicitly close an IConnection object, but you may do so if you so desire. If you don't, the object will disconnect from the database in a sane way when it is garbage-collected. However, a disconnection may raise an error, so you are encouraged to explicitly call 'disconnect'. Also, garbage collection may not run when the program terminates, and some databases really like an explicit disconnect. So, bottom line is, you're best off calling 'disconnect' directly, but the world won't end if you forget. This function discards any data not committed already. Database driver implementators should explicitly call 'rollback' if their databases don't do this automatically on disconnect. Bad Things (TM) could happen if you call this while you have 'Statement's active. In more precise language, the results in such situations are undefined and vary by database. So don't do it. -} disconnect :: conn -> IO () {- | Commit any pending data to the database. Required to make any changes take effect. -} commit :: conn -> IO () {- | Roll back to the state the database was in prior to the last 'commit' or 'rollback'. -} rollback :: conn -> IO () {- | Execute an SQL string, which may contain multiple queries. This is intended for situations where you need to run DML or DDL queries and aren't interested in results. -} runRaw :: conn -> String -> IO () runRaw conn sql = do sth <- prepare conn sql _ <- execute sth [] `finally` finish sth return () {- | Execute a single SQL query. Returns the number of rows modified (see 'execute' for details). The second parameter is a list of replacement values, if any. -} run :: conn -> String -> [SqlValue] -> IO Integer {- | Prepares a statement for execution. Question marks in the statement will be replaced by positional parameters in a later call to 'execute'. Please note that, depending on the database and the driver, errors in your SQL may be raised either here or by 'execute'. Make sure you handle exceptions both places if necessary. -} prepare :: conn -> String -> IO Statement {- | Create a new 'Connection' object, pointed at the same server as this object is. This will generally establish a separate physical connection. When you wish to establish multiple connections to a single server, the correct way to do so is to establish the first connection with the driver-specific connection function, and then clone it for each additional connection. This can be important when a database doesn't provide much thread support itself, and the HDBC driver module must serialize access to a particular database. This can also be a handy utility function whenever you need a separate connection to whatever database you are connected to already. -} clone :: conn -> IO conn {- | The name of the HDBC driver module for this connection. Ideally would be the same as the database name portion of the Cabal package name. For instance, \"sqlite3\" or \"odbc\". This is the layer that is bound most tightly to HDBC. -} hdbcDriverName :: conn -> String {- | The version of the C (or whatever) client library that the HDBC driver module is bound to. The meaning of this is driver-specific. For an ODBC or similar proxying driver, this should be the version of the ODBC library, not the eventual DB client driver. -} hdbcClientVer :: conn -> String {- | In the case of a system such as ODBC, the name of the database client\/server in use, if available. For others, identical to 'hdbcDriverName'. -} proxiedClientName :: conn -> String {- | In the case of a system such as ODBC, the version of the database client in use, if available. For others, identical to 'hdbcClientVer'. This is the next layer out past the HDBC driver. -} proxiedClientVer :: conn -> String {- | The version of the database server, if available. -} dbServerVer :: conn -> String {- | Whether or not the current database supports transactions. If False, then 'commit' and 'rollback' should be expected to raise errors. MySQL is the only commonly-used database that is known to not support transactions entirely. Please see the MySQL notes in the ODBC driver for more information. -} dbTransactionSupport :: conn -> Bool {- | The names of all tables accessible by the current connection, excluding special meta-tables (system tables). You should expect this to be returned in the same manner as a result from 'Database.HDBC.fetchAllRows''. All results should be converted to lowercase for you before you see them. -} getTables :: conn -> IO [String] {- | Obtain information about the columns in a specific table. The String in the result set is the column name. You should expect this to be returned in the same manner as a result from 'Database.HDBC.fetchAllRows''. All results should be converted to lowercase for you before you see them. -} describeTable :: conn -> String -> IO [(String, SqlColDesc)] {- | Sometimes, it is annoying to use typeclasses with Haskell's type system. In those situations, you can use a ConnWrapper. You can create one with: >let wrapped = ConnWrapper iconn You can then use this directly, since a ConnWrapper is also an 'IConnection'. However, you will not be able to use private database functions on it. Or, you can use 'withWConn'. -} data ConnWrapper = forall conn. IConnection conn => ConnWrapper conn {- | Unwrap a 'ConnWrapper' and pass the embedded 'IConnection' to a function. Example: >withWConn wrapped run $ "SELECT * from foo where bar = 1" [] -} withWConn :: forall b. ConnWrapper -> (forall conn. IConnection conn => conn -> b) -> b withWConn conn f = case conn of ConnWrapper x -> f x instance IConnection ConnWrapper where disconnect w = withWConn w disconnect commit w = withWConn w commit rollback w = withWConn w rollback runRaw w = withWConn w runRaw run w = withWConn w run prepare w = withWConn w prepare clone w = withWConn w (\dbh -> clone dbh >>= return . ConnWrapper) hdbcDriverName w = withWConn w hdbcDriverName hdbcClientVer w = withWConn w hdbcClientVer proxiedClientName w = withWConn w proxiedClientName proxiedClientVer w = withWConn w proxiedClientVer dbServerVer w = withWConn w dbServerVer dbTransactionSupport w = withWConn w dbTransactionSupport getTables w = withWConn w getTables describeTable w = withWConn w describeTable HDBC-2.4.0.2/Database/HDBC/Utils.hs0000644000000000000000000002446713146574642014434 0ustar0000000000000000{-# LANGUAGE CPP #-} -- #hide {- | Module : Database.HDBC.Utils Copyright : Copyright (C) 2005-2011 John Goerzen License : BSD3 Maintainer : John Goerzen Stability : provisional Portability: portable Internal module -- not exported directly. Everything in here is expoerted by "Database.HDBC". Please use -- and read -- "Database.HDBC" directly. Written by John Goerzen, jgoerzen\@complete.org -} module Database.HDBC.Utils where import Database.HDBC.Types import qualified Data.Map as Map import Control.Exception import System.IO.Unsafe import Data.List(genericLength) -- import Data.Dynamic below for GHC < 6.10 #if __GLASGOW_HASKELL__ >= 610 {- | Execute the given IO action. If it raises a 'SqlError', then execute the supplied handler and return its return value. Otherwise, proceed as normal. -} catchSql :: IO a -> (SqlError -> IO a) -> IO a catchSql action handler = catchJust sqlExceptions action handler {- | Like 'catchSql', with the order of arguments reversed. -} handleSql :: (SqlError -> IO a) -> IO a -> IO a handleSql h f = catchSql f h {- | Given an Exception, return Just SqlError if it was an SqlError, or Nothing otherwise. Useful with functions like catchJust. -} sqlExceptions :: SqlError -> Maybe SqlError sqlExceptions e = Just e #else import Data.Dynamic {- | Execute the given IO action. If it raises a 'SqlError', then execute the supplied handler and return its return value. Otherwise, proceed as normal. -} catchSql :: IO a -> (SqlError -> IO a) -> IO a catchSql = catchDyn {- | Like 'catchSql', with the order of arguments reversed. -} handleSql :: (SqlError -> IO a) -> IO a -> IO a handleSql h f = catchDyn f h {- | Given an Exception, return Just SqlError if it was an SqlError, or Nothing otherwise. Useful with functions like catchJust. -} sqlExceptions :: Exception -> Maybe SqlError sqlExceptions e = dynExceptions e >>= fromDynamic #endif {- | Catches 'SqlError's, and re-raises them as IO errors with fail. Useful if you don't care to catch SQL errors, but want to see a sane error message if one happens. One would often use this as a high-level wrapper around SQL calls. -} handleSqlError :: IO a -> IO a handleSqlError action = catchSql action handler where handler e = fail ("SQL error: " ++ show e) {- | Like 'run', but take a list of Maybe Strings instead of 'SqlValue's. -} sRun :: IConnection conn => conn -> String -> [Maybe String] -> IO Integer sRun conn qry lst = run conn qry (map toSql lst) {- | Like 'execute', but take a list of Maybe Strings instead of 'SqlValue's. -} sExecute :: Statement -> [Maybe String] -> IO Integer sExecute sth lst = execute sth (map toSql lst) {- | Like 'executeMany', but take a list of Maybe Strings instead of 'SqlValue's. -} sExecuteMany :: Statement -> [[Maybe String]] -> IO () sExecuteMany sth lst = executeMany sth (map (map toSql) lst) {- | Like 'fetchRow', but return a list of Maybe Strings instead of 'SqlValue's. -} sFetchRow :: Statement -> IO (Maybe [Maybe String]) sFetchRow sth = do res <- fetchRow sth case res of Nothing -> return Nothing Just x -> return $ Just $ map fromSql x {- | Execute some code. If any uncaught exception occurs, run 'rollback' and re-raise it. Otherwise, run 'commit' and return. This function, therefore, encapsulates the logical property that a transaction is all about: all or nothing. The 'IConnection' object passed in is passed directly to the specified function as a convenience. This function traps /all/ uncaught exceptions, not just SqlErrors. Therefore, you will get a rollback for any exception that you don't handle. That's probably what you want anyway. Since all operations in HDBC are done in a transaction, this function doesn't issue an explicit \"begin\" to the server. You should ideally have called 'Database.HDBC.commit' or 'Database.HDBC.rollback' before calling this function. If you haven't, this function will commit or rollback more than just the changes made in the included action. If there was an error while running 'rollback', this error will not be reported since the original exception will be propogated back. (You'd probably like to know about the root cause for all of this anyway.) Feedback on this behavior is solicited. -} withTransaction :: IConnection conn => conn -> (conn -> IO a) -> IO a withTransaction conn func = #if __GLASGOW_HASKELL__ >= 610 do r <- onException (func conn) doRollback commit conn return r where doRollback = -- Discard any exception from (rollback conn) so original -- exception can be re-raised Control.Exception.catch (rollback conn) doRollbackHandler doRollbackHandler :: SomeException -> IO () doRollbackHandler _ = return () #else do r <- try (func conn) case r of Right x -> do commit conn return x Left e -> do try (rollback conn) -- Discard any exception here throw e #endif {- | Lazily fetch all rows from an executed 'Statement'. You can think of this as hGetContents applied to a database result set. The result of this is a lazy list, and each new row will be read, lazily, from the database as the list is processed. When you have exhausted the list, the 'Statement' will be 'finish'ed. Please note that the careless use of this function can lead to some unpleasant behavior. In particular, if you have not consumed the entire list, then attempt to 'finish' or re-execute the statement, and then attempt to consume more elements from the list, the result will almost certainly not be what you want. But then, similar caveats apply with hGetContents. Bottom line: this is a very convenient abstraction; use it wisely. Use 'fetchAllRows'' if you need something that is strict, without all these caveats. -} fetchAllRows :: Statement -> IO [[SqlValue]] fetchAllRows sth = unsafeInterleaveIO $ do row <- fetchRow sth case row of Nothing -> return [] Just x -> do remainder <- fetchAllRows sth return (x : remainder) evalAll :: [[a]] -> IO Integer evalAll inp = do r1 <- mapM (evaluate . genericLength) inp evaluate (sum r1) {- | Strict version of 'fetchAllRows'. Does not have the side-effects of 'fetchAllRows', but forces the entire result set to be buffered in memory. -} fetchAllRows' :: Statement -> IO [[SqlValue]] fetchAllRows' sth = do res <- fetchAllRows sth _ <- evalAll res return res {- | Like 'fetchAllRows', but return Maybe Strings instead of 'SqlValue's. -} sFetchAllRows :: Statement -> IO [[Maybe String]] sFetchAllRows sth = do res <- fetchAllRows sth return $ map (map fromSql) res {- | Strict version of 'sFetchAllRows'. -} sFetchAllRows' :: Statement -> IO [[Maybe String]] sFetchAllRows' sth = do res <- sFetchAllRows sth _ <- evalAll res return res {- | Like 'fetchRow', but instead of returning a list, return an association list from column name to value. The keys of the column names are lowercase versions of the data returned by 'getColumnNames'. Please heed the warnings there. Additionally, results are undefined if multiple columns are returned with identical names. -} fetchRowAL :: Statement -> IO (Maybe [(String, SqlValue)]) fetchRowAL sth = do row <- fetchRow sth case row of Nothing -> return Nothing Just r -> do names <- getColumnNames sth return $ Just $ zip names r {- | Strict version of 'fetchRowAL'. -} fetchRowAL' :: Statement -> IO (Maybe [(String, SqlValue)]) fetchRowAL' sth = do res <- fetchRowAL sth _ <- case res of Nothing -> return 0 Just x -> evaluate ((genericLength x)::Integer) return res {- | Similar to 'fetchRowAL', but return a Map instead of an association list. -} fetchRowMap :: Statement -> IO (Maybe (Map.Map String SqlValue)) fetchRowMap sth = do r <- fetchRowAL sth case r of Nothing -> return Nothing Just x -> return $ Just $ Map.fromList x {- | Strict version of 'fetchRowMap'. -} fetchRowMap' :: Statement -> IO (Maybe (Map.Map String SqlValue)) fetchRowMap' sth = do res <- fetchRowMap sth _ <- case res of Nothing -> return 0 Just x -> evaluate ((genericLength (Map.toList x))::Integer) return res {- | Like 'fetchAllRows', but instead of returning a list for each row, return an association list for each row, from column name to value. See 'fetchRowAL' for more details. -} fetchAllRowsAL :: Statement -> IO [[(String, SqlValue)]] fetchAllRowsAL sth = do names <- getColumnNames sth rows <- fetchAllRows sth return $ map (zip names) rows {- | Strict version of 'fetchAllRowsAL' -} fetchAllRowsAL' :: Statement -> IO [[(String, SqlValue)]] fetchAllRowsAL' sth = do res <- fetchAllRowsAL sth _ <- evalAll res return res {- | Like 'fetchAllRowsAL', but return a list of Maps instead of a list of association lists. -} fetchAllRowsMap :: Statement -> IO [Map.Map String SqlValue] fetchAllRowsMap sth = fetchAllRowsAL sth >>= (return . map Map.fromList) {- | Strict version of 'fetchAllRowsMap' -} fetchAllRowsMap' :: Statement -> IO [Map.Map String SqlValue] fetchAllRowsMap' sth = do res <- fetchAllRowsMap sth _ <- evaluate ((genericLength res)::Integer) return res {- | A quick way to do a query. Similar to preparing, executing, and then calling 'fetchAllRows' on a statement. See also 'quickQuery''. -} quickQuery :: IConnection conn => conn -> String -> [SqlValue] -> IO [[SqlValue]] quickQuery conn qrystr args = do sth <- prepare conn qrystr _ <- execute sth args fetchAllRows sth {- | Strict version of 'quickQuery'. -} quickQuery' :: IConnection conn => conn -> String -> [SqlValue] -> IO [[SqlValue]] quickQuery' conn qrystr args = do res <- quickQuery conn qrystr args _ <- evalAll res return res {- | A utility function to throw a 'SqlError'. The mechanics of throwing such a thing differ between GHC 6.8.x, Hugs, and GHC 6.10. This function takes care of the special cases to make it simpler. With GHC 6.10, it is a type-restricted alias for throw. On all other systems, it is a type-restricted alias for throwDyn. -} throwSqlError :: SqlError -> IO a #if __GLASGOW_HASKELL__ >= 610 throwSqlError = throw #else throwSqlError = throwDyn #endif HDBC-2.4.0.2/testsrc/0000755000000000000000000000000013146574642012246 5ustar0000000000000000HDBC-2.4.0.2/testsrc/TestInfrastructure.hs0000644000000000000000000000063213146574642016463 0ustar0000000000000000{- Copyright (C) 2009 John Goerzen All rights reserved. For license and copyright information, see the file COPYRIGHT -} module TestInfrastructure where import qualified Test.QuickCheck as QC import qualified Test.HUnit as HU import Test.HUnit.Tools q :: QC.Testable a => String -> a -> HU.Test q = qc2hu 250 qverbose :: QC.Testable a => String -> a -> HU.Test qverbose = qc2hu 250 HDBC-2.4.0.2/testsrc/TestSqlValue.hs0000644000000000000000000000306713146574642015204 0ustar0000000000000000{- Copyright (C) 2009 John Goerzen All rights reserved. For license and copyright information, see the file COPYRIGHT -} module TestSqlValue where import TestInfrastructure import Test.QuickCheck hiding (Result) import Test.QuickCheck.Property (Result) import Test.QuickCheck.Tools import qualified Test.HUnit as HU import Database.HDBC import Data.Time.Format import Data.Time.LocalTime import Database.HDBC.Locale (defaultTimeLocale, iso8601DateFormat) import Data.Maybe instance Eq ZonedTime where a == b = zonedTimeToUTC a == zonedTimeToUTC b && zonedTimeZone a == zonedTimeZone b toSql_Int :: Int -> Result toSql_Int x = toSql x @?= SqlInt64 (fromIntegral x) fromSql_Int :: Int -> Result fromSql_Int x = Right x @=? safeFromSql (SqlInt64 (fromIntegral x)) testZonedTimeStr = "1989-08-01 15:33:01 -0500" testZonedTime :: ZonedTime testZonedTime = fromJust $ parseTime defaultTimeLocale (iso8601DateFormat (Just "%T %z")) testZonedTimeStr testZonedTimeFracStr = "1989-08-01 15:33:01.536 -0500" testZonedTimeFrac :: ZonedTime testZonedTimeFrac = fromJust $ parseTime defaultTimeLocale (iso8601DateFormat (Just "%T%Q %z")) testZonedTimeFracStr ztparsenf = (HU.@=?) testZonedTime (fromSql (SqlString testZonedTimeStr)) ztparsef = (HU.@=?) testZonedTimeFrac (fromSql (SqlString testZonedTimeFracStr)) hut msg = HU.TestLabel msg . HU.TestCase allt = [q "toSql Int" toSql_Int, q "safeFromSql Int" fromSql_Int, hut "non-frac parse" ztparsenf, hut "frac parse" ztparsef] HDBC-2.4.0.2/testsrc/runtests.hs0000644000000000000000000000047713146574642014501 0ustar0000000000000000module Main where import qualified Test.HUnit as HU import Test.HUnit.Tools import qualified TestSqlValue test1 = HU.TestCase ((HU.@=?) "x" "x") alltests = [HU.TestLabel "test1" test1, tl "TestSqlValue" TestSqlValue.allt ] main = do runVerboseTests (HU.TestList alltests) return ()