DBlimited 0.1 → 0.1.1
raw patch · 4 files changed
+246/−2 lines, 4 files
Files
- DBlimited.cabal +2/−2
- Query.hs +89/−0
- Schema.hs +126/−0
- Utils.hs +29/−0
DBlimited.cabal view
@@ -7,7 +7,7 @@ -- The package version. See the Haskell package versioning policy -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for -- standards guiding when and how versions should be incremented.-Version: 0.1+Version: 0.1.1 -- A short (one-line) description of the package. Synopsis: A command-line SQL interface for flat files (tdf,csv,etc.)@@ -54,7 +54,7 @@ Build-depends: containers, base >= 2 && < 4, parsec -- Modules not exported by this package.- -- Other-modules: + Other-modules: Utils, Schema, Query -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source. -- Build-tools:
+ Query.hs view
@@ -0,0 +1,89 @@+{-+ Query+-}++module Query where++-- DBlimited imports+import Schema+import qualified Utils as U++-- Package imports+import Data.Char+import System.IO+import qualified Text.ParserCombinators.Parsec as P+import qualified Data.Map as M++data Clause = All |+ And Clause Clause |+ Or Clause Clause |+ Equals {getTarget :: String, getTest :: String}+ deriving (Show)++type Row = [String]++data Query = InvalidQuery |+ Query {getSelect :: [Column], getFrom :: Table, getWhere :: Clause}+ deriving (Show)++-- build a query+getQuery :: Schema -> String -> IO Query+getQuery schema queryString = case (P.parse (parseQuery schema) "" queryString) of+ Left err -> return InvalidQuery+ Right (Just q) -> return q++parseQuery :: Schema -> P.Parser (Maybe Query)+parseQuery schema = do+ parseChars "SELECT "+ cols <- U.csv -- re-used from Schema+ parseChars " FROM "+ maybeTable <- (parseTable schema)+ P.char ';'+ return $ case maybeTable of+ Nothing -> Nothing+ (Just table) -> Just (Query cols table All)+ P.<?> "SELECT a,b,c FROM tablename;"++parseTable :: Schema -> P.Parser (Maybe Table)+parseTable schema = do+ tablename <- U.object+ return (M.lookup tablename schema)++parseChars :: [Char] -> P.Parser String+parseChars [] = return ""+parseChars (c:cs) = do+ pc <- P.char (toUpper c) P.<|> P.char (toLower c)+ pcs <- parseChars cs+ return (toLower pc : pcs)+ P.<?> (c:cs)++-- use the schema to look up and load the table+loadTable :: Table -> IO [Row]+loadTable table = do+ handle <- openFile (getPath table) ReadMode+ raw <- hGetContents handle+ return . (map.(U.split).getDlmt$table) . lines $ raw++-- given a initial list of columns and a subset, return the corresponding subset from a given row+evalSelect :: [Column] -> [Column] -> (Row -> Row)+evalSelect sourceCols pickedCols = + (\inputrow -> [ inputrow !! i | i <- indices ])+ where + indices = U.selector sourceCols pickedCols++-- given an initial list of columns and a test, determine if a given line passes the test+evalWhere :: [Column] -> Clause -> (Row -> Bool)+evalWhere _cols All = (\r -> True)+evalWhere cols (And c1 c2) = (\r -> (evalWhere cols c1 r && evalWhere cols c2 r))+evalWhere cols (Or c1 c2) = (\r -> (evalWhere cols c1 r || evalWhere cols c2 r))+evalWhere cols (Equals target f) = undefined++-- given a query and some rows, return the resulting rows+evalQuery :: Query -> IO ()+evalQuery InvalidQuery = putStrLn "Error in query"+evalQuery (Query cols table clause) = do+ inputRows <- loadTable table+ putStrLn . show . + (map $ evalSelect (getCols table) cols) . + (filter $ evalWhere (getCols table) clause) $ + inputRows
+ Schema.hs view
@@ -0,0 +1,126 @@+{-+ Schema+-}++module Schema where++--import Char+import Control.Monad+import qualified Data.Map as M+import qualified Text.ParserCombinators.Parsec as P+--import Data.Either+import Data.Maybe+import Data.Monoid++import qualified Utils as U++type Column = String +type Delimiter = Char++data Table = Table {getName :: String, getPath :: FilePath, getDlmt :: Delimiter, getCols :: [Column]}+ deriving (Show)++{-+delimiterMap :: Map Char Char+delimiterMap = fromList + [('c',','),+ ('t','\t'),+ ('s',' '),+ ('l','~')]+-}++prettyPrintTables :: [Maybe Table] -> IO ()+prettyPrintTables [] = return ()+prettyPrintTables (Nothing :ts) = putStrLn "Not a table."+prettyPrintTables ((Just t):ts) = do + putStrLn $ (getName t) ++ " : " ++ (show.getCols$t)+ prettyPrintTables ts++type Schema = M.Map String Table++prettyPrintSchema :: Schema -> IO ()+prettyPrintSchema schema = prettyPrintTables $ map Just (M.elems schema)++{-+Schema file format:++<tablename>;<absolutefilepath>;<delimitercharacter>;<column_0>,<column_1>,...,<column_n-1>++tablename can be alphanumeric, but can't start with a digit+aboslutefilepath is self-explanitory+delimitercharacter is coded, t for tab, c for comma, s for space, l for tilde+finally, comma separated columns, with same rules as tablename+-}+++tablename :: P.Parser String+tablename = U.object++abspath :: P.Parser FilePath+abspath = do+ root <- P.char '/'+ rest <- P.many (P.letter + P.<|> P.digit + P.<|> P.char '/' + P.<|> P.char '-' + P.<|> P.char '_' + P.<|> P.char '.')+ return (root:rest)+ P.<?> "absoulute file path"++delimiter :: P.Parser Delimiter+delimiter = P.anyChar++comment :: P.Parser ()+comment = do+ P.char '#'+ P.skipMany (P.noneOf "\n")+ P.<?> "comment"++eol :: P.Parser ()+eol = do+ P.oneOf "\n"+ return ()+ P.<?> "end of line"++item :: P.Parser (String,Table)+item = do+ tn <- tablename+ P.char ';'+ path <- abspath+ P.char ';'+ dlmt <- delimiter+ P.char ';'+ cols <- U.csv+ P.manyTill P.anyChar ( P.try eol P.<|> P.try comment P.<|> P.eof)+ return (tn,Table tn path dlmt cols)+ P.<?> "item"++line :: P.Parser (Maybe (String,Table))+line = do+ P.skipMany P.space+ P.try (comment >> return Nothing) P.<|> (item >>= return . Just)++fileParser :: P.Parser [(String,Table)]+fileParser = do+ lines <- P.many line+ return (catMaybes lines)++{-+readSchema1 :: FilePath -> IO (Either P.ParseError Schema)+readSchema1 schemaPath = + P.parseFromFile fileParser schemaPath >>= + return . fmap (foldr (uncurry M.insert) M.empty)+-}++readSchema :: FilePath -> IO Schema+readSchema schemaPath = do+ result <- P.parseFromFile fileParser schemaPath+ return $ case result of+ Left err -> M.empty+ Right ts -> foldr (uncurry M.insert) M.empty ts++{- test instances -}++sampleSchema = readSchema "/DBlimited/sampleschema.txt"+sampleTable = Table "person" "/DBlimited/sample/person.csv" ',' ["id","name","address","telephone","dob"]
+ Utils.hs view
@@ -0,0 +1,29 @@+{-+ Utils+-}++module Utils where++import Data.List+import Data.Function+import Data.Maybe+import qualified Text.ParserCombinators.Parsec as P++split :: Char -> String -> [String]+split dlmt = filter ( not . any ((==) dlmt)). groupBy (on (==) ((==) dlmt))++selector :: Eq a => [a] -> [a] -> [Int]+selector source picks = (catMaybes [elemIndex x source | x <- picks])++object :: P.Parser String+object = do+ c <- P.letter+ cs <- P.many (P.letter P.<|> P.digit)+ return (c:cs)+ P.<?> "object"++csv :: P.Parser [String]+csv = do+ csv <- P.many (P.letter P.<|> P.digit P.<|> P.char ',')+ return (split ',' csv)+ P.<?> "columns"