srtree-db-0.1.3.0: app/Export.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Export
( ExportOpts(..)
, exportParser
, runExport
) where
import qualified Data.Text as T
import Options.Applicative
import System.IO (hPutStrLn, hFlush, stdout, stderr)
import Data.SRTree.Print (showExpr)
import Algorithm.EqSat.Storage.Backend (SqlBackend(..), SqlValue(..), sqlToInt, sqlToMaybeDouble)
import Algorithm.EqSat.Storage.Extract (extractBestFromDB)
import Algorithm.EqSat.Storage.Schema (createSchemaFit)
import Algorithm.EqSat.Storage.SQLite ()
import Database.SQLite3 (Database, open, close, exec)
import Control.Exception (bracket)
import Control.Monad (forM_)
data ExportOpts = ExportOpts
{ exportEgraph :: String
, exportFitdb :: String
, exportDataset :: String
, exportFinite :: Bool
} deriving (Show)
exportParser :: Parser ExportOpts
exportParser = ExportOpts
<$> strOption
( long "egraph"
<> metavar "FILE"
<> help "Path to e-graph database" )
<*> strOption
( long "fitdb"
<> metavar "FILE"
<> help "Path to fit database" )
<*> strOption
( long "dataset"
<> metavar "NAME"
<> help "Dataset name" )
<*> switch
( long "finite"
<> short 'f'
<> help "Only export expressions with finite (non-NaN) fitness" )
runExport :: ExportOpts -> IO ()
runExport ExportOpts{..} = do
withSQLite exportFitdb $ \fitDb -> do
createSchemaFit fitDb
-- Look up dataset id (don't create if missing)
dsRows <- queryDb fitDb "SELECT id FROM dataset WHERE name = ?"
[SqlText (T.pack exportDataset)]
case dsRows of
[] -> do
hPutStrLn stderr $ "Dataset '" ++ exportDataset ++ "' not found."
hFlush stderr
[[dsIdVal]] -> do
let dsid = sqlToInt dsIdVal
withSQLite exportEgraph $ \egDb -> do
-- Query fit rows
let fitQuery
| exportFinite =
"SELECT eid, fitness, size FROM dataset_fit \
\WHERE dataset_id = ? AND fitness IS NOT NULL \
\ORDER BY eid"
| otherwise =
"SELECT eid, fitness, size FROM dataset_fit \
\WHERE dataset_id = ? \
\ORDER BY eid"
rows <- queryDb fitDb fitQuery [SqlInteger (fromIntegral dsid)]
-- Print header
putStrLn "expression,length,fitness"
hFlush stdout
-- Process each row
forM_ rows (processRow egDb)
let total = length rows
label = if exportFinite then " (finite)" else ""
hPutStrLn stderr $ "Exported " ++ show total ++ " expressions" ++ label ++ " from dataset '" ++ exportDataset ++ "'"
hFlush stderr
_ -> do
hPutStrLn stderr $ "Dataset '" ++ exportDataset ++ "' query returned unexpected result."
hFlush stderr
where
processRow egDb [eidVal, fitVal, szVal] = do
let eid = sqlToInt eidVal
sz = sqlToInt szVal
mfit = sqlToMaybeDouble fitVal
mTree <- extractBestFromDB egDb eid
case mTree of
Nothing -> do
hPutStrLn stderr $ "WARNING: could not reconstruct expression for eid=" ++ show eid
hFlush stderr
Just tree -> do
let expr = showExpr tree
fitStr = case mfit of
Nothing -> "NaN"
Just f -> show f
putStrLn $ expr ++ "," ++ show sz ++ "," ++ fitStr
hFlush stdout
processRow _ _ = pure ()
withSQLite :: String -> (Database -> IO a) -> IO a
withSQLite path f = bracket openDb close f
where
openDb = do
db <- open (T.pack path)
exec db "PRAGMA busy_timeout = 5000"
pure db