Skip to content

Commit 6b3c42d

Browse files
committed
Add proper support for Word64
Previously when the schema used `Word64` as the column type, Persistent would use `SqlInt64` as the SQL representation which means that `Word64` values above `maxBound :: Int64` would be stored as negative values in the database. That is fine for a database only accessed from Haskell but is a pain in the neck when the database is used as an interop layer for other languages. This commit fixes these issues by adding `SqlWord64` and `PersistWord64`. Closes: #1095
1 parent 424ad12 commit 6b3c42d

6 files changed

Lines changed: 21 additions & 3 deletions

File tree

persistent-postgresql/Database/Persist/Postgresql.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ instance PGTF.ToField P where
428428
toField (P (PersistText t)) = PGTF.toField t
429429
toField (P (PersistByteString bs)) = PGTF.toField (PG.Binary bs)
430430
toField (P (PersistInt64 i)) = PGTF.toField i
431+
toField (P (PersistWord64 i)) = PGTF.toField i
431432
toField (P (PersistDouble d)) = PGTF.toField d
432433
toField (P (PersistRational r)) = PGTF.Plain $
433434
BBB.fromString $
@@ -1110,6 +1111,7 @@ showSqlType :: SqlType -> Text
11101111
showSqlType SqlString = "VARCHAR"
11111112
showSqlType SqlInt32 = "INT4"
11121113
showSqlType SqlInt64 = "INT8"
1114+
showSqlType SqlWord64 = "NUMERIC(20,0)" -- length (show (maxBound :: Word64)) == 20
11131115
showSqlType SqlReal = "DOUBLE PRECISION"
11141116
showSqlType (SqlNumeric s prec) = T.concat [ "NUMERIC(", T.pack (show s), ",", T.pack (show prec), ")" ]
11151117
showSqlType SqlDay = "DATE"

