improve the dialect handling to be more feature based
This commit is contained in:
parent
ee432d79ba
commit
020b33e729
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue