influxdb 1.8.0 → 1.9.0
raw patch · 8 files changed
+137/−51 lines, 8 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
- Database.InfluxDB: newtype Tagged (s :: k) b
- Database.InfluxDB: parseResults :: QueryResults a => Precision 'QueryRequest -> Value -> Parser (Vector a)
- Database.InfluxDB.Format: instance (a GHC.Types.~ r) => Data.String.IsString (Database.InfluxDB.Format.Format a r)
- Database.InfluxDB.Query: newtype Tagged (s :: k) b
- Database.InfluxDB.Query: parseResults :: QueryResults a => Precision 'QueryRequest -> Value -> Parser (Vector a)
+ Database.InfluxDB: coerceDecoder :: QueryResults a => proxy a -> Maybe Decoder
+ Database.InfluxDB: data Empty
+ Database.InfluxDB: data Ignored
+ Database.InfluxDB: newtype Tagged (s :: k) b :: forall k. () => k -> Type -> Type
+ Database.InfluxDB.Format: instance (a Data.Type.Equality.~ r) => Data.String.IsString (Database.InfluxDB.Format.Format a r)
+ Database.InfluxDB.Query: coerceDecoder :: QueryResults a => proxy a -> Maybe Decoder
+ Database.InfluxDB.Query: data Empty
+ Database.InfluxDB.Query: data Ignored
+ Database.InfluxDB.Query: instance Database.InfluxDB.Query.QueryResults Database.InfluxDB.Query.Empty
+ Database.InfluxDB.Query: instance Database.InfluxDB.Query.QueryResults Database.InfluxDB.Query.Ignored
+ Database.InfluxDB.Query: instance GHC.Show.Show Database.InfluxDB.Query.Empty
+ Database.InfluxDB.Query: instance GHC.Show.Show Database.InfluxDB.Query.Ignored
+ Database.InfluxDB.Query: newtype Tagged (s :: k) b :: forall k. () => k -> Type -> Type
- Database.InfluxDB: Tagged :: b -> Tagged (s :: k) b
+ Database.InfluxDB: Tagged :: b -> Tagged b
- Database.InfluxDB: [FieldNull] :: Field 'Nullable
+ Database.InfluxDB: [FieldNull] :: Field 'Nullable
- Database.InfluxDB: [RFC3339] :: Precision 'QueryRequest
+ Database.InfluxDB: [RFC3339] :: Precision 'QueryRequest
- Database.InfluxDB: [unTagged] :: Tagged (s :: k) b -> b
+ Database.InfluxDB: [unTagged] :: Tagged b -> b
- Database.InfluxDB: parseMeasurement :: QueryResults a => Precision 'QueryRequest -> Maybe Text -> HashMap Text Text -> Vector Text -> Array -> Parser a
+ Database.InfluxDB: parseMeasurement :: QueryResults a => Precision 'QueryRequest -> Maybe Text -> HashMap Text Text -> Vector Text -> Array -> Parser a
- Database.InfluxDB: query :: QueryResults a => QueryParams -> Query -> IO (Vector a)
+ Database.InfluxDB: query :: forall a. QueryResults a => QueryParams -> Query -> IO (Vector a)
- Database.InfluxDB: roundTo :: Timestamp time => Precision 'WriteRequest -> time -> Int64
+ Database.InfluxDB: roundTo :: Timestamp time => Precision 'WriteRequest -> time -> Int64
- Database.InfluxDB: scaleTo :: Timestamp time => Precision 'WriteRequest -> time -> Int64
+ Database.InfluxDB: scaleTo :: Timestamp time => Precision 'WriteRequest -> time -> Int64
- Database.InfluxDB: type LineField = Field 'NonNullable
+ Database.InfluxDB: type LineField = Field 'NonNullable
- Database.InfluxDB: type QueryField = Field 'Nullable
+ Database.InfluxDB: type QueryField = Field 'Nullable
- Database.InfluxDB: untag :: forall k (s :: k) b. Tagged s b -> b
+ Database.InfluxDB: untag :: () => Tagged s b -> b
- Database.InfluxDB.Line: [FieldNull] :: Field 'Nullable
+ Database.InfluxDB.Line: [FieldNull] :: Field 'Nullable
- Database.InfluxDB.Line: [RFC3339] :: Precision 'QueryRequest
+ Database.InfluxDB.Line: [RFC3339] :: Precision 'QueryRequest
- Database.InfluxDB.Line: type LineField = Field 'NonNullable
+ Database.InfluxDB.Line: type LineField = Field 'NonNullable
- Database.InfluxDB.Query: Tagged :: b -> Tagged (s :: k) b
+ Database.InfluxDB.Query: Tagged :: b -> Tagged b
- Database.InfluxDB.Query: [unTagged] :: Tagged (s :: k) b -> b
+ Database.InfluxDB.Query: [unTagged] :: Tagged b -> b
- Database.InfluxDB.Query: parseMeasurement :: QueryResults a => Precision 'QueryRequest -> Maybe Text -> HashMap Text Text -> Vector Text -> Array -> Parser a
+ Database.InfluxDB.Query: parseMeasurement :: QueryResults a => Precision 'QueryRequest -> Maybe Text -> HashMap Text Text -> Vector Text -> Array -> Parser a
- Database.InfluxDB.Query: parseQueryResults :: QueryResults a => Precision 'QueryRequest -> Value -> Parser (Vector a)
+ Database.InfluxDB.Query: parseQueryResults :: forall a. QueryResults a => Precision 'QueryRequest -> Value -> Parser (Vector a)
- Database.InfluxDB.Query: parseQueryResultsWith :: QueryResults a => Decoder -> Precision 'QueryRequest -> Value -> Parser (Vector a)
+ Database.InfluxDB.Query: parseQueryResultsWith :: forall a. QueryResults a => Decoder -> Precision 'QueryRequest -> Value -> Parser (Vector a)
- Database.InfluxDB.Query: query :: QueryResults a => QueryParams -> Query -> IO (Vector a)
+ Database.InfluxDB.Query: query :: forall a. QueryResults a => QueryParams -> Query -> IO (Vector a)
- Database.InfluxDB.Query: untag :: forall k (s :: k) b. Tagged s b -> b
+ Database.InfluxDB.Query: untag :: () => Tagged s b -> b
- Database.InfluxDB.Types: [FieldNull] :: Field 'Nullable
+ Database.InfluxDB.Types: [FieldNull] :: Field 'Nullable
- Database.InfluxDB.Types: [RFC3339] :: Precision 'QueryRequest
+ Database.InfluxDB.Types: [RFC3339] :: Precision 'QueryRequest
- Database.InfluxDB.Types: roundTo :: Timestamp time => Precision 'WriteRequest -> time -> Int64
+ Database.InfluxDB.Types: roundTo :: Timestamp time => Precision 'WriteRequest -> time -> Int64
- Database.InfluxDB.Types: scaleTo :: Timestamp time => Precision 'WriteRequest -> time -> Int64
+ Database.InfluxDB.Types: scaleTo :: Timestamp time => Precision 'WriteRequest -> time -> Int64
- Database.InfluxDB.Types: type LineField = Field 'NonNullable
+ Database.InfluxDB.Types: type LineField = Field 'NonNullable
- Database.InfluxDB.Types: type QueryField = Field 'Nullable
+ Database.InfluxDB.Types: type QueryField = Field 'Nullable
Files
- CHANGELOG.md +8/−0
- LICENSE +1/−1
- influxdb.cabal +2/−4
- src/Database/InfluxDB.hs +13/−8
- src/Database/InfluxDB/Manage.hs +10/−9
- src/Database/InfluxDB/Query.hs +73/−21
- src/Database/InfluxDB/Write.hs +4/−4
- tests/regressions.hs +26/−4
CHANGELOG.md view
@@ -1,5 +1,13 @@ # Revision history for influxdb +## v1.9.0 - 2020-07-18++* Fix `Ignore` and `Empty` to replace the `QueryResults` instance for `Void`. The instance has been deprecated.+* Remove the deprecated `parseResults` method in `QueryResults`.+* Add the `coerceDecoder` method in `QueryResults`.+* Drop support for GHC 8.2 and older because of the use of `EmptyDataDeriving`.+* Update doctest comments with `TypeApplications`.+ ## v1.8.0 - 2020-06-19 This release reworked the `QueryResuls` type class. There are some breaking changes:
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2014-2015, Mitsutoshi Aoe+Copyright (c) 2014-2020, Mitsutoshi Aoe All rights reserved.
influxdb.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.24 name: influxdb-version: 1.8.0+version: 1.9.0 synopsis: Haskell client library for InfluxDB description: @influxdb@ is an InfluxDB client library for Haskell.@@ -15,8 +15,6 @@ category: Database build-type: Custom tested-with:- GHC == 8.0.2- GHC == 8.2.2 GHC == 8.4.4 GHC == 8.6.5 GHC == 8.8.3@@ -75,7 +73,7 @@ ViewPatterns ghc-options: -Wall build-depends:- base >= 4.9 && < 4.15+ base >= 4.11 && < 4.15 , aeson >= 0.7 && < 1.6 , attoparsec < 0.14 , bytestring >= 0.10 && < 0.11
src/Database/InfluxDB.hs view
@@ -52,15 +52,19 @@ , Decoder , lenientDecoder , strictDecoder++ -- ** Helper types and functions+ , Ignored+ , Empty+ , Tagged(..)+ , untag+ , getField , getTag , parseJSON , parseUTCTime , parsePOSIXTime - -- *** Re-exports from tagged- , Tagged(..)- , untag -- * Database management , manage@@ -110,6 +114,7 @@ >>> :set -XOverloadedStrings >>> :set -XRecordWildCards+>>> :set -XTypeApplications >>> import Database.InfluxDB >>> import qualified Database.InfluxDB.Format as F >>> import Control.Lens@@ -146,7 +151,7 @@ >>> let cpuUsage = "cpu_usage" >>> :{ writeBatch wp- [ Line cpuUsage (Map.singleton "cpu" "cpu-total")+ [ Line @UTCTime cpuUsage (Map.singleton "cpu" "cpu-total") (Map.fromList [ ("idle", FieldFloat 10.1) , ("system", FieldFloat 53.3)@@ -154,7 +159,7 @@ ]) (Just $ parseTimeOrError False defaultTimeLocale "%F %T%Q %Z"- "2017-06-17 15:41:40.42659044 UTC") :: Line UTCTime+ "2017-06-17 15:41:40.42659044 UTC") ] :} @@ -168,9 +173,9 @@ If all the field types are an instance of 'Data.Aeson.FromJSON', we can use a tuple to store the results. ->>> :set -XDataKinds -XOverloadedStrings+>>> :set -XDataKinds -XOverloadedStrings -XTypeOperators >>> type CPUUsage = (Tagged "time" UTCTime, Tagged "idle" Double, Tagged "system" Double, Tagged "user" Double)->>> v <- query p $ formatQuery ("SELECT * FROM "%F.measurement) cpuUsage :: IO (V.Vector CPUUsage)+>>> v <- query @CPUUsage p $ formatQuery ("SELECT * FROM "%F.measurement) cpuUsage >>> v [(Tagged 2017-06-17 15:41:40 UTC,Tagged 10.1,Tagged 53.3,Tagged 46.6)] @@ -206,7 +211,7 @@ return CPUUsage {..} :} ->>> query p $ formatQuery ("SELECT * FROM "%F.measurement) cpuUsage :: IO (V.Vector CPUUsage)+>>> query @CPUUsage p $ formatQuery ("SELECT * FROM "%F.measurement) cpuUsage [CPUUsage {time = 2017-06-17 15:41:40 UTC, cpuIdle = 10.1, cpuSystem = 53.3, cpuUser = 46.6}] -}
src/Database/InfluxDB/Manage.hs view
@@ -43,7 +43,6 @@ import Data.Scientific (toBoundedInteger) import Data.Text (Text) import Data.Time.Clock-import Data.Void import qualified Data.Aeson.Types as A import qualified Data.Attoparsec.Combinator as AC import qualified Data.Attoparsec.Text as AT@@ -76,9 +75,11 @@ Left message -> throwIO $ UnexpectedResponse message request body Right val -> do- let parser = parseQueryResultsWith (params^.decoder) (params^.precision)+ let parser = parseQueryResultsWith+ (params ^. decoder)+ (params ^. precision) case A.parse parser val of- A.Success (_ :: V.Vector Void) -> return ()+ A.Success (_ :: V.Vector Empty) -> return () A.Error message -> do let status = HC.responseStatus response when (HT.statusIsServerError status) $@@ -107,7 +108,7 @@ Server {..} = params^.server -- |--- >>> v <- query (queryParams "_internal") "SHOW QUERIES" :: IO (V.Vector ShowQuery)+-- >>> v <- query @ShowQuery (queryParams "_internal") "SHOW QUERIES" data ShowQuery = ShowQuery { showQueryQid :: !Int , showQueryText :: !Query@@ -163,20 +164,20 @@ -- | Query ID ----- >>> v <- query (queryParams "_internal") "SHOW QUERIES" :: IO (V.Vector ShowQuery)+-- >>> v <- query @ShowQuery (queryParams "_internal") "SHOW QUERIES" -- >>> v ^.. each.qid -- ... qid :: Lens' ShowQuery Int -- | Query text ----- >>> v <- query (queryParams "_internal") "SHOW QUERIES" :: IO (V.Vector ShowQuery)+-- >>> v <- query @ShowQuery (queryParams "_internal") "SHOW QUERIES" -- >>> v ^.. each.queryText -- ... queryText :: Lens' ShowQuery Query -- |--- >>> v <- query (queryParams "_internal") "SHOW QUERIES" :: IO (V.Vector ShowQuery)+-- >>> v <- query @ShowQuery (queryParams "_internal") "SHOW QUERIES" -- >>> v ^.. each.database -- ... instance HasDatabase ShowQuery where@@ -184,7 +185,7 @@ -- | Duration of the query ----- >>> v <- query (queryParams "_internal") "SHOW QUERIES" :: IO (V.Vector ShowQuery)+-- >>> v <- query @ShowQuery (queryParams "_internal") "SHOW QUERIES" -- >>> v ^.. each.duration -- ... duration :: Lens' ShowQuery NominalDiffTime@@ -193,7 +194,7 @@ -- | Series name ----- >>> v <- query (queryParams "_internal") "SHOW SERIES" :: IO (V.Vector ShowSeries)+-- >>> v <- query @ShowSeries (queryParams "_internal") "SHOW SERIES" -- >>> length $ v ^.. each.key -- ... key :: Lens' ShowSeries Key
src/Database/InfluxDB/Query.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE EmptyDataDeriving #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-}@@ -36,7 +37,9 @@ -- * Low-level functions , withQueryResponse - -- * Re-exports from tagged+ -- * Helper types+ , Ignored+ , Empty , Tagged(..) , untag ) where@@ -44,6 +47,7 @@ import Control.Monad import Data.Char import Data.List+import Data.Maybe (fromMaybe) import Data.Proxy import GHC.TypeLits @@ -62,8 +66,9 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as BL-import qualified Data.Text.Encoding as TE import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+import qualified Data.Vector as V import qualified Network.HTTP.Client as HC import qualified Network.HTTP.Types as HT @@ -72,8 +77,10 @@ import qualified Database.InfluxDB.Format as F -- $setup+-- >>> :set -XDataKinds -- >>> :set -XOverloadedStrings -- >>> :set -XRecordWildCards+-- >>> :set -XTypeApplications -- >>> import Data.Time (UTCTime) -- >>> import qualified Data.Vector as V @@ -99,14 +106,7 @@ -- return H2OFeet {..} -- :} class QueryResults a where- -- | Parse a JSON object as an array of values of expected type.- parseResults- :: Precision 'QueryRequest- -> Value- -> A.Parser (Vector a)- parseResults = parseQueryResultsWith strictDecoder-- -- | Parse a measurement in a JSON object.+ -- | Parse a single measurement in a JSON object. parseMeasurement :: Precision 'QueryRequest -- ^ Timestamp precision@@ -120,38 +120,88 @@ -- ^ Field values -> A.Parser a -{-# DEPRECATED parseResults- "Use 'parseQueryResults' or 'parseQueryResultsWith' " #-}+ -- | Always use this 'Decoder' when decoding this type.+ --+ -- @'Just' dec@ means 'decoder' in 'QueryParams' will be ignored and be+ -- replaced with the @dec@. 'Nothing' means 'decoder' in 'QueryParams' will+ -- be used.+ coerceDecoder :: proxy a -> Maybe Decoder+ coerceDecoder _ = Nothing -- | Parse a JSON object as an array of values of expected type. parseQueryResults- :: QueryResults a+ :: forall a. QueryResults a => Precision 'QueryRequest -> Value -> A.Parser (Vector a)-parseQueryResults = parseQueryResultsWith strictDecoder+parseQueryResults =+ parseQueryResultsWith $+ fromMaybe strictDecoder (coerceDecoder (Proxy :: Proxy a)) parseQueryResultsWith- :: QueryResults a+ :: forall a. QueryResults a => Decoder -> Precision 'QueryRequest -> Value -> A.Parser (Vector a) parseQueryResultsWith decoder prec =- parseResultsWithDecoder decoder (parseMeasurement prec)+ parseResultsWithDecoder+ (fromMaybe decoder (coerceDecoder (Proxy :: Proxy a)))+ (parseMeasurement prec) -- | 'QueryResults' instance for empty results. Used by -- 'Database.InfluxDB.Manage.manage'.+--+-- NOTE: This instance is deprecated because it's unclear from the name whether+-- it can be used to ignore results. Use 'Empty' when expecting an empty result.+-- Use 'Ignored' if you want to ignore any results. instance QueryResults Void where- parseMeasurement _ _ _ _ _ = parseJSON A.emptyArray+ parseMeasurement _ _ _ _ _ = fail "parseMeasurement for Void"+ coerceDecoder _ = Just $ Decoder $ SomeDecoder+ { decodeEach = id+ , decodeFold = const $ pure V.empty+ } +-- | 'Ignored' can be used in the result type of 'query' when the result values+-- are not needed.+--+-- >>> v <- query @Ignored (queryParams "dummy") "SHOW DATABASES"+-- >>> v+-- []+data Ignored deriving Show++-- | 'QueryResults' instance for ignoring results.+instance QueryResults Ignored where+ parseMeasurement _ _ _ _ _ = fail "parseMeasurement for Ignored"+ coerceDecoder _ = Just $ Decoder $ SomeDecoder+ { decodeEach = id -- doesn't matter+ , decodeFold = const $ pure V.empty -- always succeeds with an empty vector+ }++-- | 'Empty' can be used in the result type of 'query' when the expected results+-- are always empty. Note that if the results are not empty, the decoding+-- process will fail:+--+-- >>> let p = queryParams "empty"+-- >>> Database.InfluxDB.Manage.manage p "CREATE DATABASE empty"+-- >>> v <- query @Empty p "SELECT * FROM empty" -- query an empty series+-- >>> v+-- []+data Empty deriving Show++-- | 'QueryResults' instance for empty results. Used by+-- 'Database.InfluxDB.Manage.manage'.+instance QueryResults Empty where+ parseMeasurement _ _ _ _ _ = fail "parseMeasurement for Empty"+ coerceDecoder _ = Just strictDecoder -- fail when the results are not empty+ fieldName :: KnownSymbol k => proxy k -> T.Text fieldName = T.pack . symbolVal -- | One-off type for non-timestamped measurements -- -- >>> let p = queryParams "_internal"--- >>> dbs <- query p "SHOW DATABASES" :: IO (V.Vector (Tagged "name" T.Text))+-- >>> dbs <- query @(Tagged "name" T.Text) p "SHOW DATABASES" -- >>> find ((== "_internal") . untag) dbs -- Just (Tagged "_internal") instance (KnownSymbol k, FromJSON v) => QueryResults (Tagged k v) where@@ -368,7 +418,7 @@ -- -- If you need a lower-level interface (e.g. to bypass the 'QueryResults' -- constraint etc), see 'withQueryResponse'.-query :: QueryResults a => QueryParams -> Query -> IO (Vector a)+query :: forall a. QueryResults a => QueryParams -> Query -> IO (Vector a) query params q = withQueryResponse params Nothing q go where go request response = do@@ -378,7 +428,9 @@ Left message -> throwIO $ UnexpectedResponse message request body Right val -> do let parser = parseQueryResultsWith- (queryDecoder params)+ (fromMaybe+ (queryDecoder params)+ (coerceDecoder (Proxy :: Proxy a))) (queryPrecision params) case A.parse parser val of A.Success vec -> return vec@@ -441,7 +493,7 @@ chunk' <- HC.responseBody response loop x k' chunk' AB.Done leftover val ->- case A.parse (parseResults (queryPrecision params)) val of+ case A.parse (parseQueryResults (queryPrecision params)) val of A.Success vec -> do x' <- step x vec loop x' k0 leftover
src/Database/InfluxDB/Write.hs view
@@ -46,7 +46,7 @@ import Database.InfluxDB.JSON -- $setup--- >>> :set -XOverloadedStrings -XNoOverloadedLists+-- >>> :set -XOverloadedStrings -XNoOverloadedLists -XTypeApplications -- >>> import qualified Data.Map as Map -- >>> import Data.Time -- >>> import Database.InfluxDB@@ -109,7 +109,7 @@ -- | Write a 'Line'. -- -- >>> let p = writeParams "test-db"--- >>> write p $ Line "room_temp" Map.empty (Map.fromList [("temp", FieldFloat 25.0)]) (Nothing :: Maybe UTCTime)+-- >>> write p $ Line @UTCTime "room_temp" Map.empty (Map.fromList [("temp", FieldFloat 25.0)]) Nothing write :: Timestamp time => WriteParams@@ -125,8 +125,8 @@ -- >>> let p = writeParams "test-db" -- >>> :{ -- writeBatch p--- [ Line "temp" (Map.singleton "city" "tokyo") (Map.fromList [("temp", FieldFloat 25.0)]) (Nothing :: Maybe UTCTime)--- , Line "temp" (Map.singleton "city" "osaka") (Map.fromList [("temp", FieldFloat 25.2)]) (Nothing :: Maybe UTCTime)+-- [ Line @UTCTime "temp" (Map.singleton "city" "tokyo") (Map.fromList [("temp", FieldFloat 25.0)]) Nothing+-- , Line @UTCTime "temp" (Map.singleton "city" "osaka") (Map.fromList [("temp", FieldFloat 25.2)]) Nothing -- ] -- :} writeBatch
tests/regressions.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE TypeApplications #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}@@ -23,14 +24,15 @@ [ testCase "issue #64" case_issue64 , testCase "issue #66" case_issue66 , testCaseSteps "issue #75" case_issue75+ , testCaseSteps "issue #79" case_issue79 ] -- https://github.com/maoe/influxdb-haskell/issues/64 case_issue64 :: Assertion case_issue64 = withDatabase dbName $ do- write wp $ Line "count" Map.empty+ write wp $ Line @UTCTime "count" Map.empty (Map.fromList [("value", FieldInt 1)])- (Nothing :: Maybe UTCTime)+ Nothing r <- try $ query qp "SELECT value FROM count" case r of Left err -> case err of@@ -70,9 +72,9 @@ step "Checking encoded value" let string = [Raw.r|bl\"a|] let encoded = encodeLine (scaleTo Nanosecond)- $ Line "testing" mempty+ $ Line @UTCTime "testing" mempty (M.singleton "test" $ FieldString string)- (Nothing :: Maybe UTCTime)+ Nothing encoded @?= [Raw.r|testing test="bl\\\"a"|] step "Preparing a test database"@@ -84,6 +86,26 @@ step "Checking server response" let wp = writeParams db writeByteString wp encoded++case_issue79 :: (String -> IO ()) -> Assertion+case_issue79 step = withDatabase db $ do+ let w = writeParams db+ let q = queryParams db+ step "Querying an empty series with two fields expected"+ _ <- query @(Tagged "time" UTCTime, Tagged "value" Int) q "SELECT * FROM foo"+ step "Querying an empty series with the results ignored"+ _ <- query @Ignored q "SELECT * FROM foo"+ step "Querying an empty series expecting an empty result"+ _ <- query @Empty q "SELECT * FROM foo"+ step "Writing a data point"+ write w $ Line @UTCTime "foo" mempty (Map.fromList [("value", FieldInt 42)]) Nothing+ step "Querying a non-empty series with two fields expected"+ _ <- query @(Tagged "time" UTCTime, Tagged "value" Int) q "SELECT * FROM foo"+ step "Querying a non-empty series with the results ignored"+ _ <- query @Ignored q "SELECT * FROM foo"+ return ()+ where+ db = "case_issue79" withDatabase :: Database -> IO a -> IO a withDatabase dbName f = bracket_