persistent/Database/Persist/Class/PersistField.hs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import Data.Int (Int8, Int16, Int32, Int64)
1919
import qualified Data.IntMap as IM
2020
import qualified Data.Map as M
2121
import Data.Monoid ((<>))
22+
import Data.Ratio (denominator, numerator)
2223
import qualified Data.Set as S
2324
import Data.Text (Text)
2425
import qualified Data.Text as T
@@ -101,6 +102,7 @@ instance {-# OVERLAPPING #-} PersistField [Char] where
101102
fromPersistValue (PersistByteString bs) =
102103
Right $ T.unpack $ TE.decodeUtf8With TERR.lenientDecode bs
103104
fromPersistValue (PersistInt64 i) = Right $ Prelude.show i
105+
fromPersistValue (PersistWord64 i) = Right $ Prelude.show i
104106
fromPersistValue (PersistDouble d) = Right $ Prelude.show d
105107
fromPersistValue (PersistRational r) = Right $ Prelude.show r
106108
fromPersistValue (PersistDay d) = Right $ Prelude.show d
@@ -226,8 +228,12 @@ instance PersistField Word32 where
226228
fromPersistValue x = Left $ fromPersistValueError "Word32" "integer" x
227229

228230
instance PersistField Word64 where
229-
toPersistValue = PersistInt64 . fromIntegral
231+
toPersistValue = PersistWord64 . fromIntegral
232+
fromPersistValue (PersistWord64 w) = Right $ fromIntegral w
230233
fromPersistValue (PersistInt64 i) = Right $ fromIntegral i
234+
fromPersistValue x@(PersistRational r) = if denominator r == 1
235+
then Right $ fromIntegral (numerator r)
236+
else Left $ fromPersistValueError "Word64" "rational" x
231237
fromPersistValue x = Left $ fromPersistValueError "Word64" "integer" x
232238

233239
instance PersistField Double where

persistent/Database/Persist/Sql/Class.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1203,7 +1203,7 @@ instance PersistFieldSql Word16 where
12031203
instance PersistFieldSql Word32 where
12041204
sqlType _ = SqlInt64
12051205
instance PersistFieldSql Word64 where
1206-
sqlType _ = SqlInt64
1206+
sqlType _ = SqlWord64
12071207
instance PersistFieldSql Double where
12081208
sqlType _ = SqlReal
12091209
instance PersistFieldSql Bool where

persistent/Database/Persist/Sql/Orphan/PersistQuery.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ instance PersistQueryRead SqlBackend where
4646
mm <- CL.head
4747
case mm of
4848
Just [PersistInt64 i] -> return $ fromIntegral i
49+
Just [PersistWord64 i] -> return $ fromIntegral i
4950
Just [PersistDouble i] ->return $ fromIntegral (truncate i :: Int64) -- gb oracle
5051
Just [PersistByteString i] -> case readInteger i of -- gb mssql
5152
Just (ret,"") -> return $ fromIntegral ret
@@ -116,6 +117,7 @@ instance PersistQueryRead SqlBackend where
116117
Nothing ->
117118
case xs of
118119
[PersistInt64 x] -> return [PersistInt64 x]
120+
[PersistWord64 x] -> return [PersistWord64 x]
119121
[PersistDouble x] -> return [PersistInt64 (truncate x)] -- oracle returns Double
120122
_ -> return xs
121123
Just pdef ->

persistent/Database/Persist/Sql/Orphan/PersistStore.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ instance PersistStoreWrite SqlBackend where
155155
Just [PersistInt64 i] -> case keyFromValues [PersistInt64 i] of
156156
Left err -> error $ "SQL insert: keyFromValues: PersistInt64 " `mappend` show i `mappend` " " `mappend` unpack err
157157
Right k -> return k
158+
Just [PersistWord64 i] -> case keyFromValues [PersistWord64 i] of
159+
Left err -> error $ "SQL insert: keyFromValues: PersistWord64 " `mappend` show i `mappend` " " `mappend` unpack err
160+
Right k -> return k
158161
Nothing -> error $ "SQL insert did not return a result giving the generated ID"
159162
Just vals' -> case keyFromValues vals' of
160163
Left e -> error $ "Invalid result from a SQL insert, got: " ++ show vals' ++ ". Error was: " ++ unpack e

persistent/Database/Persist/Types/Base.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import Data.Text.Encoding.Error (lenientDecode)
2222
import Data.Time (Day, TimeOfDay, UTCTime)
2323
import Data.Typeable (Typeable)
2424
import qualified Data.Vector as V
25-
import Data.Word (Word32)
25+
import Data.Word (Word32, Word64)
2626
import Numeric (showHex, readHex)
2727
import Web.PathPieces (PathPiece(..))
2828
import Web.HttpApiData (ToHttpApiData (..), FromHttpApiData (..), parseUrlPieceMaybe, showTextData, readTextData, parseBoundedTextData)
@@ -368,6 +368,7 @@ instance Error PersistException where
368368
data PersistValue = PersistText Text
369369
| PersistByteString ByteString
370370
| PersistInt64 Int64
371+
| PersistWord64 Word64 -- @since 2.11.0
371372
| PersistDouble Double
372373
| PersistRational Rational
373374
| PersistBool Bool
@@ -417,6 +418,7 @@ instance ToHttpApiData PersistValue where
417418
instance FromHttpApiData PersistValue where
418419
parseUrlPiece input =
419420
PersistInt64 <$> parseUrlPiece input
421+
<!> PersistWord64 <$> parseUrlPiece input
420422
<!> PersistList <$> readTextData input
421423
<!> PersistText <$> return input
422424
where
@@ -433,6 +435,7 @@ fromPersistValueText (PersistText s) = Right s
433435
fromPersistValueText (PersistByteString bs) =
434436
Right $ TE.decodeUtf8With lenientDecode bs
435437
fromPersistValueText (PersistInt64 i) = Right $ T.pack $ show i
438+
fromPersistValueText (PersistWord64 w) = Right $ T.pack $ show w
436439
fromPersistValueText (PersistDouble d) = Right $ T.pack $ show d
437440
fromPersistValueText (PersistRational r) = Right $ T.pack $ show r
438441
fromPersistValueText (PersistDay d) = Right $ T.pack $ show d
@@ -450,6 +453,7 @@ instance A.ToJSON PersistValue where
450453
toJSON (PersistText t) = A.String $ T.cons 's' t
451454
toJSON (PersistByteString b) = A.String $ T.cons 'b' $ TE.decodeUtf8 $ B64.encode b
452455
toJSON (PersistInt64 i) = A.Number $ fromIntegral i
456+
toJSON (PersistWord64 w) = A.Number $ fromIntegral w
453457
toJSON (PersistDouble d) = A.Number $ Data.Scientific.fromFloatDigits d
454458
toJSON (PersistRational r) = A.String $ T.pack $ 'r' : show r
455459
toJSON (PersistBool b) = A.Bool b
@@ -534,6 +538,7 @@ data SqlType = SqlString
534538
| SqlTime
535539
| SqlDayTime -- ^ Always uses UTC timezone
536540
| SqlBlob
541+
| SqlWord64 -- @since 2.11.0
537542
| SqlOther T.Text -- ^ a backend-specific name
538543
deriving (Show, Read, Eq, Typeable, Ord)
539544

0 commit comments

Comments
 (0)