on master help sign in
fix CSV import/export encoding (now universally UTF-8)
dc3ee07AgentM
src/lib/ProjectM36/Atom.hs+1−1
@@ -15,7 +15,7 @@ atomToText :: Atom -> T.Text
15atomToText (IntegerAtom i) = (T.pack . show) i
15atomToText (IntegerAtom i) = (T.pack . show) i
16atomToText (IntAtom i) = (T.pack . show) i
16atomToText (IntAtom i) = (T.pack . show) i
17atomToText (DoubleAtom i) = (T.pack . show) i
17atomToText (DoubleAtom i) = (T.pack . show) i
18−atomToText (TextAtom i) = i --does this break quoting in CSV export?
18+atomToText (TextAtom i) = (T.pack . show) i --quotes necessary for ConstructedAtom subatoms
19atomToText (DayAtom i) = (T.pack . show) i
19atomToText (DayAtom i) = (T.pack . show) i
20atomToText (DateTimeAtom i) = (T.pack . show) i
20atomToText (DateTimeAtom i) = (T.pack . show) i
21atomToText (ByteStringAtom i) = (T.pack . show) i
21atomToText (ByteStringAtom i) = (T.pack . show) i
src/lib/ProjectM36/Persist.hs+3−2
@@ -6,7 +6,6 @@ module ProjectM36.Persist (writeFileSync,
6 printFdCount,
6 printFdCount,
7 DiskSync(..)) where
7 DiskSync(..)) where
8-- on Windows, use FlushFileBuffers and MoveFileEx
8-- on Windows, use FlushFileBuffers and MoveFileEx
9−import qualified Data.Text.IO as TIO
10import qualified Data.Text as T
9import qualified Data.Text as T
11
10
12#if defined(linux_HOST_OS)
11#if defined(linux_HOST_OS)
@@ -38,6 +37,8 @@ import Foreign.C
38
37
39import System.IO (withFile, IOMode(WriteMode), Handle)
38import System.IO (withFile, IOMode(WriteMode), Handle)
40import qualified Data.ByteString.Lazy as BS
39import qualified Data.ByteString.Lazy as BS
40+import qualified Data.ByteString as BS'
41+import qualified Data.Text.Encoding as TE
41
42
42#if defined(mingw32_HOST_OS)
43#if defined(mingw32_HOST_OS)
43import ProjectM36.Win32Handle
44import ProjectM36.Win32Handle
@@ -52,7 +53,7 @@ writeFileSync :: DiskSync -> FilePath -> T.Text -> IO()
52writeFileSync sync path strOut = withFile path WriteMode handler
53writeFileSync sync path strOut = withFile path WriteMode handler
53 where
54 where
54 handler handle = do
55 handler handle = do
55− TIO.hPutStr handle strOut
56+ BS'.hPut handle (TE.encodeUtf8 strOut)
56 syncHandle sync handle
57 syncHandle sync handle
57
58
58renameSync :: DiskSync -> FilePath -> FilePath -> IO ()
59renameSync :: DiskSync -> FilePath -> FilePath -> IO ()
src/lib/ProjectM36/Relation/Show/CSV.hs+1−1
@@ -41,7 +41,7 @@ instance DefaultOrdered RecordRelationTuple where
41newtype RecordAtom = RecordAtom {unAtom :: Atom}
41newtype RecordAtom = RecordAtom {unAtom :: Atom}
42
42
43instance ToField RecordAtom where
43instance ToField RecordAtom where
44− toField (RecordAtom (TextAtom atomVal)) = TE.encodeUtf8 atomVal --squelch extraneous quotes for text type- the CSV library will add them if necessary
44+ toField (RecordAtom (TextAtom atomVal)) = TE.encodeUtf8 atomVal --without this, CSV text atoms are doubly quoted
45 toField (RecordAtom atomVal) = (TE.encodeUtf8 . atomToText) atomVal
45 toField (RecordAtom atomVal) = (TE.encodeUtf8 . atomToText) atomVal
46
46
47
47
src/lib/ProjectM36/TransactionGraph/Persist.hs+14−3
@@ -21,7 +21,8 @@ import Data.Either (isRight)
21import Data.Maybe (fromMaybe)
21import Data.Maybe (fromMaybe)
22import qualified Data.List as L
22import qualified Data.List as L
23import Control.Exception.Base
23import Control.Exception.Base
24−import qualified Data.Text.IO as TIO
24+import qualified Data.ByteString as BS
25+import qualified Data.Text.Encoding as TE
25import Data.ByteString (ByteString)
26import Data.ByteString (ByteString)
26import Data.Monoid
27import Data.Monoid
27import qualified Crypto.Hash.SHA256 as SHA256
28import qualified Crypto.Hash.SHA256 as SHA256
@@ -200,7 +201,7 @@ writeGraphTransactionIdFile sync destDirectory (TransactionGraph _ transSet) = w
200
201
201readGraphTransactionIdFileDigest :: FilePath -> IO LockFileHash
202readGraphTransactionIdFileDigest :: FilePath -> IO LockFileHash
202readGraphTransactionIdFileDigest dbdir = do
203readGraphTransactionIdFileDigest dbdir = do
203− let graphTransactionIdData = TIO.readFile (transactionLogPath dbdir)
204+ let graphTransactionIdData = readUTF8FileOrError (transactionLogPath dbdir)
204 SHA256.hash . encodeUtf8 <$> graphTransactionIdData
205 SHA256.hash . encodeUtf8 <$> graphTransactionIdData
205
206
206readGraphTransactionIdFile :: FilePath -> IO (Either PersistenceError [(TransactionId, UTCTime, [TransactionId])])
207readGraphTransactionIdFile :: FilePath -> IO (Either PersistenceError [(TransactionId, UTCTime, [TransactionId])])
@@ -210,5 +211,15 @@ readGraphTransactionIdFile dbdir = do
210 (readUUID tid, readEpoch epochText, map readUUID parentIds)
211 (readUUID tid, readEpoch epochText, map readUUID parentIds)
211 readUUID uuidText = fromMaybe (error "failed to read uuid") (U.fromText uuidText)
212 readUUID uuidText = fromMaybe (error "failed to read uuid") (U.fromText uuidText)
212 readEpoch t = posixSecondsToUTCTime (realToFrac (either (error "failed to read epoch") fst (double t)))
213 readEpoch t = posixSecondsToUTCTime (realToFrac (either (error "failed to read epoch") fst (double t)))
213− Right . map grapher . T.lines <$> TIO.readFile (transactionLogPath dbdir)
214+ Right . map grapher . T.lines <$> readUTF8FileOrError (transactionLogPath dbdir)
214
215
216+--rationale- reading essential database files must fail hard
217+readUTF8FileOrError :: FilePath -> IO (T.Text)
218+readUTF8FileOrError pathIn = do
219+ eFileBytes <- try (BS.readFile pathIn) :: IO (Either IOError BS.ByteString)
220+ case eFileBytes of
221+ Left err -> error (show err)
222+ Right fileBytes ->
223+ case TE.decodeUtf8' fileBytes of
224+ Left err -> error (show err)
225+ Right utf8Bytes -> pure utf8Bytes
test/Relation/Export/CSV.hs+1−1
@@ -85,7 +85,7 @@ testCSVExport = TestCase $ do
85 Right rel ->
85 Right rel ->
86 case relationAsCSV rel of
86 case relationAsCSV rel of
87 Left err -> assertFailure $ "export failed: " ++ show err
87 Left err -> assertFailure $ "export failed: " ++ show err
88− Right csvData ->
88+ Right csvData ->
89 --BS.writeFile "/tmp/csv" csvData
89 --BS.writeFile "/tmp/csv" csvData
90 --putStrLn (TL.unpack (TE.decodeUtf8 csvData))
90 --putStrLn (TL.unpack (TE.decodeUtf8 csvData))
91 case csvAsRelation attrs basicTypeConstructorMapping csvData of -- import csv data back to relation
91 case csvAsRelation attrs basicTypeConstructorMapping csvData of -- import csv data back to relation