Portability | portable |
---|---|
Stability | experimental |
Maintainer | Aleksey Uymanov <s9gf4ult@gmail.com> |
Safe Haskell | None |
- class ToSql a where
- class FromSql a where
- safeFromSql :: SqlValue -> Either ConvertError a
- fromSql :: SqlValue -> a
- data ConvertError
- = ConvertError { }
- | IncompatibleTypes {
- ceFromType :: String
- ceToType :: String
- newtype BitField = BitField {
- unBitField :: Word64
- data SqlValue
Documentation
All types must convert to SqlValue safely and unambiguously. That's why there is no ''safeToSql'' method
safeFromSql :: SqlValue -> Either ConvertError aSource
fromSql :: SqlValue -> aSource
Unsafe method, throws ConvertError
if convertion failed. Has default
implementation.
data ConvertError Source
Convertion error description. Used in FromSql
typeclass.
ConvertError | |
IncompatibleTypes | Type names must unique. Expecting names are generated by ( |
|
Auxiliary type to represent bit field outside of SqlValue
SQL value marshalling
SqlValue
is the main type for expressing Haskell values to SQL databases.
WHAT IS SQLVALUE
SqlValue is an intermediate type to storerecevie data tofrom the database. Every database driver will do it's best to properly convert any SqlValue to the database record's field, and properly convert the record's field to SqlValue back.
The SqlValue
has predefined FromSql
and ToSql
instances for many Haskell's
types. Any Haskell's type can be converted to the SqlValue
with toSql
function. There is no safeToSql function because toSql
never fails. Also, any
SqlValue
type can be converted to almost any Haskell's type as well. Not any
SqlValue
can be converted back to Haskell's type, so there is safeFromSql
function to do that safely. There is also unsafe toSql
function of caurse.
You can sure, that fromSql . toSql == id
SQLVALUE CONSTRUCTORS
SqlValue
constructors is the MINIMAL set of constructors, required to
represent the most wide range of native database types.
For example, there is FLOAT native database type and DOUBLE, but any DOUBLE can
carry any FLOAT value, so there is no need to create SqlValue
constructor to
represent FLOAT type, we can do it with Double. But there is DECIMAL database
type, representing arbitrary precision value which can be carried just by
Decimal
Haskell's type, so we need a constructor for it.
There is no SqlRational any more, because there is no one database which have
native Rational type. This is the key idea: if database can not store this type
natively we will not create SqlValue
clause for it.
Each SqlValue
constructor is documented or self-explaining to understand what
it is needed for.
'ToSql' and 'FromSql' INSTANCES
The key idea is to do the most obvious conversion between types only if it is
not ambiguous. For example, the most obvious conversion of Double
to Int32
is just truncate the Double
, the most obvious conversion of String to
UTCTime
is to try read the String
as date and time. But there is no obvious
way to convert Int32
to UTCTime
, so if you will try to convert (SqlInteger
44) to date you will fail. User must handle this cases properly converting
values with right way. It is not very good idea to silently perform strange and
ambiguous convertions between absolutely different data types.
ERROR CONDITIONS
There may be sometimes an error during conversion. For instance, if you have an
SqlText
and 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
using fromSql
, or a Left result using safeFromSql
.
STORING SQLVALUE TO DATABASE
Any SqlValue
can be converted to Text
and then readed from Text
back. This
is guaranteed by tests, so the database driver's author can use it to store and
read data through Text
for types which is not supported by the database
natively.
TEXT AND BYTESTRINGS
We are using lazy Text everywhere because it is faster than String
and has
builders. Strict text can be converted to one-chanked lazy text with O(1)
complexity, but lazy to strict converts with O(n) complexity, so it is logical
to use lazy Text.
We are not using ByteString as text encoded in UTF-8, ByteStrings are just
sequences of bytes. We are using strict ByteStrings because HDBI drivers uses
them to pass the ByteString to the C library as CString
, so it must be strict.
We are not using String
as data of query or as query itself because it is not
effective in memory and cpu.
DATE AND TIME
We are not using time with timezone, because there is no one database working with it natively except PostgreSQL, but the documentations of PostgreSQL says
To address these difficulties, we recommend using datetime types that contain both date and time when using time zones. We do not recommend using the type time with time zone (though it is supported by PostgreSQL for legacy applications and for compliance with the SQL standard). PostgreSQL assumes your local time zone for any type containing only date or time./
This is not recomended to use time with timezone.
We are using UTCTime
instead of TimeWithTimezone
because no one database
actually save timezone information. All databases just convert datetime to
UTCTime
when save data and convert UTCTime back to LOCAL SERVER TIMEZONE when
returning the data. So it is logical to work with timezones on the haskell side.
Time intervals are not widely supported, actually just in PostgreSQL and
Oracle. So, if you need them you can serialize throgh SqlText
by hands, or
write your own ToSql
and FromSql
instances to do that more convenient.
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.
SqlDecimal Decimal | Arbitrary precision DECIMAL value |
SqlInteger Integer | Any Integer, including Int32, Int64 and Words. |
SqlDouble Double | |
SqlText Text | |
SqlBlob ByteString | Blob field in the database. This field can not be implicitly converted to any other type because it is just an array of bytes, not an UTF-8 encoded string. |
SqlBool Bool | |
SqlBitField BitField | Represent bit field with 64 bits |
SqlUUID UUID | UUID value http:en.wikipedia.orgwikiUUID |
SqlUTCTime UTCTime | UTC YYYY-MM-DD HH:MM:SS |
SqlLocalDate Day | Local YYYY-MM-DD (no timezone) |
SqlLocalTimeOfDay TimeOfDay | Local HH:MM:SS (no timezone) |
SqlLocalTime LocalTime | Local YYYY-MM-DD HH:MM:SS (no timezone) |
SqlNull | NULL in SQL or Nothing in Haskell |