/src-extra/tosort/examples/util/Database/HsSqlPpp/Utils/RoundTripTester.lhs

http://github.com/JakeWheat/hssqlppp · Haskell · 293 lines · 192 code · 35 blank · 66 comment · 13 complexity · 8748df32ae90429ead606e9255fef50a MD5 · raw file

  1. This file is a confused mess. The plan is to completely start again,
  2. to produce a big multi function routine which analyzes sql:
  3. * check it parses
  4. * lists type errors
  5. * produces documentation
  6. * catalog reference docs - so we can search an index, then click to go
  7. to definition in the rendered docs
  8. * check the roundtripping, etc.
  9. This will be mostly based on the runtestbattery function below.
  10. > {-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables,FlexibleContexts,QuasiQuotes #-}
  11. > module Database.HsSqlPpp.Utils.RoundTripTester
  12. > (roundTripTest
  13. > ,RoundTripResults(..)
  14. > ,TypeErrorList
  15. > ,rtShowBrief) where
  16. >
  17. > import System.Console.CmdArgs
  18. > --import System.IO
  19. > import Control.Monad.Error
  20. > import Data.List
  21. > --import Debug.Trace
  22. > import Data.Maybe
  23. > import Data.Generics.Uniplate.Data
  24. >
  25. > --import Database.HsSqlPpp.Tests.Tests
  26. > import Database.HsSqlPpp.Utils.Utils
  27. > --import Database.HsSqlPpp.Utils.Here
  28. >
  29. > import Database.HsSqlPpp.Ast
  30. > import Database.HsSqlPpp.Catalog
  31. > import Database.HsSqlPpp.TypeChecker
  32. > import Database.HsSqlPpp.Annotation
  33. > import Database.HsSqlPpp.SqlTypes
  34. >
  35. > import Database.HsSqlPpp.Parser
  36. > --import Database.HsSqlPpp.Parsing.Lexer
  37. >
  38. > import Database.HsSqlPpp.PrettyPrinter
  39. >
  40. > --import Database.HsSqlPpp.Examples.AnnotateSource
  41. >
  42. > --import Database.HsSqlPpp.DatabaseLoader.DatabaseLoader
  43. > --import Database.HsSqlPpp.Examples.WrapperGen
  44. > import Database.HsSqlPpp.Utils.DBUtils
  45. >
  46. > --import Database.HsSqlPpp.DevelTools.MakeWebsite
  47. > --import Database.HsSqlPpp.DevelTools.MakeAntiNodes
  48. > --import Database.HsSqlPpp.Examples.Extensions.TransitionConstraints
  49. > --import Database.HsSqlPpp.Examples.Extensions.ChaosExtensions
  50. > --import Database.HsSqlPpp.Examples.Chaos2010
  51. > type TypeErrorList = [(Maybe (String,Int,Int), [TypeError])]
  52. > data RoundTripResults = RoundTripResults
  53. > {rtDatabaseName :: String
  54. > ,rtFiles :: [FilePath]
  55. > ,rtEmptyCat :: Catalog
  56. > ,rtOrigCat :: Catalog
  57. > ,rtOrigAst :: [Statement]
  58. > ,rtOrigTypeErrors :: TypeErrorList
  59. > ,rtPgCat :: Catalog
  60. > ,rtOrigPgCatDiff :: CatalogDiff
  61. > ,rtDumpAst :: [Statement]
  62. > ,rtDumpCat :: Catalog
  63. > ,rtDumpTypeErrors :: TypeErrorList
  64. > ,rtOrigDumpCatDiff :: CatalogDiff
  65. > }
  66. > rtShowBrief :: RoundTripResults -> String
  67. > rtShowBrief rtt =
  68. > header "initial type errors"
  69. > ++ intercalate "\n" (ppTypeErrors (rtOrigTypeErrors rtt))
  70. > ++ header "cat diff: orig to pg"
  71. > ++ ppCatDiff (rtOrigPgCatDiff rtt)
  72. > ++ header "dump type errors"
  73. > ++ intercalate "\n" (ppTypeErrors (rtDumpTypeErrors rtt))
  74. > ++ header "cat diff: orig to dump"
  75. > ++ ppCatDiff (rtOrigDumpCatDiff rtt)
  76. > where
  77. > header x = "-------------" ++ x ++ "\n"
  78. > {-ppCatalogA = mode $ PPCatalog {database = def
  79. > ,files = def &= typ "FILES" & args}
  80. > &= text "reads each file, parses, type checks, then outputs the \
  81. > \changes to the catalog that the sql makes"
  82. >
  83. > ppCatalog :: String -> [FilePath] -> IO ()
  84. > ppCatalog db fns = wrapETs $ do
  85. > scat <- liftIO (readCatalog db) >>= tsl
  86. > (ncat, _) <- mapM (\f -> (liftIO . readInput) f >>=
  87. > tsl . P.parseSql f) fns >>=
  88. > return . (concat |>
  89. > astTransformer |>
  90. > A.typeCheck scat)
  91. > liftIO $ putStrLn $ ppCatDiff $ compareCatalogs scat emptyCatalog ncat
  92. -------------------------------------------------------------------------------
  93. load
  94. ====
  95. load sql files into a database via parsing and pretty printing them
  96. >
  97. > loadSql :: String -> [String] -> IO ()
  98. > loadSql db fns = wrapETs $
  99. > liftIO (hSetBuffering stdout NoBuffering) >>
  100. > mapM (\f -> (liftIO . readInput) f >>=
  101. > tsl . P.parseSql f) fns >>=
  102. > return . (concat |>
  103. > astTransformer) >>=
  104. > liftIO . loadAst db
  105. -------------------------------------------------------------------------------
  106. loadPsql
  107. ========
  108. load sql files into a database via psql
  109. > loadPsqlA = mode $ LoadPsql {database = def
  110. > ,files = def &= typ "FILES" & args}
  111. > &= text "loads sql into a database using psql."
  112. >
  113. > loadSqlPsql :: String -> [String] -> IO ()
  114. > loadSqlPsql db = wrapETs .
  115. > mapM_ (\s -> liftIO (loadSqlUsingPsqlFromFile db s) >>=
  116. > tsl >>=
  117. > liftIO . putStrLn)
  118. -------------------------------------------------------------------------------
  119. clearLoad
  120. =========
  121. like load above, but runs the clear command first
  122. might try to work out a way of running multiple commands in one invoc
  123. of this exe, then this command will disappear
  124. > clearLoadA = mode $ ClearLoad {database = def
  125. > ,files = def &= typ "FILES" & args}
  126. > &= text "cleardb then loadsql"
  127. >
  128. > clearAndLoadSql :: String -> [String] -> IO ()
  129. > clearAndLoadSql db fns = cleardb db >> loadSql db fns -}
  130. -------------------------------------------------------------------------------
  131. testBattery
  132. ===========
  133. run test battery: run a bunch of tests including consistency on the
  134. database and sql files given
  135. The idea is to typecheck the sql, load it into pg and dump it via psql
  136. and via database loader, can then compare asts, catalogs, etc. in a
  137. lot of different ways
  138. currently:
  139. parse and type check sql, save the catalog
  140. load the sql into the db using psql, compare the catalog read from pg
  141. with the catalog from typechecking
  142. dump the sql and typecheck the dump, compare the catalog from this
  143. check with the catalog from the original typecheck
  144. todo: compare asts from initial parse with parsed dump - this is going
  145. to be a lot of work to get passing since the statements are
  146. re-ordered, and sometimes changed/ split up by pg
  147. also: load the sql using the extension system and database loader,
  148. then compare pg catalog with initial catalog, and dump and compare ast
  149. with original ast
  150. want to run a similar set of tests starting with the dump sql:
  151. get ast,cat from dump sql, load using psql and using databaseloader
  152. and check cats and subsequent dump asts.
  153. getting the dump ast comparing with the original ast:
  154. step one: convert tests in parser test to also roundtrip through
  155. database, see parsertests for details
  156. step two: write an ast conversion routine: assume that the pgdump ast
  157. is like the ast fed into pg but with a few statements split into
  158. components (e.g. create table with serial is split into create
  159. sequence and create table), and then the statements are reordered, so
  160. write a routine to mirror this - will then have
  161. (anyast -> rearrange and reorder) == (anyast -> pg->pgdump)
  162. rough new plan:
  163. combine this with the planned report generator
  164. have new annotation routine used in website, in annotatesource2
  165. first part is to run this and produce html report of the source,
  166. then add the catalog summary page, and list of type errors as with the website generator
  167. then we do the round trip tests:
  168. load into database, then compare catalogs
  169. dump from database, reparse and compare catalogs
  170. then parse, typecheck the dumped code, list type errors and catalog differences
  171. when the code is up to it, compare the original ast to the dumped ast.
  172. make different stages optional:
  173. run as a check tool, just want the catalog differences and type errors
  174. on the command line. would be nice to try and link the catalog
  175. differences to source positions.
  176. do website generation, without the pg roundtrips
  177. > parseFiles :: [String] -> IO [StatementList]
  178. > parseFiles fns = do
  179. > as <- mapM (\f -> fmap (parseStatements f) $ readFile f) fns
  180. > return $ either (error . show) id $ sequence as
  181. > roundTripTest :: ([Statement] -> [Statement]) -> String -> [FilePath] -> IO RoundTripResults
  182. > roundTripTest astTransformer dbName fns = wrapETs $ do
  183. > -- clear target database
  184. > liftIO $ clearDBN dbName
  185. > -- get the catalog of the empty database
  186. > emptyCat <- readCat dbName
  187. > -- get the ast and catalog of the sql to test with the catalog
  188. > -- determined by the hssqlppp typechecker
  189. > (origAst :: [Statement]) <- (astTransformer . concat) `fmap` liftIO (parseFiles fns)
  190. > let (origCat :: Catalog, origAast :: [Statement]) = typeCheckStatements emptyCat origAst
  191. > let origTypeErrors = getTypeErrors origAast
  192. > -- load the test sql into postgresql using psql and get the
  193. > -- new catalog from postgresql
  194. > _ <- liftIO $ loadSqlUsingPsql dbName $ printStatements origAst
  195. > pgCat <- readCat dbName
  196. > -- show the differences between the catalog as determined by the
  197. > -- hssqlppp type checker and by loading into postgresql and reading
  198. > -- the catalog from the loaded database
  199. > let origPgCatDiff = compareCatalogs emptyCat origCat pgCat
  200. > -- dump the database from postgresql, parse and run the dump sql through the
  201. > -- hssqlppp type checker
  202. > dumpSql <- liftIO $ pgDump dbName
  203. > dumpSqlAst <- etsr $ parseStatements "" dumpSql
  204. > let (dumpCat,dumpAast) = typeCheckStatements emptyCat dumpSqlAst
  205. > let dumpTypeErrors = getTypeErrors dumpAast
  206. > let origDumpCatDiff = compareCatalogs emptyCat origCat dumpCat
  207. >
  208. > return $ RoundTripResults
  209. > {rtDatabaseName = dbName
  210. > ,rtFiles = fns
  211. > ,rtEmptyCat = emptyCat
  212. > ,rtOrigCat = origCat
  213. > ,rtOrigAst = origAast
  214. > ,rtOrigTypeErrors = origTypeErrors
  215. > ,rtPgCat = pgCat
  216. > ,rtOrigPgCatDiff = origPgCatDiff
  217. > ,rtDumpAst = dumpAast
  218. > ,rtDumpCat = dumpCat
  219. > ,rtDumpTypeErrors = dumpTypeErrors
  220. > ,rtOrigDumpCatDiff = origDumpCatDiff
  221. > }
  222. > where
  223. > readCat d = liftIO (readCatalog d) >>= etsr
  224. > -- | Pretty print list of type errors with optional source position
  225. > -- in emacs readable format.
  226. > ppTypeErrors :: [(Maybe (String,Int,Int), [TypeError])] -> [String]
  227. > ppTypeErrors tes =
  228. > map showSpTe tes
  229. > where
  230. > showSpTe (Just (fn,l,c), e) =
  231. > fn ++ ":" ++ show l ++ ":" ++ show c ++ ":\n" ++ show e
  232. > showSpTe (_,e) = "unknown:0:0:\n" ++ show e
  233. > getTypeErrors :: Data a => a -> [(Maybe (String,Int,Int), [TypeError])]
  234. > getTypeErrors es =
  235. > let as = [(a::Annotation) | a <- universeBi es]
  236. > in mapMaybe getTes as
  237. > where
  238. > getTes as = let tes = errs as
  239. > in if null tes
  240. > then Nothing
  241. > else Just (asrc as, tes)
  242. > type ErrorIO e a = ErrorT e IO a
  243. > etsr :: Show e => Either e a -> ErrorIO String a
  244. > etsr = either (throwError . show) return