-
Notifications
You must be signed in to change notification settings - Fork 1
/
tssql.hs
163 lines (140 loc) · 6.7 KB
/
tssql.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
-----------------------------------------------------------------------------
-- |
-- Copyright : (c) Keith Sheppard 2009-2010
-- License : BSD3
-- Maintainer : [email protected]
-- Stability : experimental
-- Portability : portable
--
-- Main entry point for the TxtSushi SQL command line
--
-----------------------------------------------------------------------------
import Data.Char
import Data.List
import qualified Data.Map as M
import System.Environment
import System.Exit
import Text.ParserCombinators.Parsec
import Database.TxtSushi.CommandLineArgument
import Database.TxtSushi.FlatFile
import Database.TxtSushi.IOUtil
import Database.TxtSushi.ParseUtil
import Database.TxtSushi.SQLExecution
import Database.TxtSushi.SQLFunctionDefinitions
import Database.TxtSushi.SQLParser
helpOption :: OptionDescription
helpOption = OptionDescription {
isRequired = False,
optionFlag = "-help",
argumentNames = ["function_name"],
minArgumentCount = 0,
argumentCountIsFixed = False}
externalSortOption :: OptionDescription
externalSortOption = OptionDescription {
isRequired = False,
optionFlag = "-external-sort",
argumentNames = [],
minArgumentCount = 0,
argumentCountIsFixed = True}
tableDefOption :: OptionDescription
tableDefOption = OptionDescription {
isRequired = False,
optionFlag = "-table",
argumentNames = ["table_name", "CSV_file_name"],
minArgumentCount = 2,
argumentCountIsFixed = True}
allOpts :: [OptionDescription]
allOpts = [helpOption, externalSortOption, tableDefOption]
sqlCmdLine :: CommandLineDescription
sqlCmdLine = CommandLineDescription {
options = allOpts,
minTailArgumentCount = 0,
tailArgumentNames = ["SQL_select_statement"],
tailArgumentCountIsFixed = True}
validateTableNames :: [String] -> [String] -> Bool
validateTableNames [] _ = True
validateTableNames (argTblHead:argTblTail) selectTbls =
if argTblHead `elem` selectTbls then
validateTableNames argTblTail selectTbls
else
error $ "The given table name \"" ++ argTblHead ++
"\" does not appear in the SELECT statement"
tableArgsToMap :: [[String]] -> M.Map String String
tableArgsToMap [] = M.empty
tableArgsToMap (currTableArgs:tailTableArgs) =
case currTableArgs of
[fileName, tblName] ->
M.insert fileName tblName (tableArgsToMap tailTableArgs)
_ ->
error $ "the \"" ++ optionFlag tableDefOption ++
"\" option should have exactly two arguments"
unwrapMapList :: (Monad m) => [(t, m t1)] -> m [(t, t1)]
unwrapMapList [] = return []
unwrapMapList ((key, value):mapTail) = do
unwrappedValue <- value
unwrappedTail <- unwrapMapList mapTail
return $ (key, unwrappedValue):unwrappedTail
printUsage :: String -> IO ()
printUsage progName = do
putStrLn $ progName ++ " (" ++ versionStr ++ ")"
putStrLn $ "Usage: " ++ progName ++ " " ++ formatCommandLine sqlCmdLine
argsToSortConfig :: M.Map OptionDescription a -> SortConfiguration
argsToSortConfig argMap =
if M.member externalSortOption argMap then UseExternalSort else UseInMemorySort
-- | the help map is a mapping from function name to a string pair
-- where fst is the grammar and snd is the description
helpMap :: M.Map String (String, String)
helpMap = M.fromList allFuncHelp
where
allFuncHelp =
map funcToHelp $ normalSyntaxFunctions ++ concat infixFunctions ++ specialFunctions
funcToHelp sqlFunc =
(map toUpper . functionName $ sqlFunc, (functionGrammar sqlFunc, functionDescription sqlFunc))
printHelpTerms :: IO ()
printHelpTerms = putStrLn $ "Functions (can be used with -help option): " ++ intercalate ", " helpTerms
where helpTerms = sort . M.keys $ helpMap
printTermHelp :: String -> IO ()
printTermHelp term = case M.lookup (map toUpper term) helpMap of
Just (grammar, description) ->
putStrLn grammar >> putChar '\t' >> putStrLn description
Nothing ->
putStrLn $ "\"" ++ term ++ "\" is not a known function"
main :: IO ()
main = do
args <- getArgs
progName <- getProgName
let (argMap, argTail) = extractCommandLineArguments sqlCmdLine args
parseOutcome = parse (withTrailing eof parseSelectStatement) "" (head argTail)
case M.lookup helpOption argMap of
Just terms -> case concat terms of
[] -> printUsage progName >> printHelpTerms
concatTerms -> printUsage progName >> mapM_ printTermHelp concatTerms
Nothing ->
if length argTail /= 1 then printUsage progName >> printHelpTerms else case parseOutcome of
Left err -> print err
Right selectStmt ->
let
-- create a table file map from the user args
tableArgs = M.findWithDefault [] tableDefOption argMap
tableArgMap = tableArgsToMap tableArgs
-- get a default table to file map from the select statement
selectTblNames = allMaybeTableNames (maybeFromTable selectStmt)
defaultTblMap = M.fromList (zip selectTblNames selectTblNames)
-- join the two with arg values taking precidence over
-- the default values
finalTblFileMap = tableArgMap `M.union` defaultTblMap
in
-- turn the files into strings
if validateTableNames (M.keys tableArgMap) selectTblNames
then do
let contentsMap = M.map getContentsFromFileOrStdin finalTblFileMap
unwrappedContents <- unwrapMapList $ M.toList contentsMap
let unwrappedContentsMap = M.fromList unwrappedContents
textTableMap = M.map (parseTable csvFormat) unwrappedContentsMap
dbTableMap = M.mapWithKey textTableToDatabaseTable textTableMap
sortCfg = argsToSortConfig argMap
selectedDbTable = select sortCfg selectStmt dbTableMap
selectedTxtTable = databaseTableToTextTable selectedDbTable
putStr $ formatTable csvFormat selectedTxtTable
else
exitFailure