Skip to content

Commit f56f1a2

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 f56f1a2

6 files changed

Lines changed: 17 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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ instance {-# OVERLAPPING #-} PersistField [Char] where
101101
fromPersistValue (PersistByteString bs) =
102102
Right $ T.unpack $ TE.decodeUtf8With TERR.lenientDecode bs
103103
fromPersistValue (PersistInt64 i) = Right $ Prelude.show i
104+
fromPersistValue (PersistWord64 i) = Right $ Prelude.show i
104105
fromPersistValue (PersistDouble d) = Right $ Prelude.show d
105106
fromPersistValue (PersistRational r) = Right $ Prelude.show r
106107
fromPersistValue (PersistDay d) = Right $ Prelude.show d
@@ -226,7 +227,8 @@ instance PersistField Word32 where
226227
fromPersistValue x = Left $ fromPersistValueError "Word32" "integer" x
227228

228229
instance PersistField Word64 where
229-
toPersistValue = PersistInt64 . fromIntegral
230+
toPersistValue = PersistWord64 . fromIntegral
231+
fromPersistValue (PersistWord64 w) = Right $ fromIntegral w
230232
fromPersistValue (PersistInt64 i) = Right $ fromIntegral i
231233
fromPersistValue x = Left $ fromPersistValueError "Word64" "integer" x
232234

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)