1
Fork 0

improve the dialect handling to be more feature based

This commit is contained in:
Jake Wheat 2019-08-31 13:40:23 +01:00
parent ee432d79ba
commit 020b33e729
2 changed files with 67 additions and 39 deletions

View file

@ -5,7 +5,6 @@ Data types to represent different dialect options
> {-# LANGUAGE DeriveDataTypeable #-} > {-# LANGUAGE DeriveDataTypeable #-}
> module Language.SQL.SimpleSQL.Dialect > module Language.SQL.SimpleSQL.Dialect
> (Dialect(..) > (Dialect(..)
> ,SyntaxFlavour(..)
> ,ansi2011 > ,ansi2011
> ,mysql > ,mysql
> ,postgres > ,postgres
@ -16,49 +15,78 @@ Data types to represent different dialect options
> import Data.Data > import Data.Data
hack for now, later will expand to flags on a feature by feature basis
> data SyntaxFlavour = ANSI2011
> | MySQL
> | Postgres
> | Oracle
> | SQLServer
> deriving (Eq,Show,Read,Data,Typeable)
> -- | Used to set the dialect used for parsing and pretty printing, > -- | Used to set the dialect used for parsing and pretty printing,
> -- very unfinished at the moment. > -- very unfinished at the moment.
> data Dialect = Dialect {diKeywords :: [String] > data Dialect = Dialect
> ,diSyntaxFlavour :: SyntaxFlavour > { -- | The list of reserved keywords
> ,diFetchFirst :: Bool > diKeywords :: [String]
> ,diLimit :: Bool > -- | does the dialect support ansi fetch first syntax
> ,diOdbc :: Bool} > ,diFetchFirst :: Bool
> -- | does the dialect support limit keyword (mysql, postgres, ...)
> ,diLimit :: Bool
> -- | allow parsing ODBC syntax
> ,diOdbc :: Bool
> -- | allow quoting identifiers with `backquotes`
> ,diBackquotedIden :: Bool
> -- | allow quoting identifiers with [square brackets]
> ,diSquareBracketQuotedIden :: Bool
> -- | allow identifiers with a leading at @example
> ,diAtIdentifier :: Bool
> -- | allow identifiers with a leading # #example
> ,diHashIdentifier :: Bool
> -- | allow positional identifiers like this: $1
> ,diPositionalArg :: Bool
> -- | allow postgres style dollar strings
> ,diDollarString :: Bool
> -- | allow strings with an e - e"example"
> ,diEString :: Bool
> -- | allow postgres style symbols
> ,diPostgresSymbols :: Bool
> -- | allow sql server style symbols
> ,diSqlServerSymbols :: Bool
> }
> deriving (Eq,Show,Read,Data,Typeable) > deriving (Eq,Show,Read,Data,Typeable)
> -- | ansi sql 2011 dialect > -- | ansi sql 2011 dialect
> ansi2011 :: Dialect > ansi2011 :: Dialect
> ansi2011 = Dialect {diKeywords = ansi2011ReservedKeywords > ansi2011 = Dialect {diKeywords = ansi2011ReservedKeywords
> ,diSyntaxFlavour = ANSI2011
> ,diFetchFirst = True > ,diFetchFirst = True
> ,diLimit = False > ,diLimit = False
> ,diOdbc = False} > ,diOdbc = False
> ,diBackquotedIden = False
> ,diSquareBracketQuotedIden = False
> ,diAtIdentifier = False
> ,diHashIdentifier = False
> ,diPositionalArg = False
> ,diDollarString = False
> ,diEString = False
> ,diPostgresSymbols = False
> ,diSqlServerSymbols = False
> }
> -- | mysql dialect > -- | mysql dialect
> mysql :: Dialect > mysql :: Dialect
> mysql = addLimit ansi2011 {diSyntaxFlavour = MySQL > mysql = addLimit ansi2011 {diFetchFirst = False
> ,diFetchFirst = False } > ,diBackquotedIden = True
> }
> -- | postgresql dialect > -- | postgresql dialect
> postgres :: Dialect > postgres :: Dialect
> postgres = addLimit ansi2011 {diSyntaxFlavour = Postgres} > postgres = addLimit ansi2011 {diPositionalArg = True
> ,diDollarString = True
> ,diEString = True
> ,diPostgresSymbols = True}
> -- | oracle dialect > -- | oracle dialect
> oracle :: Dialect > oracle :: Dialect
> oracle = ansi2011 {diSyntaxFlavour = Oracle} > oracle = ansi2011 -- {}
> -- | microsoft sql server dialect > -- | microsoft sql server dialect
> sqlserver :: Dialect > sqlserver :: Dialect
> sqlserver = ansi2011 {diSyntaxFlavour = SQLServer} > sqlserver = ansi2011 {diSquareBracketQuotedIden = True
> ,diAtIdentifier = True
> ,diHashIdentifier = True
> ,diSqlServerSymbols = True }
> addLimit :: Dialect -> Dialect > addLimit :: Dialect -> Dialect
> addLimit d = d {diKeywords = "limit": diKeywords d > addLimit d = d {diKeywords = "limit": diKeywords d

View file

@ -174,8 +174,8 @@ u&"unicode quoted identifier"
> [quotedIden > [quotedIden
> ,unicodeQuotedIden > ,unicodeQuotedIden
> ,regularIden > ,regularIden
> ,guard (diSyntaxFlavour d == MySQL) >> mySqlQuotedIden > ,guard (diBackquotedIden d) >> mySqlQuotedIden
> ,guard (diSyntaxFlavour d == SQLServer) >> sqlServerQuotedIden > ,guard (diSquareBracketQuotedIden d) >> sqlServerQuotedIden
> ] > ]
> where > where
> regularIden = Identifier Nothing <$> identifierString > regularIden = Identifier Nothing <$> identifierString
@ -217,15 +217,15 @@ use try because : and @ can be part of other things also
> prefixedVariable :: Dialect -> Parser Token > prefixedVariable :: Dialect -> Parser Token
> prefixedVariable d = try $ choice > prefixedVariable d = try $ choice
> [PrefixedVariable <$> char ':' <*> identifierString > [PrefixedVariable <$> char ':' <*> identifierString
> ,guard (diSyntaxFlavour d == SQLServer) >> > ,guard (diAtIdentifier d) >>
> PrefixedVariable <$> char '@' <*> identifierString > PrefixedVariable <$> char '@' <*> identifierString
> ,guard (diSyntaxFlavour d == SQLServer) >> > ,guard (diHashIdentifier d) >>
> PrefixedVariable <$> char '#' <*> identifierString > PrefixedVariable <$> char '#' <*> identifierString
> ] > ]
> positionalArg :: Dialect -> Parser Token > positionalArg :: Dialect -> Parser Token
> positionalArg d = > positionalArg d =
> guard (diSyntaxFlavour d == Postgres) >> > guard (diPositionalArg d) >>
> -- use try to avoid ambiguities with other syntax which starts with dollar > -- use try to avoid ambiguities with other syntax which starts with dollar
> PositionalArg <$> try (char '$' *> (read <$> many1 digit)) > PositionalArg <$> try (char '$' *> (read <$> many1 digit))
@ -243,7 +243,7 @@ x'hexidecimal string'
> sqlString d = dollarString <|> csString <|> normalString > sqlString d = dollarString <|> csString <|> normalString
> where > where
> dollarString = do > dollarString = do
> guard $ diSyntaxFlavour d == Postgres > guard $ diDollarString d
> -- use try because of ambiguity with symbols and with > -- use try because of ambiguity with symbols and with
> -- positional arg > -- positional arg
> delim <- (\x -> concat ["$",x,"$"]) > delim <- (\x -> concat ["$",x,"$"])
@ -270,7 +270,7 @@ x'hexidecimal string'
> -- error messages and user predictability make it a good > -- error messages and user predictability make it a good
> -- pragmatic choice > -- pragmatic choice
> csString > csString
> | diSyntaxFlavour d == Postgres = > | diEString d =
> choice [SqlString <$> try (string "e'" <|> string "E'") > choice [SqlString <$> try (string "e'" <|> string "E'")
> <*> return "'" <*> normalStringSuffix True "" > <*> return "'" <*> normalStringSuffix True ""
> ,csString'] > ,csString']
@ -304,7 +304,7 @@ constant.
> SqlNumber <$> completeNumber > SqlNumber <$> completeNumber
> -- this is for definitely avoiding possibly ambiguous source > -- this is for definitely avoiding possibly ambiguous source
> <* choice [-- special case to allow e.g. 1..2 > <* choice [-- special case to allow e.g. 1..2
> guard (diSyntaxFlavour d == Postgres) > guard (diPostgresSymbols d)
> *> (void $ lookAhead $ try $ string "..") > *> (void $ lookAhead $ try $ string "..")
> <|> void (notFollowedBy (oneOf "eE.")) > <|> void (notFollowedBy (oneOf "eE."))
> ,notFollowedBy (oneOf "eE.") > ,notFollowedBy (oneOf "eE.")
@ -324,7 +324,7 @@ constant.
> -- special case for postgresql, we backtrack if we see two adjacent dots > -- special case for postgresql, we backtrack if we see two adjacent dots
> -- to parse 1..2, but in other dialects we commit to the failure > -- to parse 1..2, but in other dialects we commit to the failure
> dot = let p = string "." <* notFollowedBy (char '.') > dot = let p = string "." <* notFollowedBy (char '.')
> in if (diSyntaxFlavour d == Postgres) > in if diPostgresSymbols d
> then try p > then try p
> else p > else p
> expon = (:) <$> oneOf "eE" <*> sInt > expon = (:) <$> oneOf "eE" <*> sInt
@ -342,12 +342,12 @@ compared with ansi and other dialects
> symbol :: Dialect -> Parser Token > symbol :: Dialect -> Parser Token
> symbol d = Symbol <$> choice (concat > symbol d = Symbol <$> choice (concat
> [dots > [dots
> ,if (diSyntaxFlavour d == Postgres) > ,if diPostgresSymbols d
> then postgresExtraSymbols > then postgresExtraSymbols
> else [] > else []
> ,miscSymbol > ,miscSymbol
> ,if diOdbc d then odbcSymbol else [] > ,if diOdbc d then odbcSymbol else []
> ,if (diSyntaxFlavour d == Postgres) > ,if diPostgresSymbols d
> then generalizedPostgresqlOperator > then generalizedPostgresqlOperator
> else basicAnsiOps > else basicAnsiOps
> ]) > ])
@ -360,10 +360,10 @@ compared with ansi and other dialects
> ,try (string "::" <* notFollowedBy (char ':')) > ,try (string "::" <* notFollowedBy (char ':'))
> ,try (string ":" <* notFollowedBy (char ':'))] > ,try (string ":" <* notFollowedBy (char ':'))]
> miscSymbol = map (string . (:[])) $ > miscSymbol = map (string . (:[])) $
> case diSyntaxFlavour d of > case () of
> SQLServer -> ",;():?" > _ | diSqlServerSymbols d -> ",;():?"
> Postgres -> "[],;()" > | diPostgresSymbols d -> "[],;()"
> _ -> "[],;():?" > | otherwise -> "[],;():?"
try is used because most of the first characters of the two character try is used because most of the first characters of the two character
symbols can also be part of a single character symbol symbols can also be part of a single character symbol
@ -562,7 +562,7 @@ followed by = or : makes a different symbol
two symbols next to eachother will fail if the symbols can combine and two symbols next to eachother will fail if the symbols can combine and
(possibly just the prefix) look like a different symbol (possibly just the prefix) look like a different symbol
> | Dialect {diSyntaxFlavour = Postgres} <- d > | diPostgresSymbols d
> , Symbol a' <- a > , Symbol a' <- a
> , Symbol b' <- b > , Symbol b' <- b
> , b' `notElem` ["+", "-"] || or (map (`elem` a') "~!@#%^&|`?") = False > , b' `notElem` ["+", "-"] || or (map (`elem` a') "~!@#%^&|`?") = False