switch tests to hspec, improve error messages
This commit is contained in:
parent
fadd010942
commit
c11bee4a9c
|
@ -74,7 +74,6 @@ try again to add annotation to the ast
|
|||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
|
||||
module Language.SQL.SimpleSQL.Lex
|
||||
(Token(..)
|
||||
,WithPos(..)
|
||||
|
@ -111,21 +110,26 @@ import Text.Megaparsec
|
|||
,pstateSourcePos
|
||||
,statePosState
|
||||
,mkPos
|
||||
,hidden
|
||||
,setErrorOffset
|
||||
|
||||
,choice
|
||||
,satisfy
|
||||
,takeWhileP
|
||||
,takeWhile1P
|
||||
,(<?>)
|
||||
,eof
|
||||
,many
|
||||
,try
|
||||
,option
|
||||
,(<|>)
|
||||
,notFollowedBy
|
||||
,manyTill
|
||||
,anySingle
|
||||
,lookAhead
|
||||
,match
|
||||
,optional
|
||||
,label
|
||||
,chunk
|
||||
,region
|
||||
,anySingle
|
||||
)
|
||||
import qualified Text.Megaparsec as M
|
||||
import Text.Megaparsec.Char
|
||||
|
@ -139,17 +143,17 @@ import qualified Data.List.NonEmpty as NE
|
|||
import Data.Proxy (Proxy(..))
|
||||
import Data.Void (Void)
|
||||
|
||||
import Control.Applicative ((<**>))
|
||||
import Data.Char
|
||||
(isAlphaNum
|
||||
,isAlpha
|
||||
,isSpace
|
||||
,isDigit
|
||||
)
|
||||
import Control.Monad (void, guard)
|
||||
import Control.Monad (void)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Data.Maybe (fromMaybe)
|
||||
--import Text.Megaparsec.Debug (dbg)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
|
@ -189,16 +193,26 @@ data Token
|
|||
| LineComment Text
|
||||
-- | A block comment, \/* stuff *\/, includes the comment delimiters
|
||||
| BlockComment Text
|
||||
-- | Used for generating better error messages when using the
|
||||
-- output of the lexer in a parser
|
||||
| InvalidToken Text
|
||||
deriving (Eq,Show,Ord)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- main api functions
|
||||
|
||||
-- | Lex some SQL to a list of tokens.
|
||||
-- | Lex some SQL to a list of tokens. The invalid token setting
|
||||
-- changes the behaviour so that if there's a parse error at the start
|
||||
-- of parsing an invalid token, it adds a final InvalidToken with the
|
||||
-- character to the result then stop parsing. This can then be used to
|
||||
-- produce a parse error with more context in the parser. Parse errors
|
||||
-- within tokens still produce Left errors.
|
||||
lexSQLWithPositions
|
||||
:: Dialect
|
||||
-- ^ dialect of SQL to use
|
||||
-> Bool
|
||||
-- ^ produce InvalidToken
|
||||
-> Text
|
||||
-- ^ filename to use in error messages
|
||||
-> Maybe (Int,Int)
|
||||
|
@ -207,13 +221,14 @@ lexSQLWithPositions
|
|||
-> Text
|
||||
-- ^ the SQL source to lex
|
||||
-> Either ParseError [WithPos Token]
|
||||
lexSQLWithPositions dialect fn p src = myParse fn p (many (sqlToken dialect) <* (eof <?> "")) src
|
||||
|
||||
lexSQLWithPositions dialect pit fn p src = myParse fn p (tokens dialect pit) src
|
||||
|
||||
-- | Lex some SQL to a list of tokens.
|
||||
lexSQL
|
||||
:: Dialect
|
||||
-- ^ dialect of SQL to use
|
||||
-> Bool
|
||||
-- ^ produce InvalidToken, see lexSQLWithPositions
|
||||
-> Text
|
||||
-- ^ filename to use in error messages
|
||||
-> Maybe (Int,Int)
|
||||
|
@ -222,8 +237,8 @@ lexSQL
|
|||
-> Text
|
||||
-- ^ the SQL source to lex
|
||||
-> Either ParseError [Token]
|
||||
lexSQL dialect fn p src =
|
||||
map tokenVal <$> lexSQLWithPositions dialect fn p src
|
||||
lexSQL dialect pit fn p src =
|
||||
map tokenVal <$> lexSQLWithPositions dialect pit fn p src
|
||||
|
||||
myParse :: Text -> Maybe (Int,Int) -> Parser a -> Text -> Either ParseError a
|
||||
myParse name sp' p s =
|
||||
|
@ -271,6 +286,7 @@ prettyToken _ (SqlNumber r) = r
|
|||
prettyToken _ (Whitespace t) = t
|
||||
prettyToken _ (LineComment l) = l
|
||||
prettyToken _ (BlockComment c) = c
|
||||
prettyToken _ (InvalidToken t) = t
|
||||
|
||||
prettyTokens :: Dialect -> [Token] -> Text
|
||||
prettyTokens d ts = T.concat $ map (prettyToken d) ts
|
||||
|
@ -281,24 +297,54 @@ prettyTokens d ts = T.concat $ map (prettyToken d) ts
|
|||
|
||||
-- | parser for a sql token
|
||||
sqlToken :: Dialect -> Parser (WithPos Token)
|
||||
sqlToken d = (do
|
||||
-- possibly there's a more efficient way of doing the source positions?
|
||||
sqlToken d =
|
||||
withPos $ hidden $ choice $
|
||||
[sqlString d
|
||||
,identifier d
|
||||
,lineComment d
|
||||
,blockComment d
|
||||
,sqlNumber d
|
||||
,positionalArg d
|
||||
,dontParseEndBlockComment d
|
||||
,prefixedVariable d
|
||||
,symbol d
|
||||
,sqlWhitespace d]
|
||||
|
||||
--fakeSourcePos :: SourcePos
|
||||
--fakeSourcePos = SourcePos "" (mkPos 1) (mkPos 1)
|
||||
|
||||
--------------------------------------
|
||||
|
||||
-- position and error helpers
|
||||
|
||||
withPos :: Parser a -> Parser (WithPos a)
|
||||
withPos p = do
|
||||
sp <- getSourcePos
|
||||
off <- getOffset
|
||||
t <- choice
|
||||
[sqlString d
|
||||
,identifier d
|
||||
,lineComment d
|
||||
,blockComment d
|
||||
,sqlNumber d
|
||||
,positionalArg d
|
||||
,dontParseEndBlockComment d
|
||||
,prefixedVariable d
|
||||
,symbol d
|
||||
,sqlWhitespace d]
|
||||
a <- p
|
||||
off1 <- getOffset
|
||||
ep <- getSourcePos
|
||||
pure $ WithPos sp ep (off1 - off) t) <?> "valid lexical token"
|
||||
pure $ WithPos sp ep (off1 - off) a
|
||||
|
||||
{-
|
||||
|
||||
TODO: extend this idea, to recover to parsing regular tokens after an
|
||||
invalid one. This can then support resumption after error in the parser.
|
||||
This would also need something similar being done for parse errors
|
||||
within lexical tokens.
|
||||
|
||||
-}
|
||||
invalidToken :: Dialect -> Parser (WithPos Token)
|
||||
invalidToken _ =
|
||||
withPos $ (hidden eof *> fail "") <|> (InvalidToken . T.singleton <$> anySingle)
|
||||
|
||||
tokens :: Dialect -> Bool -> Parser [WithPos Token]
|
||||
tokens d pit = do
|
||||
x <- many (sqlToken d)
|
||||
if pit
|
||||
then choice [x <$ hidden eof
|
||||
,(\y -> x ++ [y]) <$> hidden (invalidToken d)]
|
||||
else x <$ hidden eof
|
||||
|
||||
--------------------------------------
|
||||
|
||||
|
@ -313,27 +359,38 @@ x'hexidecimal string'
|
|||
-}
|
||||
|
||||
sqlString :: Dialect -> Parser Token
|
||||
sqlString d = dollarString <|> csString <|> normalString
|
||||
sqlString d =
|
||||
(if (diDollarString d)
|
||||
then (dollarString <|>)
|
||||
else id) csString <|> normalString
|
||||
where
|
||||
dollarString = do
|
||||
guard $ diDollarString d
|
||||
-- use try because of ambiguity with symbols and with
|
||||
-- positional arg
|
||||
delim <- (\x -> T.concat ["$",x,"$"])
|
||||
<$> try (char '$' *> option "" identifierString <* char '$')
|
||||
SqlString delim delim . T.pack <$> manyTill anySingle (try $ string delim)
|
||||
normalString = SqlString "'" "'" <$> (char '\'' *> normalStringSuffix False "")
|
||||
normalStringSuffix allowBackslash t = do
|
||||
s <- takeWhileP Nothing $ if allowBackslash
|
||||
then (`notElemChar` "'\\")
|
||||
else (/= '\'')
|
||||
-- deal with '' or \' as literal quote character
|
||||
choice [do
|
||||
ctu <- choice ["''" <$ try (string "''")
|
||||
,"\\'" <$ string "\\'"
|
||||
,"\\" <$ char '\\']
|
||||
normalStringSuffix allowBackslash $ T.concat [t,s,ctu]
|
||||
,T.concat [t,s] <$ char '\'']
|
||||
delim <- fstMatch (try (char '$' *> hoptional_ identifierString <* char '$'))
|
||||
let moreDollarString =
|
||||
label (T.unpack delim) $ takeWhileP_ Nothing (/='$') *> checkDollar
|
||||
checkDollar = label (T.unpack delim) $
|
||||
choice
|
||||
[lookAhead (chunk_ delim) *> pure () -- would be nice not to parse it twice?
|
||||
-- but makes the whole match trick much less neat
|
||||
,char_ '$' *> moreDollarString]
|
||||
str <- fstMatch moreDollarString
|
||||
chunk_ delim
|
||||
pure $ SqlString delim delim str
|
||||
lq = label "'" $ char_ '\''
|
||||
normalString = SqlString "'" "'" <$> (lq *> normalStringSuffix False)
|
||||
normalStringSuffix allowBackslash = label "'" $ do
|
||||
let regularChar = if allowBackslash
|
||||
then (\x -> x /= '\'' && x /='\\')
|
||||
else (\x -> x /= '\'')
|
||||
nonQuoteStringChar = takeWhileP_ Nothing regularChar
|
||||
nonRegularContinue =
|
||||
(hchunk_ "''" <|> hchunk_ "\\'" <|> hchar_ '\\')
|
||||
moreChars = nonQuoteStringChar
|
||||
*> (option () (nonRegularContinue *> moreChars))
|
||||
fstMatch moreChars <* lq
|
||||
|
||||
-- try is used to to avoid conflicts with
|
||||
-- identifiers which can start with n,b,x,u
|
||||
-- once we read the quote type and the starting '
|
||||
|
@ -345,13 +402,13 @@ sqlString d = dollarString <|> csString <|> normalString
|
|||
csString
|
||||
| diEString d =
|
||||
choice [SqlString <$> try (string "e'" <|> string "E'")
|
||||
<*> pure "'" <*> normalStringSuffix True ""
|
||||
<*> pure "'" <*> normalStringSuffix True
|
||||
,csString']
|
||||
| otherwise = csString'
|
||||
csString' = SqlString
|
||||
<$> try cs
|
||||
<*> pure "'"
|
||||
<*> normalStringSuffix False ""
|
||||
<*> normalStringSuffix False
|
||||
csPrefixes = map (`T.cons` "'") "nNbBxX" ++ ["u&'", "U&'"]
|
||||
cs :: Parser Text
|
||||
cs = choice $ map string csPrefixes
|
||||
|
@ -370,42 +427,49 @@ u&"unicode quoted identifier"
|
|||
|
||||
identifier :: Dialect -> Parser Token
|
||||
identifier d =
|
||||
choice
|
||||
choice $
|
||||
[quotedIden
|
||||
,unicodeQuotedIden
|
||||
,regularIden
|
||||
,guard (diBackquotedIden d) >> mySqlQuotedIden
|
||||
,guard (diSquareBracketQuotedIden d) >> sqlServerQuotedIden
|
||||
]
|
||||
,regularIden]
|
||||
++ [mySqlQuotedIden | diBackquotedIden d]
|
||||
++ [sqlServerQuotedIden | diSquareBracketQuotedIden d]
|
||||
where
|
||||
regularIden = Identifier Nothing <$> identifierString
|
||||
quotedIden = Identifier (Just ("\"","\"")) <$> qidenPart
|
||||
mySqlQuotedIden = Identifier (Just ("`","`"))
|
||||
<$> (char '`' *> takeWhile1P Nothing (/='`') <* char '`')
|
||||
sqlServerQuotedIden = Identifier (Just ("[","]"))
|
||||
<$> (char '[' *> takeWhile1P Nothing (`notElemChar` "[]") <* char ']')
|
||||
quotedIden = Identifier (Just ("\"","\"")) <$> qiden
|
||||
failEmptyIden c = failOnThis (char_ c) "empty identifier"
|
||||
mySqlQuotedIden =
|
||||
Identifier (Just ("`","`")) <$>
|
||||
(char_ '`' *>
|
||||
(failEmptyIden '`'
|
||||
<|> (takeWhile1P Nothing (/='`') <* char_ '`')))
|
||||
sqlServerQuotedIden =
|
||||
Identifier (Just ("[","]")) <$>
|
||||
(char_ '[' *>
|
||||
(failEmptyIden ']'
|
||||
<|> (takeWhileP Nothing (`notElemChar` "[]")
|
||||
<* choice [char_ ']'
|
||||
-- should probably do this error message as
|
||||
-- a proper unexpected message
|
||||
,failOnThis (char_ '[') "unexpected ["])))
|
||||
-- try is used here to avoid a conflict with identifiers
|
||||
-- and quoted strings which also start with a 'u'
|
||||
unicodeQuotedIden = Identifier
|
||||
<$> (f <$> try (oneOf "uU" <* string "&"))
|
||||
<*> qidenPart
|
||||
<*> qiden
|
||||
where f x = Just (T.cons x "&\"", "\"")
|
||||
qidenPart = char '"' *> qidenSuffix ""
|
||||
qidenSuffix t = do
|
||||
s <- takeWhileP Nothing (/='"')
|
||||
void $ char '"'
|
||||
-- deal with "" as literal double quote character
|
||||
choice [do
|
||||
void $ char '"'
|
||||
qidenSuffix $ T.concat [t,s,"\"\""]
|
||||
,pure $ T.concat [t,s]]
|
||||
qiden =
|
||||
char_ '"' *> (failEmptyIden '"' <|> fstMatch moreQIden <* char_ '"')
|
||||
moreQIden =
|
||||
label "\""
|
||||
(takeWhileP_ Nothing (/='"')
|
||||
*> hoptional_ (chunk "\"\"" *> moreQIden))
|
||||
|
||||
identifierString :: Parser Text
|
||||
identifierString = (do
|
||||
identifierString = label "identifier" $ do
|
||||
c <- satisfy isFirstLetter
|
||||
choice
|
||||
[T.cons c <$> takeWhileP (Just "identifier char") isIdentifierChar
|
||||
,pure $ T.singleton c]) <?> "identifier"
|
||||
[T.cons c <$> takeWhileP Nothing isIdentifierChar
|
||||
,pure $ T.singleton c]
|
||||
where
|
||||
isFirstLetter c = c == '_' || isAlpha c
|
||||
|
||||
|
@ -415,12 +479,11 @@ isIdentifierChar c = c == '_' || isAlphaNum c
|
|||
--------------------------------------
|
||||
|
||||
lineComment :: Dialect -> Parser Token
|
||||
lineComment _ = do
|
||||
try (string_ "--") <?> ""
|
||||
rest <- takeWhileP (Just "non newline character") (/='\n')
|
||||
lineComment _ = LineComment <$> fstMatch (do
|
||||
hidden (string_ "--")
|
||||
takeWhileP_ Nothing (/='\n')
|
||||
-- can you optionally read the \n to terminate the takewhilep without reparsing it?
|
||||
suf <- option "" ("\n" <$ char_ '\n')
|
||||
pure $ LineComment $ T.concat ["--", rest, suf]
|
||||
hoptional_ $ char_ '\n')
|
||||
|
||||
--------------------------------------
|
||||
|
||||
|
@ -428,28 +491,30 @@ lineComment _ = do
|
|||
-- I don't know any dialects that use this, but I think it's useful, if needed,
|
||||
-- add it back in under a dialect flag?
|
||||
blockComment :: Dialect -> Parser Token
|
||||
blockComment _ = (do
|
||||
try $ string_ "/*"
|
||||
BlockComment . T.concat . ("/*":) <$> more) <?> ""
|
||||
blockComment _ = BlockComment <$> fstMatch bc
|
||||
where
|
||||
more = choice
|
||||
[["*/"] <$ try (string_ "*/") -- comment ended
|
||||
,char_ '*' *> (("*":) <$> more) -- comment contains * but this isn't the comment end token
|
||||
-- not sure if there's an easy optimisation here
|
||||
,(:) <$> takeWhile1P (Just "non comment terminator text") (/= '*') <*> more]
|
||||
bc = chunk_ "/*" *> moreBlockChars
|
||||
regularBlockCommentChars = label "*/" $
|
||||
takeWhileP_ Nothing (\x -> x /= '*' && x /= '/')
|
||||
continueBlockComment = label "*/" (char_ '*' <|> char_ '/') *> moreBlockChars
|
||||
endComment = label "*/" $ chunk_ "*/"
|
||||
moreBlockChars = label "*/" $
|
||||
regularBlockCommentChars
|
||||
*> (endComment
|
||||
<|> (label "*/" bc *> moreBlockChars) -- nest
|
||||
<|> continueBlockComment)
|
||||
|
||||
{-
|
||||
This is to improve user experience: provide an error if we see */
|
||||
outside a comment. This could potentially break postgres ops with */
|
||||
in them (which is a stupid thing to do). In other cases, the user
|
||||
should write * / instead (I can't think of any cases when this would
|
||||
be valid syntax though).
|
||||
in them (it is not sensible to use operators that contain this as a
|
||||
substring). In other cases, the user should write * / instead (I can't
|
||||
think of any cases when this would be valid syntax).
|
||||
-}
|
||||
|
||||
dontParseEndBlockComment :: Dialect -> Parser Token
|
||||
dontParseEndBlockComment _ =
|
||||
-- don't use try, then it should commit to the error
|
||||
try (string "*/") *> fail "comment end without comment start"
|
||||
failOnThis (chunk_ "*/") "comment end without comment start"
|
||||
|
||||
--------------------------------------
|
||||
|
||||
|
@ -482,63 +547,51 @@ followed by an optional exponent
|
|||
|
||||
sqlNumber :: Dialect -> Parser Token
|
||||
sqlNumber d =
|
||||
SqlNumber <$> completeNumber
|
||||
-- this is for definitely avoiding possibly ambiguous source
|
||||
<* choice [-- special case to allow e.g. 1..2
|
||||
guard (diPostgresSymbols d)
|
||||
*> void (lookAhead $ try (string ".." <?> ""))
|
||||
<|> void (notFollowedBy (oneOf "eE."))
|
||||
,notFollowedBy (oneOf "eE.")
|
||||
]
|
||||
SqlNumber <$> fstMatch
|
||||
((numStartingWithDigits <|> numStartingWithDot)
|
||||
*> hoptional_ expo *> trailingCheck)
|
||||
where
|
||||
completeNumber =
|
||||
(digits <??> (pp dot <??.> pp digits)
|
||||
-- try is used in case we read a dot
|
||||
-- and it isn't part of a number
|
||||
-- if there are any following digits, then we commit
|
||||
-- to it being a number and not something else
|
||||
<|> try ((<>) <$> dot <*> digits))
|
||||
<??> pp expon
|
||||
|
||||
-- make sure we don't parse two adjacent dots in a number
|
||||
-- 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
|
||||
dot = let p = string "." <* notFollowedBy (char '.')
|
||||
in if diPostgresSymbols d
|
||||
then try p
|
||||
else p
|
||||
expon = T.cons <$> oneOf "eE" <*> sInt
|
||||
sInt = (<>) <$> option "" (T.singleton <$> oneOf "+-") <*> digits
|
||||
pp = (<$$> (<>))
|
||||
p <??> q = p <**> option id q
|
||||
pa <$$> c = pa <**> pure (flip c)
|
||||
pa <??.> pb =
|
||||
let c = (<$>) . flip
|
||||
in (.) `c` pa <*> option id pb
|
||||
numStartingWithDigits = digits_ *> hoptional_ (safeDot *> hoptional_ digits_)
|
||||
-- use try, so we don't commit to a number when there's a . with no following digit
|
||||
numStartingWithDot = try (safeDot *> digits_)
|
||||
expo = (char_ 'e' <|> char_ 'E') *> optional_ (char_ '-' <|> char_ '+') *> digits_
|
||||
digits_ = label "digits" $ takeWhile1P_ Nothing isDigit
|
||||
-- if there's a '..' next to the number, and it's a dialect that has .. as a
|
||||
-- lexical token, parse what we have so far and leave the dots in the chamber
|
||||
-- otherwise, give an error
|
||||
safeDot =
|
||||
if diPostgresSymbols d
|
||||
then try (char_ '.' <* notFollowedBy (char_ '.'))
|
||||
else char_ '.' <* notFollowedBy (char_ '.')
|
||||
-- additional check to give an error if the number is immediately
|
||||
-- followed by e, E or . with an exception for .. if this symbol is supported
|
||||
trailingCheck =
|
||||
if diPostgresSymbols d
|
||||
then -- special case to allow e.g. 1..2
|
||||
void (lookAhead $ hidden $ chunk_ "..")
|
||||
<|> void (notFollowedBy (oneOf "eE."))
|
||||
else notFollowedBy (oneOf "eE.")
|
||||
|
||||
digits :: Parser Text
|
||||
digits = takeWhile1P (Just "digit") isDigit
|
||||
digits = label "digits" $ takeWhile1P Nothing isDigit
|
||||
|
||||
--------------------------------------
|
||||
|
||||
positionalArg :: Dialect -> Parser Token
|
||||
positionalArg d =
|
||||
guard (diPositionalArg d) >>
|
||||
-- use try to avoid ambiguities with other syntax which starts with dollar
|
||||
PositionalArg <$> try (char_ '$' *> (read . T.unpack <$> digits))
|
||||
choice [PositionalArg <$>
|
||||
try (char_ '$' *> (read . T.unpack <$> digits)) | diPositionalArg d]
|
||||
|
||||
--------------------------------------
|
||||
|
||||
-- todo: I think the try here should read a prefix char, then a single valid
|
||||
-- identifier char, then commit
|
||||
prefixedVariable :: Dialect -> Parser Token
|
||||
prefixedVariable d = try $ choice
|
||||
[PrefixedVariable <$> char ':' <*> identifierString
|
||||
,guard (diAtIdentifier d) >>
|
||||
PrefixedVariable <$> char '@' <*> identifierString
|
||||
,guard (diHashIdentifier d) >>
|
||||
PrefixedVariable <$> char '#' <*> identifierString
|
||||
]
|
||||
prefixedVariable d = try $ choice $
|
||||
[PrefixedVariable <$> char ':' <*> identifierString]
|
||||
++ [PrefixedVariable <$> char '@' <*> identifierString | diAtIdentifier d]
|
||||
++ [PrefixedVariable <$> char '#' <*> identifierString | diHashIdentifier d]
|
||||
|
||||
--------------------------------------
|
||||
|
||||
|
@ -565,7 +618,7 @@ symbol d = Symbol <$> choice (concat
|
|||
else basicAnsiOps
|
||||
])
|
||||
where
|
||||
dots = [takeWhile1P (Just "dot") (=='.')]
|
||||
dots = [takeWhile1P Nothing (=='.')]
|
||||
odbcSymbol = [string "{", string "}"]
|
||||
postgresExtraSymbols =
|
||||
[try (string ":=")
|
||||
|
@ -670,7 +723,7 @@ generalizedPostgresqlOperator = [singlePlusMinus,opMoreChars]
|
|||
--------------------------------------
|
||||
|
||||
sqlWhitespace :: Dialect -> Parser Token
|
||||
sqlWhitespace _ = Whitespace <$> takeWhile1P (Just "whitespace") isSpace <?> ""
|
||||
sqlWhitespace _ = Whitespace <$> takeWhile1P Nothing isSpace
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
|
@ -679,6 +732,9 @@ sqlWhitespace _ = Whitespace <$> takeWhile1P (Just "whitespace") isSpace <?> ""
|
|||
char_ :: Char -> Parser ()
|
||||
char_ = void . char
|
||||
|
||||
hchar_ :: Char -> Parser ()
|
||||
hchar_ = void . hidden . char
|
||||
|
||||
string_ :: Text -> Parser ()
|
||||
string_ = void . string
|
||||
|
||||
|
@ -688,6 +744,39 @@ oneOf = M.oneOf
|
|||
notElemChar :: Char -> [Char] -> Bool
|
||||
notElemChar a b = a `notElem` (b :: [Char])
|
||||
|
||||
fstMatch :: Parser () -> Parser Text
|
||||
fstMatch x = fst <$> match x
|
||||
|
||||
hoptional_ :: Parser a -> Parser ()
|
||||
hoptional_ = void . hoptional
|
||||
|
||||
hoptional :: Parser a -> Parser (Maybe a)
|
||||
hoptional = hidden . optional
|
||||
|
||||
optional_ :: Parser a -> Parser ()
|
||||
optional_ = void . optional
|
||||
|
||||
--hoption :: a -> Parser a -> Parser a
|
||||
--hoption a p = hidden $ option a p
|
||||
|
||||
takeWhileP_ :: Maybe String -> (Char -> Bool) -> Parser ()
|
||||
takeWhileP_ m p = void $ takeWhileP m p
|
||||
|
||||
takeWhile1P_ :: Maybe String -> (Char -> Bool) -> Parser ()
|
||||
takeWhile1P_ m p = void $ takeWhile1P m p
|
||||
|
||||
chunk_ :: Text -> Parser ()
|
||||
chunk_ = void . chunk
|
||||
|
||||
hchunk_ :: Text -> Parser ()
|
||||
hchunk_ = void . hidden . chunk
|
||||
|
||||
failOnThis :: Parser () -> Text -> Parser a
|
||||
failOnThis p msg = do
|
||||
o <- getOffset
|
||||
hidden p
|
||||
region (setErrorOffset o) $ fail $ T.unpack msg
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
|
|
@ -195,8 +195,8 @@ import Text.Megaparsec
|
|||
|
||||
,ParseErrorBundle(..)
|
||||
,errorBundlePretty
|
||||
,hidden
|
||||
|
||||
,(<?>)
|
||||
,(<|>)
|
||||
,token
|
||||
,choice
|
||||
|
@ -212,6 +212,7 @@ import Text.Megaparsec
|
|||
)
|
||||
import qualified Control.Monad.Combinators.Expr as E
|
||||
import qualified Control.Monad.Permutations as P
|
||||
import qualified Text.Megaparsec as M
|
||||
|
||||
import Control.Monad.Reader
|
||||
(Reader
|
||||
|
@ -235,6 +236,8 @@ import qualified Data.Text as T
|
|||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.Dialect
|
||||
import qualified Language.SQL.SimpleSQL.Lex as L
|
||||
--import Text.Megaparsec.Debug (dbg)
|
||||
import Text.Read (readMaybe)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
|
@ -324,9 +327,9 @@ wrapParse :: Parser a
|
|||
-> Text
|
||||
-> Either ParseError a
|
||||
wrapParse parser d f p src = do
|
||||
lx <- either (Left . LexError) Right $ L.lexSQLWithPositions d f p src
|
||||
lx <- either (Left . LexError) Right $ L.lexSQLWithPositions d True f p src
|
||||
either (Left . ParseError) Right $
|
||||
runReader (runParserT (parser <* (eof <?> "")) (T.unpack f)
|
||||
runReader (runParserT (parser <* (hidden eof)) (T.unpack f)
|
||||
$ L.SQLStream (T.unpack src) $ filter notSpace lx) d
|
||||
where
|
||||
notSpace = notSpace' . L.tokenVal
|
||||
|
@ -379,20 +382,20 @@ u&"example quoted"
|
|||
-}
|
||||
|
||||
name :: Parser Name
|
||||
name = do
|
||||
name = label "name" $ do
|
||||
bl <- askDialect diKeywords
|
||||
uncurry Name <$> identifierTok bl
|
||||
|
||||
-- todo: replace (:[]) with a named function all over
|
||||
|
||||
names :: Parser [Name]
|
||||
names = reverse <$> (((:[]) <$> name) <??*> anotherName)
|
||||
names = label "name" (reverse <$> (((:[]) <$> name) <??*> anotherName))
|
||||
-- can't use a simple chain here since we
|
||||
-- want to wrap the . + name in a try
|
||||
-- this will change when this is left factored
|
||||
where
|
||||
anotherName :: Parser ([Name] -> [Name])
|
||||
anotherName = try ((:) <$> ((symbol "." *> name) <?> ""))
|
||||
anotherName = try ((:) <$> (hidden (symbol "." *> name)))
|
||||
|
||||
{-
|
||||
= Type Names
|
||||
|
@ -501,36 +504,48 @@ a lob type name.
|
|||
|
||||
Unfortunately, to improve the error messages, there is a lot of (left)
|
||||
factoring in this function, and it is a little dense.
|
||||
|
||||
the hideArg is used when the typename is used as part of a typed
|
||||
literal expression, to hide what comes after the paren in
|
||||
'typename('. This is so 'arbitrary_fn(' gives an 'expecting expression',
|
||||
instead of 'expecting expression or number', which is odd.
|
||||
|
||||
-}
|
||||
|
||||
typeName :: Parser TypeName
|
||||
typeName =
|
||||
(rowTypeName <|> intervalTypeName <|> otherTypeName)
|
||||
<??*> tnSuffix
|
||||
typeName = typeName' False
|
||||
|
||||
typeName' :: Bool -> Parser TypeName
|
||||
typeName' hideArg =
|
||||
label "typename" (
|
||||
(rowTypeName <|> intervalTypeName <|> otherTypeName)
|
||||
<??*> tnSuffix)
|
||||
where
|
||||
rowTypeName =
|
||||
RowTypeName <$> (keyword_ "row" *> parens (commaSep1 rowField))
|
||||
RowTypeName <$> (hidden (keyword_ "row") *> parens (commaSep1 rowField))
|
||||
rowField = (,) <$> name <*> typeName
|
||||
----------------------------
|
||||
intervalTypeName =
|
||||
keyword_ "interval" *>
|
||||
hidden (keyword_ "interval") *>
|
||||
(uncurry IntervalTypeName <$> intervalQualifier)
|
||||
----------------------------
|
||||
otherTypeName =
|
||||
nameOfType <**>
|
||||
(typeNameWithParens
|
||||
<|> pure Nothing <**> (timeTypeName <|> charTypeName)
|
||||
<|> pure Nothing <**> (hidden timeTypeName <|> hidden charTypeName)
|
||||
<|> pure TypeName)
|
||||
nameOfType = reservedTypeNames <|> names
|
||||
charTypeName = charSet <**> (option [] tcollate <$$$$> CharTypeName)
|
||||
<|> pure [] <**> (tcollate <$$$$> CharTypeName)
|
||||
typeNameWithParens =
|
||||
(openParen *> unsignedInteger)
|
||||
<**> (closeParen *> precMaybeSuffix
|
||||
<|> (precScaleTypeName <|> precLengthTypeName) <* closeParen)
|
||||
(hidden openParen *> (if hideArg then hidden unsignedInteger else unsignedInteger))
|
||||
<**> (closeParen *> hidden precMaybeSuffix
|
||||
<|> hidden (precScaleTypeName <|> precLengthTypeName) <* closeParen)
|
||||
precMaybeSuffix = (. Just) <$> (timeTypeName <|> charTypeName)
|
||||
<|> pure (flip PrecTypeName)
|
||||
precScaleTypeName = (comma *> unsignedInteger) <$$$> PrecScaleTypeName
|
||||
precScaleTypeName =
|
||||
(hidden comma *> (if hideArg then hidden unsignedInteger else unsignedInteger))
|
||||
<$$$> PrecScaleTypeName
|
||||
precLengthTypeName =
|
||||
Just <$> lobPrecSuffix
|
||||
<**> (optional lobUnits <$$$$> PrecLengthTypeName)
|
||||
|
@ -609,7 +624,7 @@ parameter = choice
|
|||
[Parameter <$ questionMark
|
||||
,HostParameter
|
||||
<$> hostParamTok
|
||||
<*> optional (keyword "indicator" *> hostParamTok)]
|
||||
<*> hoptional (keyword "indicator" *> hostParamTok)]
|
||||
|
||||
-- == positional arg
|
||||
|
||||
|
@ -734,11 +749,12 @@ this. also fix the monad -> applicative
|
|||
-}
|
||||
|
||||
intervalLit :: Parser ScalarExpr
|
||||
intervalLit = try (keyword_ "interval" >> do
|
||||
s <- optional $ choice [Plus <$ symbol_ "+"
|
||||
,Minus <$ symbol_ "-"]
|
||||
intervalLit =
|
||||
label "interval literal" $ try (keyword_ "interval" >> do
|
||||
s <- hoptional $ choice [Plus <$ symbol_ "+"
|
||||
,Minus <$ symbol_ "-"]
|
||||
lit <- singleQuotesOnlyStringTok
|
||||
q <- optional intervalQualifier
|
||||
q <- hoptional intervalQualifier
|
||||
mkIt s lit q)
|
||||
where
|
||||
mkIt Nothing val Nothing = pure $ TypedLit (TypeName [Name Nothing "interval"]) val
|
||||
|
@ -764,23 +780,41 @@ all the scalar expressions which start with an identifier
|
|||
|
||||
idenExpr :: Parser ScalarExpr
|
||||
idenExpr =
|
||||
-- todo: work out how to left factor this
|
||||
try (TypedLit <$> typeName <*> singleQuotesOnlyStringTok)
|
||||
<|> (names <**> option Iden app)
|
||||
<|> keywordFunctionOrIden
|
||||
-- todo: try reversing these
|
||||
-- then if it parses as a typename as part of a typed literal
|
||||
-- and not a regularapplike, then you'll get a better error message
|
||||
try typedLiteral <|> regularAppLike
|
||||
where
|
||||
-- special cases for keywords that can be parsed as an iden or app
|
||||
keywordFunctionOrIden = try $ do
|
||||
x <- unquotedIdentifierTok [] Nothing
|
||||
-- parse regular iden or app
|
||||
-- if it could potentially be a typed literal typename 'literaltext'
|
||||
-- optionally try to parse that
|
||||
regularAppLike = do
|
||||
e <- (keywordFunctionOrIden <|> (names <**> hoption Iden app))
|
||||
let getInt s = readMaybe (T.unpack s)
|
||||
case e of
|
||||
Iden nm -> tryTypedLiteral (TypeName nm) <|> pure e
|
||||
App nm [NumLit prec]
|
||||
| Just prec' <- getInt prec ->
|
||||
tryTypedLiteral (PrecTypeName nm prec') <|> pure e
|
||||
App nm [NumLit prec,NumLit scale]
|
||||
| Just prec' <- getInt prec
|
||||
, Just scale' <- getInt scale ->
|
||||
tryTypedLiteral (PrecScaleTypeName nm prec' scale') <|> pure e
|
||||
_ -> pure e
|
||||
tryTypedLiteral tn =
|
||||
TypedLit tn <$> hidden singleQuotesOnlyStringTok
|
||||
typedLiteral = TypedLit <$> hidden (typeName' True) <*> singleQuotesOnlyStringTok
|
||||
keywordFunctionOrIden = do
|
||||
d <- askDialect id
|
||||
x <- hidden (keywordTok (diIdentifierKeywords d ++ diAppKeywords d))
|
||||
let i = T.toLower x `elem` diIdentifierKeywords d
|
||||
a = T.toLower x `elem` diAppKeywords d
|
||||
case () of
|
||||
_ | i && a -> pure [Name Nothing x] <**> option Iden app
|
||||
| i -> pure (Iden [Name Nothing x])
|
||||
| a -> pure [Name Nothing x] <**> app
|
||||
| otherwise -> fail ""
|
||||
|
||||
_ | i && a -> pure [Name Nothing x] <**> hoption Iden app
|
||||
| i -> pure (Iden [Name Nothing x])
|
||||
| a -> pure [Name Nothing x] <**> app
|
||||
| otherwise -> -- shouldn't get here
|
||||
fail $ "unexpected keyword: " <> T.unpack x
|
||||
|
||||
{-
|
||||
=== special
|
||||
|
@ -814,7 +848,7 @@ specialOpK opName firstArg kws =
|
|||
case (e,kws) of
|
||||
(Iden [Name Nothing i], (k,_):_)
|
||||
| T.toLower i == k ->
|
||||
fail $ "cannot use keyword here: " ++ T.unpack i
|
||||
fail $ "unexpected " ++ T.unpack i
|
||||
_ -> pure ()
|
||||
pure e
|
||||
fa <- case firstArg of
|
||||
|
@ -921,24 +955,24 @@ together.
|
|||
|
||||
app :: Parser ([Name] -> ScalarExpr)
|
||||
app =
|
||||
openParen *> choice
|
||||
[duplicates
|
||||
hidden openParen *> choice
|
||||
[hidden duplicates
|
||||
<**> (commaSep1 scalarExpr
|
||||
<**> ((option [] orderBy <* closeParen)
|
||||
<**> (optional afilter <$$$$$> AggregateApp)))
|
||||
<**> ((hoption [] orderBy <* closeParen)
|
||||
<**> (hoptional afilter <$$$$$> AggregateApp)))
|
||||
-- separate cases with no all or distinct which must have at
|
||||
-- least one scalar expr
|
||||
,commaSep1 scalarExpr
|
||||
<**> choice
|
||||
[closeParen *> choice
|
||||
[closeParen *> hidden (choice
|
||||
[window
|
||||
,withinGroup
|
||||
,(Just <$> afilter) <$$$> aggAppWithoutDupeOrd
|
||||
,pure (flip App)]
|
||||
,orderBy <* closeParen
|
||||
<**> (optional afilter <$$$$> aggAppWithoutDupe)]
|
||||
,pure (flip App)])
|
||||
,hidden orderBy <* closeParen
|
||||
<**> (hoptional afilter <$$$$> aggAppWithoutDupe)]
|
||||
-- no scalarExprs: duplicates and order by not allowed
|
||||
,([] <$ closeParen) <**> option (flip App) (window <|> withinGroup)
|
||||
,([] <$ closeParen) <**> hoption (flip App) (window <|> withinGroup)
|
||||
]
|
||||
where
|
||||
aggAppWithoutDupeOrd n es f = AggregateApp n SQDefault es [] f
|
||||
|
@ -970,8 +1004,11 @@ window =
|
|||
<**> (option [] orderBy
|
||||
<**> ((optional frameClause <* closeParen) <$$$$$> WindowApp))
|
||||
where
|
||||
partitionBy = keywords_ ["partition","by"] *> commaSep1 scalarExpr
|
||||
partitionBy =
|
||||
label "partition by" $
|
||||
keywords_ ["partition","by"] *> commaSep1 scalarExpr
|
||||
frameClause =
|
||||
label "frame clause" $
|
||||
frameRowsRange -- TODO: this 'and' could be an issue
|
||||
<**> choice [(keyword_ "between" *> frameLimit True)
|
||||
<**> ((keyword_ "and" *> frameLimit True)
|
||||
|
@ -1128,8 +1165,8 @@ scalar expressions (the other is a variation on joins)
|
|||
|
||||
|
||||
odbcExpr :: Parser ScalarExpr
|
||||
odbcExpr = between (symbol "{") (symbol "}")
|
||||
(odbcTimeLit <|> odbcFunc)
|
||||
odbcExpr =
|
||||
braces (odbcTimeLit <|> odbcFunc)
|
||||
where
|
||||
odbcTimeLit =
|
||||
OdbcLiteral <$> choice [OLDate <$ keyword "d"
|
||||
|
@ -1232,33 +1269,33 @@ opTable bExpr =
|
|||
|
||||
]
|
||||
where
|
||||
binarySymL nm = E.InfixL (mkBinOp nm <$ symbol_ nm)
|
||||
binarySymR nm = E.InfixR (mkBinOp nm <$ symbol_ nm)
|
||||
binarySymN nm = E.InfixN (mkBinOp nm <$ symbol_ nm)
|
||||
binaryKeywordN nm = E.InfixN (mkBinOp nm <$ keyword_ nm)
|
||||
binaryKeywordL nm = E.InfixL (mkBinOp nm <$ keyword_ nm)
|
||||
binarySymL nm = E.InfixL (hidden $ mkBinOp nm <$ symbol_ nm)
|
||||
binarySymR nm = E.InfixR (hidden $ mkBinOp nm <$ symbol_ nm)
|
||||
binarySymN nm = E.InfixN (hidden $ mkBinOp nm <$ symbol_ nm)
|
||||
binaryKeywordN nm = E.InfixN (hidden $ mkBinOp nm <$ keyword_ nm)
|
||||
binaryKeywordL nm = E.InfixL (hidden $ mkBinOp nm <$ keyword_ nm)
|
||||
mkBinOp nm a b = BinOp a (mkNm nm) b
|
||||
prefixSym nm = prefix (PrefixOp (mkNm nm) <$ symbol_ nm)
|
||||
prefixKeyword nm = prefix (PrefixOp (mkNm nm) <$ keyword_ nm)
|
||||
prefixSym nm = prefix (hidden $ PrefixOp (mkNm nm) <$ symbol_ nm)
|
||||
prefixKeyword nm = prefix (hidden $ PrefixOp (mkNm nm) <$ keyword_ nm)
|
||||
mkNm nm = [Name Nothing nm]
|
||||
binaryKeywordsN p =
|
||||
E.InfixN (do
|
||||
E.InfixN (hidden $ do
|
||||
o <- try p
|
||||
pure (\a b -> BinOp a [Name Nothing $ T.unwords o] b))
|
||||
multisetBinOp = E.InfixL (do
|
||||
multisetBinOp = E.InfixL (hidden $ do
|
||||
keyword_ "multiset"
|
||||
o <- choice [Union <$ keyword_ "union"
|
||||
,Intersect <$ keyword_ "intersect"
|
||||
,Except <$ keyword_ "except"]
|
||||
d <- option SQDefault duplicates
|
||||
d <- hoption SQDefault duplicates
|
||||
pure (\a b -> MultisetBinOp a o d b))
|
||||
postfixKeywords p =
|
||||
postfix $ do
|
||||
postfix $ hidden $ do
|
||||
o <- try p
|
||||
pure $ PostfixOp [Name Nothing $ T.unwords o]
|
||||
-- parse repeated prefix or postfix operators
|
||||
postfix p = E.Postfix $ foldr1 (flip (.)) <$> some p
|
||||
prefix p = E.Prefix $ foldr1 (.) <$> some p
|
||||
postfix p = E.Postfix $ foldr1 (flip (.)) <$> some (hidden p)
|
||||
prefix p = E.Prefix $ foldr1 (.) <$> some (hidden p)
|
||||
|
||||
{-
|
||||
== scalar expression top level
|
||||
|
@ -1271,31 +1308,32 @@ documenting/fixing.
|
|||
-}
|
||||
|
||||
scalarExpr :: Parser ScalarExpr
|
||||
scalarExpr = E.makeExprParser term (opTable False)
|
||||
scalarExpr = label "expression" $ E.makeExprParser term (opTable False)
|
||||
|
||||
term :: Parser ScalarExpr
|
||||
term = choice [simpleLiteral
|
||||
,parameter
|
||||
,positionalArg
|
||||
,star
|
||||
,parensExpr
|
||||
,caseExpr
|
||||
,cast
|
||||
,convertSqlServer
|
||||
,arrayCtor
|
||||
,multisetCtor
|
||||
,nextValueFor
|
||||
,subquery
|
||||
,intervalLit
|
||||
,specialOpKs
|
||||
,idenExpr
|
||||
,odbcExpr]
|
||||
<?> "scalar expression"
|
||||
term = label "expression" $
|
||||
choice
|
||||
[simpleLiteral
|
||||
,parameter
|
||||
,positionalArg
|
||||
,star
|
||||
,parensExpr
|
||||
,caseExpr
|
||||
,cast
|
||||
,convertSqlServer
|
||||
,arrayCtor
|
||||
,multisetCtor
|
||||
,nextValueFor
|
||||
,subquery
|
||||
,intervalLit
|
||||
,specialOpKs
|
||||
,idenExpr
|
||||
,odbcExpr]
|
||||
|
||||
-- expose the b expression for window frame clause range between
|
||||
|
||||
scalarExprB :: Parser ScalarExpr
|
||||
scalarExprB = E.makeExprParser term (opTable True)
|
||||
scalarExprB = label "expression" $ E.makeExprParser term (opTable True)
|
||||
|
||||
{-
|
||||
== helper parsers
|
||||
|
@ -1321,9 +1359,10 @@ use a data type for the datetime field?
|
|||
-}
|
||||
|
||||
datetimeField :: Parser Text
|
||||
datetimeField = choice (map keyword ["year","month","day"
|
||||
,"hour","minute","second"])
|
||||
<?> "datetime field"
|
||||
datetimeField =
|
||||
choice (map keyword ["year","month","day"
|
||||
,"hour","minute","second"])
|
||||
<?> "datetime field"
|
||||
|
||||
{-
|
||||
This is used in multiset operations (scalar expr), selects (query expr)
|
||||
|
@ -1344,8 +1383,8 @@ duplicates =
|
|||
-}
|
||||
|
||||
selectItem :: Parser (ScalarExpr,Maybe Name)
|
||||
selectItem = (,) <$> scalarExpr <*> optional als
|
||||
where als = optional (keyword_ "as") *> name
|
||||
selectItem = label "select item" ((,) <$> scalarExpr <*> optional als)
|
||||
where als = label "alias" $ optional (keyword_ "as") *> name
|
||||
|
||||
selectList :: Parser [(ScalarExpr,Maybe Name)]
|
||||
selectList = commaSep1 selectItem
|
||||
|
@ -1366,33 +1405,33 @@ aliases.
|
|||
-}
|
||||
|
||||
from :: Parser [TableRef]
|
||||
from = keyword_ "from" *> commaSep1 tref
|
||||
from = label "from" (keyword_ "from" *> commaSep1 tref)
|
||||
where
|
||||
-- TODO: use P (a->) for the join tref suffix
|
||||
-- chainl or buildexpressionparser
|
||||
tref = (nonJoinTref <?> "table ref") >>= optionSuffix joinTrefSuffix
|
||||
tref = (nonJoinTref <?> "table ref") >>= hoptionSuffix joinTrefSuffix
|
||||
nonJoinTref = choice
|
||||
[parens $ choice
|
||||
[hidden $ parens $ choice
|
||||
[TRQueryExpr <$> queryExpr
|
||||
,TRParens <$> tref]
|
||||
,TRLateral <$> (keyword_ "lateral"
|
||||
,TRLateral <$> (hidden (keyword_ "lateral")
|
||||
*> nonJoinTref)
|
||||
,do
|
||||
n <- names
|
||||
choice [TRFunction n
|
||||
<$> parens (commaSep scalarExpr)
|
||||
<$> hidden (parens (commaSep scalarExpr))
|
||||
,pure $ TRSimple n]
|
||||
-- todo: I think you can only have outer joins inside the oj,
|
||||
-- not sure.
|
||||
,TROdbc <$> (symbol "{" *> keyword_ "oj" *> tref <* symbol "}")
|
||||
,TROdbc <$> (hidden (braces (keyword_ "oj" *> tref)))
|
||||
] <??> aliasSuffix
|
||||
aliasSuffix = fromAlias <$$> TRAlias
|
||||
aliasSuffix = hidden (fromAlias <$$> TRAlias)
|
||||
joinTrefSuffix t =
|
||||
((TRJoin t <$> option False (True <$ keyword_ "natural")
|
||||
<*> joinType
|
||||
<*> nonJoinTref
|
||||
<*> optional joinCondition)
|
||||
>>= optionSuffix joinTrefSuffix) <?> ""
|
||||
<*> hoptional joinCondition)
|
||||
>>= hoptionSuffix joinTrefSuffix)
|
||||
|
||||
{-
|
||||
TODO: factor the join stuff to produce better error messages (and make
|
||||
|
@ -1422,8 +1461,8 @@ joinCondition = choice
|
|||
fromAlias :: Parser Alias
|
||||
fromAlias = Alias <$> tableAlias <*> columnAliases
|
||||
where
|
||||
tableAlias = optional (keyword_ "as") *> name
|
||||
columnAliases = optional $ parens $ commaSep1 name
|
||||
tableAlias = hoptional (keyword_ "as") *> name
|
||||
columnAliases = hoptional $ parens $ commaSep1 name
|
||||
|
||||
{-
|
||||
== simple other parts
|
||||
|
@ -1433,10 +1472,11 @@ pretty trivial.
|
|||
-}
|
||||
|
||||
whereClause :: Parser ScalarExpr
|
||||
whereClause = keyword_ "where" *> scalarExpr
|
||||
whereClause = label "where" (keyword_ "where" *> scalarExpr)
|
||||
|
||||
groupByClause :: Parser [GroupingExpr]
|
||||
groupByClause = keywords_ ["group","by"] *> commaSep1 groupingExpression
|
||||
groupByClause =
|
||||
label "group by" (keywords_ ["group","by"] *> commaSep1 groupingExpression)
|
||||
where
|
||||
groupingExpression = choice
|
||||
[keyword_ "cube" >>
|
||||
|
@ -1450,16 +1490,16 @@ groupByClause = keywords_ ["group","by"] *> commaSep1 groupingExpression
|
|||
]
|
||||
|
||||
having :: Parser ScalarExpr
|
||||
having = keyword_ "having" *> scalarExpr
|
||||
having = label "having" (keyword_ "having" *> scalarExpr)
|
||||
|
||||
orderBy :: Parser [SortSpec]
|
||||
orderBy = keywords_ ["order","by"] *> commaSep1 ob
|
||||
orderBy = label "order by" (keywords_ ["order","by"] *> commaSep1 ob)
|
||||
where
|
||||
ob = SortSpec
|
||||
<$> scalarExpr
|
||||
<*> option DirDefault (choice [Asc <$ keyword_ "asc"
|
||||
<*> hoption DirDefault (choice [Asc <$ keyword_ "asc"
|
||||
,Desc <$ keyword_ "desc"])
|
||||
<*> option NullsOrderDefault
|
||||
<*> hoption NullsOrderDefault
|
||||
-- todo: left factor better
|
||||
(keyword_ "nulls" >>
|
||||
choice [NullsFirst <$ keyword "first"
|
||||
|
@ -1477,9 +1517,9 @@ offsetFetch =
|
|||
maybePermutation p = P.toPermutationWithDefault Nothing (Just <$> p)
|
||||
|
||||
offset :: Parser ScalarExpr
|
||||
offset = keyword_ "offset" *> scalarExpr
|
||||
offset = label "offset" (keyword_ "offset" *> scalarExpr
|
||||
<* option () (choice [keyword_ "rows"
|
||||
,keyword_ "row"])
|
||||
,keyword_ "row"]))
|
||||
|
||||
fetch :: Parser ScalarExpr
|
||||
fetch = fetchFirst <|> limit
|
||||
|
@ -1496,13 +1536,13 @@ fetch = fetchFirst <|> limit
|
|||
|
||||
with :: Parser QueryExpr
|
||||
with = keyword_ "with" >>
|
||||
With <$> option False (True <$ keyword_ "recursive")
|
||||
With <$> hoption False (True <$ keyword_ "recursive")
|
||||
<*> commaSep1 withQuery <*> queryExpr
|
||||
where
|
||||
withQuery = (,) <$> (withAlias <* keyword_ "as")
|
||||
<*> parens queryExpr
|
||||
withAlias = Alias <$> name <*> columnAliases
|
||||
columnAliases = optional $ parens $ commaSep1 name
|
||||
columnAliases = hoptional $ parens $ commaSep1 name
|
||||
|
||||
|
||||
{-
|
||||
|
@ -1513,15 +1553,15 @@ and union, etc..
|
|||
-}
|
||||
|
||||
queryExpr :: Parser QueryExpr
|
||||
queryExpr = E.makeExprParser qeterm qeOpTable
|
||||
queryExpr = label "query expr" $ E.makeExprParser qeterm qeOpTable
|
||||
where
|
||||
qeterm = with <|> select <|> table <|> values
|
||||
qeterm = label "query expr" (with <|> select <|> table <|> values)
|
||||
|
||||
select = keyword_ "select" >>
|
||||
mkSelect
|
||||
<$> option SQDefault duplicates
|
||||
<$> hoption SQDefault duplicates
|
||||
<*> selectList
|
||||
<*> optional tableExpression <?> "table expression"
|
||||
<*> optional tableExpression
|
||||
mkSelect d sl Nothing =
|
||||
toQueryExpr $ makeSelect {msSetQuantifier = d, msSelectList = sl}
|
||||
mkSelect d sl (Just (TableExpression f w g h od ofs fe)) =
|
||||
|
@ -1535,12 +1575,12 @@ queryExpr = E.makeExprParser qeterm qeOpTable
|
|||
,[E.InfixL $ setOp Except "except"
|
||||
,E.InfixL $ setOp Union "union"]]
|
||||
setOp :: SetOperatorName -> Text -> Parser (QueryExpr -> QueryExpr -> QueryExpr)
|
||||
setOp ctor opName = (cq
|
||||
setOp ctor opName = hidden (cq
|
||||
<$> (ctor <$ keyword_ opName)
|
||||
<*> option SQDefault duplicates
|
||||
<*> corr) <?> ""
|
||||
<*> hoption SQDefault duplicates
|
||||
<*> corr)
|
||||
cq o d c q0 q1 = QueryExprSetOp q0 o d c q1
|
||||
corr = option Respectively (Corresponding <$ keyword_ "corresponding")
|
||||
corr = hoption Respectively (Corresponding <$ keyword_ "corresponding")
|
||||
|
||||
|
||||
{-
|
||||
|
@ -1560,12 +1600,15 @@ data TableExpression
|
|||
,_teFetchFirst :: Maybe ScalarExpr}
|
||||
|
||||
tableExpression :: Parser TableExpression
|
||||
tableExpression = mkTe <$> (from <?> "from clause")
|
||||
<*> (optional whereClause <?> "where clause")
|
||||
<*> (option [] groupByClause <?> "group by clause")
|
||||
<*> (optional having <?> "having clause")
|
||||
<*> (option [] orderBy <?> "order by clause")
|
||||
<*> (offsetFetch <?> "")
|
||||
tableExpression =
|
||||
label "from" $
|
||||
mkTe
|
||||
<$> from
|
||||
<*> optional whereClause
|
||||
<*> option [] groupByClause
|
||||
<*> optional having
|
||||
<*> option [] orderBy
|
||||
<*> (hidden offsetFetch)
|
||||
where
|
||||
mkTe f w g h od (ofs,fe) =
|
||||
TableExpression f w g h od ofs fe
|
||||
|
@ -1589,7 +1632,8 @@ topLevelStatement = statement
|
|||
-}
|
||||
|
||||
statementWithoutSemicolon :: Parser Statement
|
||||
statementWithoutSemicolon = choice
|
||||
statementWithoutSemicolon =
|
||||
label "statement" $ choice
|
||||
[keyword_ "create" *> choice [createSchema
|
||||
,createTable
|
||||
,createIndex
|
||||
|
@ -1623,7 +1667,7 @@ statementWithoutSemicolon = choice
|
|||
]
|
||||
|
||||
statement :: Parser Statement
|
||||
statement = statementWithoutSemicolon <* optional semi <|> EmptyStatement <$ semi
|
||||
statement = statementWithoutSemicolon <* optional semi <|> EmptyStatement <$ hidden semi
|
||||
|
||||
createSchema :: Parser Statement
|
||||
createSchema = keyword_ "schema" >>
|
||||
|
@ -1638,7 +1682,7 @@ createTable = do
|
|||
separator = if diNonCommaSeparatedConstraints d
|
||||
then optional comma
|
||||
else Just <$> comma
|
||||
constraints = sepBy parseConstraintDef separator
|
||||
constraints = sepBy parseConstraintDef (hidden separator)
|
||||
entries = ((:) <$> parseColumnDef <*> ((comma >> entries) <|> pure [])) <|> constraints
|
||||
|
||||
keyword_ "table" >>
|
||||
|
@ -1660,7 +1704,7 @@ columnDef = ColumnDef <$> name <*> typeName
|
|||
<*> optional defaultClause
|
||||
<*> option [] (some colConstraintDef)
|
||||
where
|
||||
defaultClause = choice [
|
||||
defaultClause = label "column default clause" $ choice [
|
||||
keyword_ "default" >>
|
||||
DefaultClause <$> scalarExpr
|
||||
-- todo: left factor
|
||||
|
@ -1689,12 +1733,12 @@ tableConstraintDef =
|
|||
(\cs ft ftcs m (u,d) -> TableReferencesConstraint cs ft ftcs m u d)
|
||||
<$> parens (commaSep1 name)
|
||||
<*> (keyword_ "references" *> names)
|
||||
<*> optional (parens $ commaSep1 name)
|
||||
<*> hoptional (parens $ commaSep1 name)
|
||||
<*> refMatch
|
||||
<*> refActions
|
||||
|
||||
refMatch :: Parser ReferenceMatch
|
||||
refMatch = option DefaultReferenceMatch
|
||||
refMatch = hoption DefaultReferenceMatch
|
||||
(keyword_ "match" *>
|
||||
choice [MatchFull <$ keyword_ "full"
|
||||
,MatchPartial <$ keyword_ "partial"
|
||||
|
@ -1833,11 +1877,11 @@ dropTable = keyword_ "table" >>
|
|||
createView :: Parser Statement
|
||||
createView =
|
||||
CreateView
|
||||
<$> (option False (True <$ keyword_ "recursive") <* keyword_ "view")
|
||||
<$> (hoption False (True <$ keyword_ "recursive") <* keyword_ "view")
|
||||
<*> names
|
||||
<*> optional (parens (commaSep1 name))
|
||||
<*> (keyword_ "as" *> queryExpr)
|
||||
<*> optional (choice [
|
||||
<*> hoptional (choice [
|
||||
-- todo: left factor
|
||||
DefaultCheckOption <$ try (keywords_ ["with", "check", "option"])
|
||||
,CascadedCheckOption <$ try (keywords_ ["with", "cascaded", "check", "option"])
|
||||
|
@ -1852,7 +1896,7 @@ createDomain :: Parser Statement
|
|||
createDomain = keyword_ "domain" >>
|
||||
CreateDomain
|
||||
<$> names
|
||||
<*> (optional (keyword_ "as") *> typeName)
|
||||
<*> ((optional (keyword_ "as") *> typeName) <?> "alias")
|
||||
<*> optional (keyword_ "default" *> scalarExpr)
|
||||
<*> many con
|
||||
where
|
||||
|
@ -1930,7 +1974,7 @@ insert :: Parser Statement
|
|||
insert = keywords_ ["insert", "into"] >>
|
||||
Insert
|
||||
<$> names
|
||||
<*> optional (parens $ commaSep1 name)
|
||||
<*> label "parens column names" (optional (parens $ commaSep1 name))
|
||||
<*> (DefaultInsertValues <$ keywords_ ["default", "values"]
|
||||
<|> InsertQuery <$> queryExpr)
|
||||
|
||||
|
@ -1938,7 +1982,7 @@ update :: Parser Statement
|
|||
update = keywords_ ["update"] >>
|
||||
Update
|
||||
<$> names
|
||||
<*> optional (optional (keyword_ "as") *> name)
|
||||
<*> label "alias" (optional (optional (keyword_ "as") *> name))
|
||||
<*> (keyword_ "set" *> commaSep1 setClause)
|
||||
<*> optional (keyword_ "where" *> scalarExpr)
|
||||
where
|
||||
|
@ -1974,10 +2018,10 @@ releaseSavepoint = keywords_ ["release","savepoint"] >>
|
|||
ReleaseSavepoint <$> name
|
||||
|
||||
commit :: Parser Statement
|
||||
commit = Commit <$ keyword_ "commit" <* optional (keyword_ "work")
|
||||
commit = Commit <$ keyword_ "commit" <* hoptional (keyword_ "work")
|
||||
|
||||
rollback :: Parser Statement
|
||||
rollback = keyword_ "rollback" >> optional (keyword_ "work") >>
|
||||
rollback = keyword_ "rollback" >> hoptional (keyword_ "work") >>
|
||||
Rollback <$> optional (keywords_ ["to", "savepoint"] *> name)
|
||||
|
||||
|
||||
|
@ -2091,6 +2135,7 @@ thick.
|
|||
|
||||
makeKeywordTree :: [Text] -> Parser [Text]
|
||||
makeKeywordTree sets =
|
||||
label (T.intercalate ", " sets) $
|
||||
parseTrees (sort $ map T.words sets)
|
||||
where
|
||||
parseTrees :: [[Text]] -> Parser [Text]
|
||||
|
@ -2116,24 +2161,20 @@ makeKeywordTree sets =
|
|||
|
||||
-- parser helpers
|
||||
|
||||
(<$$>) :: Applicative f =>
|
||||
f b -> (a -> b -> c) -> f (a -> c)
|
||||
(<$$>) :: Parser b -> (a -> b -> c) -> Parser (a -> c)
|
||||
(<$$>) pa c = pa <**> pure (flip c)
|
||||
|
||||
(<$$$>) :: Applicative f =>
|
||||
f c -> (a -> b -> c -> t) -> f (b -> a -> t)
|
||||
(<$$$>) :: Parser c -> (a -> b -> c -> t) -> Parser (b -> a -> t)
|
||||
p <$$$> c = p <**> pure (flip3 c)
|
||||
|
||||
(<$$$$>) :: Applicative f =>
|
||||
f d -> (a -> b -> c -> d -> t) -> f (c -> b -> a -> t)
|
||||
(<$$$$>) :: Parser d -> (a -> b -> c -> d -> t) -> Parser (c -> b -> a -> t)
|
||||
p <$$$$> c = p <**> pure (flip4 c)
|
||||
|
||||
(<$$$$$>) :: Applicative f =>
|
||||
f e -> (a -> b -> c -> d -> e -> t) -> f (d -> c -> b -> a -> t)
|
||||
(<$$$$$>) :: Parser e -> (a -> b -> c -> d -> e -> t) -> Parser (d -> c -> b -> a -> t)
|
||||
p <$$$$$> c = p <**> pure (flip5 c)
|
||||
|
||||
optionSuffix :: (a -> Parser a) -> a -> Parser a
|
||||
optionSuffix p a = option a (p a)
|
||||
hoptionSuffix :: (a -> Parser a) -> a -> Parser a
|
||||
hoptionSuffix p a = hoption a (p a)
|
||||
|
||||
{-
|
||||
parses an optional postfix element and applies its result to its left
|
||||
|
@ -2144,12 +2185,12 @@ other operators so it can be used nicely
|
|||
-}
|
||||
|
||||
(<??>) :: Parser a -> Parser (a -> a) -> Parser a
|
||||
p <??> q = p <**> option id q
|
||||
p <??> q = p <**> hoption id q
|
||||
|
||||
-- 0 to many repeated applications of suffix parser
|
||||
|
||||
(<??*>) :: Parser a -> Parser (a -> a) -> Parser a
|
||||
p <??*> q = foldr ($) <$> p <*> (reverse <$> many q)
|
||||
p <??*> q = foldr ($) <$> p <*> (reverse <$> many (hidden q))
|
||||
|
||||
{-
|
||||
These are to help with left factored parsers:
|
||||
|
@ -2177,7 +2218,7 @@ unsignedInteger = read . T.unpack <$> sqlNumberTok True <?> "natural number"
|
|||
-- todo: work out the symbol parsing better
|
||||
|
||||
symbol :: Text -> Parser Text
|
||||
symbol s = symbolTok (Just s) <?> T.unpack s
|
||||
symbol s = symbolTok (Just s) <?> s
|
||||
|
||||
singleCharSymbol :: Char -> Parser Char
|
||||
singleCharSymbol c = c <$ symbol (T.singleton c)
|
||||
|
@ -2185,44 +2226,39 @@ singleCharSymbol c = c <$ symbol (T.singleton c)
|
|||
questionMark :: Parser Char
|
||||
questionMark = singleCharSymbol '?' <?> "question mark"
|
||||
|
||||
openParen :: Parser Char
|
||||
openParen = singleCharSymbol '('
|
||||
|
||||
closeParen :: Parser Char
|
||||
closeParen = singleCharSymbol ')'
|
||||
|
||||
openBracket :: Parser Char
|
||||
openBracket = singleCharSymbol '['
|
||||
|
||||
closeBracket :: Parser Char
|
||||
closeBracket = singleCharSymbol ']'
|
||||
openParen :: Parser ()
|
||||
openParen = void $ singleCharSymbol '('
|
||||
|
||||
closeParen :: Parser ()
|
||||
closeParen = void $ singleCharSymbol ')'
|
||||
|
||||
comma :: Parser Char
|
||||
comma = singleCharSymbol ',' <?> ""
|
||||
comma = singleCharSymbol ','
|
||||
|
||||
semi :: Parser Char
|
||||
semi = singleCharSymbol ';' <?> ""
|
||||
semi = singleCharSymbol ';'
|
||||
|
||||
-- = helper functions
|
||||
|
||||
keyword :: Text -> Parser Text
|
||||
keyword k = unquotedIdentifierTok [] (Just k) <?> T.unpack k
|
||||
keyword k = keywordTok [k] <?> k
|
||||
|
||||
-- helper function to improve error messages
|
||||
|
||||
keywords_ :: [Text] -> Parser ()
|
||||
keywords_ ks = mapM_ keyword_ ks <?> T.unpack (T.unwords ks)
|
||||
|
||||
keywords_ ks = label (T.unwords ks) $ mapM_ keyword_ ks
|
||||
|
||||
parens :: Parser a -> Parser a
|
||||
parens = between openParen closeParen
|
||||
|
||||
brackets :: Parser a -> Parser a
|
||||
brackets = between openBracket closeBracket
|
||||
brackets = between (singleCharSymbol '[') (singleCharSymbol ']')
|
||||
|
||||
braces :: Parser a -> Parser a
|
||||
braces = between (singleCharSymbol '{') (singleCharSymbol '}')
|
||||
|
||||
commaSep :: Parser a -> Parser [a]
|
||||
commaSep = (`sepBy` comma)
|
||||
commaSep = (`sepBy` hidden comma)
|
||||
|
||||
keyword_ :: Text -> Parser ()
|
||||
keyword_ = void . keyword
|
||||
|
@ -2231,7 +2267,19 @@ symbol_ :: Text -> Parser ()
|
|||
symbol_ = void . symbol
|
||||
|
||||
commaSep1 :: Parser a -> Parser [a]
|
||||
commaSep1 = (`sepBy1` comma)
|
||||
commaSep1 = (`sepBy1` hidden comma)
|
||||
|
||||
hoptional :: Parser a -> Parser (Maybe a)
|
||||
hoptional = hidden . optional
|
||||
|
||||
hoption :: a -> Parser a -> Parser a
|
||||
hoption a p = hidden $ option a p
|
||||
|
||||
label :: Text -> Parser a -> Parser a
|
||||
label x = M.label (T.unpack x)
|
||||
|
||||
(<?>) :: Parser a -> Text -> Parser a
|
||||
(<?>) p a = (M.<?>) p (T.unpack a)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
|
@ -2277,25 +2325,25 @@ stringTokExtend = do
|
|||
]
|
||||
|
||||
hostParamTok :: Parser Text
|
||||
hostParamTok = token test Set.empty <?> ""
|
||||
hostParamTok = token test Set.empty <?> "host param"
|
||||
where
|
||||
test (L.WithPos _ _ _ (L.PrefixedVariable c p)) = Just $ T.cons c p
|
||||
test _ = Nothing
|
||||
|
||||
positionalArgTok :: Parser Int
|
||||
positionalArgTok = token test Set.empty <?> ""
|
||||
positionalArgTok = token test Set.empty <?> "positional arg"
|
||||
where
|
||||
test (L.WithPos _ _ _ (L.PositionalArg p)) = Just p
|
||||
test _ = Nothing
|
||||
|
||||
sqlNumberTok :: Bool -> Parser Text
|
||||
sqlNumberTok intOnly = token test Set.empty <?> ""
|
||||
sqlNumberTok intOnly = token test Set.empty <?> "number"
|
||||
where
|
||||
test (L.WithPos _ _ _ (L.SqlNumber p)) | not intOnly || T.all isDigit p = Just p
|
||||
test _ = Nothing
|
||||
|
||||
symbolTok :: Maybe Text -> Parser Text
|
||||
symbolTok sym = token test Set.empty <?> ""
|
||||
symbolTok sym = token test Set.empty <?> lbl
|
||||
where
|
||||
test (L.WithPos _ _ _ (L.Symbol p)) =
|
||||
case sym of
|
||||
|
@ -2303,6 +2351,9 @@ symbolTok sym = token test Set.empty <?> ""
|
|||
Just sym' | sym' == p -> Just p
|
||||
_ -> Nothing
|
||||
test _ = Nothing
|
||||
lbl = case sym of
|
||||
Nothing -> "symbol"
|
||||
Just p -> p
|
||||
|
||||
{-
|
||||
The blacklisted names are mostly needed when we parse something with
|
||||
|
@ -2341,21 +2392,19 @@ will likely mean many things don't parse anymore.
|
|||
-}
|
||||
|
||||
identifierTok :: [Text] -> Parser (Maybe (Text,Text), Text)
|
||||
identifierTok blackList = token test Set.empty <?> ""
|
||||
identifierTok blackList = do
|
||||
token test Set.empty <?> "identifier"
|
||||
where
|
||||
test (L.WithPos _ _ _ (L.Identifier q@(Just {}) p)) = Just (q,p)
|
||||
test (L.WithPos _ _ _ (L.Identifier q p))
|
||||
test (L.WithPos _ _ _ (L.Identifier q@Nothing p))
|
||||
| T.toLower p `notElem` blackList = Just (q,p)
|
||||
test _ = Nothing
|
||||
|
||||
unquotedIdentifierTok :: [Text] -> Maybe Text -> Parser Text
|
||||
unquotedIdentifierTok blackList kw = token test Set.empty <?> ""
|
||||
where
|
||||
test (L.WithPos _ _ _ (L.Identifier Nothing p)) =
|
||||
case kw of
|
||||
Nothing | T.toLower p `notElem` blackList -> Just p
|
||||
Just k | k == T.toLower p -> Just p
|
||||
_ -> Nothing
|
||||
keywordTok :: [Text] -> Parser Text
|
||||
keywordTok allowed = do
|
||||
token test Set.empty where
|
||||
test (L.WithPos _ _ _ (L.Identifier Nothing p))
|
||||
| T.toLower p `elem` allowed = Just p
|
||||
test _ = Nothing
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
|
8
Makefile
8
Makefile
|
@ -11,7 +11,11 @@ build :
|
|||
|
||||
.PHONY : test
|
||||
test :
|
||||
cabal run test:Tests -- --hide-successes --ansi-tricks=false
|
||||
cabal run test:Tests -- -f failed-examples +RTS -N
|
||||
|
||||
.PHONY : fast-test
|
||||
fast-test :
|
||||
cabal run test:Tests -- -f failed-examples --skip ansiLexerTests --skip postgresLexerTests +RTS -N
|
||||
|
||||
.PHONY : test-coverage
|
||||
test-coverage :
|
||||
|
@ -67,7 +71,9 @@ build/test_cases.html : website/RenderTestCases.hs website/template1.pandoc
|
|||
# no idea why not using --disable-optimisation on cabal build, but putting -O0
|
||||
# in the cabal file (and then cabal appears to say it's still using -O1
|
||||
# is faster
|
||||
echo Entering directory \`website/\'
|
||||
cd website/ && cabal build RenderTestCases && cabal run RenderTestCases | pandoc -s -N --template template1.pandoc -V toc-title:"Simple SQL Parser test case examples" -c main1.css -f markdown -t html --toc=true --metadata title="Simple SQL Parse test case examples" > ../build/test_cases.html
|
||||
echo Leaving directory \`website/\'
|
||||
|
||||
# works here, but not in a recipe. amazing
|
||||
# GHC_VER="$(shell ghc --numeric-version)"
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
0.8.0 (not yet released)
|
||||
lexer has new option to output an invalid token on some kinds of
|
||||
parse errors
|
||||
switch tests to hspec
|
||||
improve parse error messages
|
||||
0.7.1 fix error message source quoting
|
||||
0.7.0 support autoincrement for sqlite
|
||||
support table constraints without separating comma for sqlite
|
||||
|
|
514
examples/ErrorMessagesTool.hs
Normal file
514
examples/ErrorMessagesTool.hs
Normal file
|
@ -0,0 +1,514 @@
|
|||
{-
|
||||
|
||||
tool to compare before and after on error messages, suggested use:
|
||||
add any extra parse error examples below
|
||||
run it on baseline code
|
||||
run it on the modified code
|
||||
use meld on the two resulting csvs
|
||||
bear in mind that " will appear as "" because of csv escaping
|
||||
|
||||
this is how to generate a csv of errors:
|
||||
|
||||
cabal -ftestexe build error-messages-tool && cabal -ftestexe run error-messages-tool -- generate | cabal -ftestexe run error-messages-tool -- test > res.csv
|
||||
|
||||
TODO:
|
||||
think about making a regression test with this
|
||||
can add some more tools:
|
||||
there's a join mode to join two sets of results, could add a filter
|
||||
to remove rows that are the same
|
||||
but finding the different rows in meld seems to work well enough
|
||||
figure out if you can display visual diffs between pairs of cells in localc
|
||||
implement the tagging feature, one idea for working with it:
|
||||
you generate a bunch of error messages
|
||||
you eyeball the list, and mark some as good, some as bad
|
||||
then when you update, you can do a compare which filters
|
||||
to keep any errors that have changed, and any that haven't
|
||||
changed but are not marked as good
|
||||
etc.
|
||||
|
||||
-}
|
||||
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
|
||||
import Text.Show.Pretty (ppShow)
|
||||
import qualified Text.RawString.QQ as R
|
||||
|
||||
import Language.SQL.SimpleSQL.Parse
|
||||
(prettyError
|
||||
,parseQueryExpr
|
||||
,parseScalarExpr
|
||||
-- ,parseStatement
|
||||
-- ,parseStatements
|
||||
,ansi2011
|
||||
-- ,ParseError(..)
|
||||
)
|
||||
--import qualified Language.SQL.SimpleSQL.Lex as L
|
||||
|
||||
import Language.SQL.SimpleSQL.Dialect
|
||||
(postgres
|
||||
,Dialect(..)
|
||||
,sqlserver
|
||||
,mysql
|
||||
)
|
||||
|
||||
import System.Environment (getArgs)
|
||||
import Data.Csv
|
||||
(encode
|
||||
,decode
|
||||
,HasHeader(..))
|
||||
|
||||
import qualified Data.ByteString.Lazy as B hiding (pack)
|
||||
import Data.ByteString.Lazy (ByteString)
|
||||
import qualified Data.ByteString.Lazy.Char8 as B (putStrLn)
|
||||
import qualified Data.Vector as V
|
||||
import Data.Vector (Vector)
|
||||
|
||||
import Database.SQLite.Simple
|
||||
(open
|
||||
,execute_
|
||||
,executeMany
|
||||
,query_
|
||||
)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
|
||||
as <- getArgs
|
||||
case as of
|
||||
["generate"] -> B.putStrLn generateData
|
||||
["test"] -> do
|
||||
txt <- B.getContents
|
||||
B.putStrLn $ runTests txt
|
||||
["compare", f1, f2] -> do
|
||||
c1 <- B.readFile f1
|
||||
c2 <- B.readFile f2
|
||||
B.putStrLn =<< compareFiles c1 c2
|
||||
_ -> error $ "unsupported arguments: " <> show as
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- compare two files
|
||||
{-
|
||||
|
||||
take two inputs
|
||||
assume they have (testrunid, parser, dialect, src, res,tags) lines
|
||||
do a full outer join between them, on
|
||||
parser,dialect,src
|
||||
so you have
|
||||
parser,dialect,src,res a, tags a, res b, tags b
|
||||
|
||||
then output this as the result
|
||||
|
||||
see what happens if you highlight the differences in localc, edit some
|
||||
tags, then save as csv - does the highlighting just disappear leaving
|
||||
the interesting data only?
|
||||
|
||||
-}
|
||||
|
||||
|
||||
compareFiles :: ByteString -> ByteString -> IO ByteString
|
||||
compareFiles csva csvb = do
|
||||
let data1 :: [(Text,Text,Text,Text,Text,Text)]
|
||||
data1 = either (error . show) V.toList $ decode NoHeader csva
|
||||
data2 :: [(Text,Text,Text,Text,Text,Text)]
|
||||
data2 = either (error . show) V.toList $ decode NoHeader csvb
|
||||
conn <- open ":memory:"
|
||||
execute_ conn [R.r|
|
||||
create table data1 (
|
||||
testrunida text,
|
||||
parser text,
|
||||
dialect text,
|
||||
source text,
|
||||
result_a text,
|
||||
tags_a text)|]
|
||||
execute_ conn [R.r|
|
||||
create table data2 (
|
||||
testrunidb text,
|
||||
parser text,
|
||||
dialect text,
|
||||
source text,
|
||||
result_b text,
|
||||
tags_b text)|]
|
||||
|
||||
executeMany conn "insert into data1 values (?,?,?,?,?,?)" data1
|
||||
executeMany conn "insert into data2 values (?,?,?,?,?,?)" data2
|
||||
r <- query_ conn [R.r|
|
||||
select
|
||||
parser, dialect, source, result_a, tags_a, result_b, tags_b
|
||||
from data1 natural full outer join data2|] :: IO [(Text,Text,Text,Text,Text,Text,Text)]
|
||||
|
||||
pure $ encode r
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- running tests
|
||||
|
||||
runTests :: ByteString -> ByteString
|
||||
runTests csvsrc =
|
||||
let csv :: Vector (Text,Text,Text)
|
||||
csv = either (error . show) id $ decode NoHeader csvsrc
|
||||
|
||||
testrunid = ("0" :: Text)
|
||||
|
||||
testLine (parser,dialect,src) =
|
||||
let d = case dialect of
|
||||
"ansi2011" -> ansi2011
|
||||
"postgres" -> postgres
|
||||
"sqlserver" -> sqlserver
|
||||
"mysql" -> mysql
|
||||
"params" -> ansi2011{diAtIdentifier=True, diHashIdentifier= True}
|
||||
"odbc" -> ansi2011{diOdbc=True}
|
||||
_ -> error $ "unknown dialect: " <> T.unpack dialect
|
||||
res = case parser of
|
||||
"queryExpr" ->
|
||||
either prettyError (T.pack . ppShow)
|
||||
$ parseQueryExpr d "" Nothing src
|
||||
"scalarExpr" ->
|
||||
either prettyError (T.pack . ppShow)
|
||||
$ parseScalarExpr d "" Nothing src
|
||||
_ -> error $ "unknown parser: " <> T.unpack parser
|
||||
-- prepend a newline to multi line fields, so they show
|
||||
-- nice in a diff in meld or similar
|
||||
resadj = if '\n' `T.elem` res
|
||||
then T.cons '\n' res
|
||||
else res
|
||||
in (testrunid, parser, dialect, src, resadj,"" :: Text)
|
||||
|
||||
allres = V.map testLine csv
|
||||
in encode $ V.toList allres
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- generating data
|
||||
|
||||
generateData :: ByteString
|
||||
generateData =
|
||||
encode $ concat
|
||||
[simpleExpressions1
|
||||
,pgExprs
|
||||
,sqlServerIden
|
||||
,mysqliden
|
||||
,paramvariations
|
||||
,odbcexpr
|
||||
,odbcqexpr
|
||||
,otherParseErrorExamples]
|
||||
|
||||
--------------------------------------
|
||||
|
||||
-- example data
|
||||
|
||||
parseExampleStrings :: Text -> [Text]
|
||||
parseExampleStrings = filter (not . T.null) . map T.strip . T.splitOn ";"
|
||||
|
||||
simpleExpressions1 :: [(Text,Text,Text)]
|
||||
simpleExpressions1 =
|
||||
concat $ flip map (parseExampleStrings simpleExprData) $ \e ->
|
||||
[("scalarExpr", "ansi2011", e)
|
||||
,("queryExpr", "ansi2011", "select " <> e)
|
||||
,("queryExpr", "ansi2011", "select " <> e <> ",")
|
||||
,("queryExpr", "ansi2011", "select " <> e <> " from")]
|
||||
where
|
||||
simpleExprData = [R.r|
|
||||
'test
|
||||
;
|
||||
'test''t
|
||||
;
|
||||
'test''
|
||||
;
|
||||
3.23e-
|
||||
;
|
||||
.
|
||||
;
|
||||
3.23e
|
||||
;
|
||||
a.3
|
||||
;
|
||||
3.a
|
||||
;
|
||||
3.2a
|
||||
;
|
||||
4iden
|
||||
;
|
||||
4iden.
|
||||
;
|
||||
iden.4iden
|
||||
;
|
||||
4iden.*
|
||||
;
|
||||
from
|
||||
;
|
||||
from.a
|
||||
;
|
||||
a.from
|
||||
;
|
||||
not
|
||||
;
|
||||
4 +
|
||||
;
|
||||
4 + from
|
||||
;
|
||||
(5
|
||||
;
|
||||
(5 +
|
||||
;
|
||||
(5 + 6
|
||||
;
|
||||
(5 + from)
|
||||
;
|
||||
case
|
||||
;
|
||||
case a
|
||||
;
|
||||
case a when b c end
|
||||
;
|
||||
case a when b then c
|
||||
;
|
||||
case a else d end
|
||||
;
|
||||
case a from c end
|
||||
;
|
||||
case a when from then to end
|
||||
;
|
||||
/* blah
|
||||
;
|
||||
/* blah /* stuff */
|
||||
;
|
||||
/* *
|
||||
;
|
||||
/* /
|
||||
;
|
||||
$$something$
|
||||
;
|
||||
$$something
|
||||
;
|
||||
$$something
|
||||
x
|
||||
;
|
||||
$a$something$b$
|
||||
;
|
||||
$a$
|
||||
;
|
||||
'''
|
||||
;
|
||||
'''''
|
||||
;
|
||||
"a
|
||||
;
|
||||
"a""
|
||||
;
|
||||
"""
|
||||
;
|
||||
"""""
|
||||
;
|
||||
""
|
||||
;
|
||||
*/
|
||||
;
|
||||
:3
|
||||
;
|
||||
@3
|
||||
;
|
||||
#3
|
||||
;
|
||||
:::
|
||||
;
|
||||
|||
|
||||
;
|
||||
...
|
||||
;
|
||||
"
|
||||
;
|
||||
]
|
||||
;
|
||||
)
|
||||
;
|
||||
[test
|
||||
;
|
||||
[]
|
||||
;
|
||||
[[test]]
|
||||
;
|
||||
`open
|
||||
;
|
||||
```
|
||||
;
|
||||
``
|
||||
;
|
||||
}
|
||||
;
|
||||
mytype(4 '4';
|
||||
;
|
||||
app(3
|
||||
;
|
||||
app(
|
||||
;
|
||||
app(something
|
||||
;
|
||||
count(*
|
||||
;
|
||||
count(* filter (where something > 5)
|
||||
;
|
||||
count(*) filter (where something > 5
|
||||
;
|
||||
count(*) filter (
|
||||
;
|
||||
sum(a over (order by b)
|
||||
;
|
||||
sum(a) over (order by b
|
||||
;
|
||||
sum(a) over (
|
||||
;
|
||||
rank(a,c within group (order by b)
|
||||
;
|
||||
rank(a,c) within group (order by b
|
||||
;
|
||||
rank(a,c) within group (
|
||||
;
|
||||
array[
|
||||
;
|
||||
|]
|
||||
|
||||
pgExprs :: [(Text,Text,Text)]
|
||||
pgExprs = flip map (parseExampleStrings src) $ \e ->
|
||||
("scalarExpr", "postgres", e)
|
||||
where src = [R.r|
|
||||
$$something$
|
||||
;
|
||||
$$something
|
||||
;
|
||||
$$something
|
||||
x
|
||||
;
|
||||
$a$something$b$
|
||||
;
|
||||
$a$
|
||||
;
|
||||
:::
|
||||
;
|
||||
|||
|
||||
;
|
||||
...
|
||||
;
|
||||
|
||||
|]
|
||||
|
||||
sqlServerIden :: [(Text,Text,Text)]
|
||||
sqlServerIden = flip map (parseExampleStrings src) $ \e ->
|
||||
("scalarExpr", "sqlserver", e)
|
||||
where src = [R.r|
|
||||
]
|
||||
;
|
||||
[test
|
||||
;
|
||||
[]
|
||||
;
|
||||
[[test]]
|
||||
|
||||
|]
|
||||
|
||||
mysqliden :: [(Text,Text,Text)]
|
||||
mysqliden = flip map (parseExampleStrings src) $ \e ->
|
||||
("scalarExpr", "mysql", e)
|
||||
where src = [R.r|
|
||||
`open
|
||||
;
|
||||
```
|
||||
;
|
||||
``
|
||||
|
||||
|]
|
||||
|
||||
paramvariations :: [(Text,Text,Text)]
|
||||
paramvariations = flip map (parseExampleStrings src) $ \e ->
|
||||
("scalarExpr", "params", e)
|
||||
where src = [R.r|
|
||||
:3
|
||||
;
|
||||
@3
|
||||
;
|
||||
#3
|
||||
|
||||
|]
|
||||
|
||||
|
||||
odbcexpr :: [(Text,Text,Text)]
|
||||
odbcexpr = flip map (parseExampleStrings src) $ \e ->
|
||||
("scalarExpr", "odbc", e)
|
||||
where src = [R.r|
|
||||
{d '2000-01-01'
|
||||
;
|
||||
{fn CHARACTER_LENGTH(string_exp)
|
||||
|
||||
|]
|
||||
|
||||
odbcqexpr :: [(Text,Text,Text)]
|
||||
odbcqexpr = flip map (parseExampleStrings src) $ \e ->
|
||||
("queryExpr", "odbc", e)
|
||||
where src = [R.r|
|
||||
select * from {oj t1 left outer join t2 on expr
|
||||
|
||||
|]
|
||||
|
||||
|
||||
|
||||
otherParseErrorExamples :: [(Text,Text,Text)]
|
||||
otherParseErrorExamples = flip map (parseExampleStrings src) $ \e ->
|
||||
("queryExpr", "ansi2011", e)
|
||||
where src = [R.r|
|
||||
select a select
|
||||
;
|
||||
select a from t,
|
||||
;
|
||||
select a from t select
|
||||
;
|
||||
select a from t(a)
|
||||
;
|
||||
select a from (t
|
||||
;
|
||||
select a from (t having
|
||||
;
|
||||
select a from t a b
|
||||
;
|
||||
select a from t as
|
||||
;
|
||||
select a from t as having
|
||||
;
|
||||
select a from (1234)
|
||||
;
|
||||
select a from (1234
|
||||
;
|
||||
select a from a wrong join b
|
||||
;
|
||||
select a from a natural wrong join b
|
||||
;
|
||||
select a from a left wrong join b
|
||||
;
|
||||
select a from a left wrong join b
|
||||
;
|
||||
select a from a join b select
|
||||
;
|
||||
select a from a join b on select
|
||||
;
|
||||
select a from a join b on (1234
|
||||
;
|
||||
select a from a join b using(a
|
||||
;
|
||||
select a from a join b using(a,
|
||||
;
|
||||
select a from a join b using(a,)
|
||||
;
|
||||
select a from a join b using(1234
|
||||
;
|
||||
select a from t order no a
|
||||
;
|
||||
select a from t order by a where c
|
||||
;
|
||||
select 'test
|
||||
'
|
||||
|
||||
|]
|
|
@ -89,7 +89,7 @@ lexCommand =
|
|||
(f,src) <- getInput args
|
||||
either (error . T.unpack . L.prettyError)
|
||||
(putStrLn . intercalate ",\n" . map show)
|
||||
$ L.lexSQL ansi2011 (T.pack f) Nothing (T.pack src)
|
||||
$ L.lexSQL ansi2011 False (T.pack f) Nothing (T.pack src)
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
cabal-version: 2.2
|
||||
|
||||
name: simple-sql-parser
|
||||
version: 0.7.1
|
||||
version: 0.8.0
|
||||
synopsis: A parser for SQL.
|
||||
|
||||
description: A parser for SQL. Parses most SQL:2011
|
||||
|
@ -29,6 +29,11 @@ Flag parserexe
|
|||
Description: Build SimpleSQLParserTool exe
|
||||
Default: False
|
||||
|
||||
Flag testexe
|
||||
Description: Build Testing exe
|
||||
Default: False
|
||||
|
||||
|
||||
common shared-properties
|
||||
default-language: Haskell2010
|
||||
build-depends: base >=4 && <5,
|
||||
|
@ -56,8 +61,10 @@ Test-Suite Tests
|
|||
main-is: RunTests.hs
|
||||
hs-source-dirs: tests
|
||||
Build-Depends: simple-sql-parser,
|
||||
tasty >= 1.1 && < 1.6,
|
||||
tasty-hunit >= 0.9 && < 0.11
|
||||
hspec,
|
||||
hspec-megaparsec,
|
||||
hspec-expectations,
|
||||
raw-strings-qq,
|
||||
|
||||
Other-Modules: Language.SQL.SimpleSQL.ErrorMessages,
|
||||
Language.SQL.SimpleSQL.FullQueries,
|
||||
|
@ -82,6 +89,8 @@ Test-Suite Tests
|
|||
Language.SQL.SimpleSQL.CustomDialect,
|
||||
Language.SQL.SimpleSQL.EmptyStatement,
|
||||
Language.SQL.SimpleSQL.CreateIndex
|
||||
Language.SQL.SimpleSQL.Expectations
|
||||
Language.SQL.SimpleSQL.TestRunners
|
||||
|
||||
ghc-options: -threaded
|
||||
|
||||
|
@ -95,3 +104,23 @@ executable SimpleSQLParserTool
|
|||
buildable: True
|
||||
else
|
||||
buildable: False
|
||||
|
||||
executable error-messages-tool
|
||||
import: shared-properties
|
||||
main-is: ErrorMessagesTool.hs
|
||||
hs-source-dirs: examples
|
||||
Build-Depends: base,
|
||||
text,
|
||||
raw-strings-qq,
|
||||
containers,
|
||||
megaparsec,
|
||||
simple-sql-parser,
|
||||
pretty-show,
|
||||
bytestring,
|
||||
cassava,
|
||||
vector,
|
||||
sqlite-simple,
|
||||
if flag(testexe)
|
||||
buildable: True
|
||||
else
|
||||
buildable: False
|
||||
|
|
|
@ -4,15 +4,19 @@ module Language.SQL.SimpleSQL.CreateIndex where
|
|||
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
createIndexTests :: TestItem
|
||||
createIndexTests = Group "create index tests"
|
||||
[TestStatement ansi2011 "create index a on tbl(c1)"
|
||||
[s "create index a on tbl(c1)"
|
||||
$ CreateIndex False [nm "a"] [nm "tbl"] [nm "c1"]
|
||||
,TestStatement ansi2011 "create index a.b on sc.tbl (c1, c2)"
|
||||
,s "create index a.b on sc.tbl (c1, c2)"
|
||||
$ CreateIndex False [nm "a", nm "b"] [nm "sc", nm "tbl"] [nm "c1", nm "c2"]
|
||||
,TestStatement ansi2011 "create unique index a on tbl(c1)"
|
||||
,s "create unique index a on tbl(c1)"
|
||||
$ CreateIndex True [nm "a"] [nm "tbl"] [nm "c1"]
|
||||
]
|
||||
where
|
||||
nm = Name Nothing
|
||||
s :: HasCallStack => Text -> Statement -> TestItem
|
||||
s src ast = testStatement ansi2011 src ast
|
||||
|
|
|
@ -3,26 +3,30 @@
|
|||
module Language.SQL.SimpleSQL.CustomDialect (customDialectTests) where
|
||||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
customDialectTests :: TestItem
|
||||
customDialectTests = Group "custom dialect tests" (map (uncurry ParseQueryExpr) passTests
|
||||
++ map (uncurry ParseScalarExprFails) failTests )
|
||||
customDialectTests = Group "custom dialect tests" $
|
||||
[q ansi2011 "SELECT a b"
|
||||
,q noDateKeyword "SELECT DATE('2000-01-01')"
|
||||
,q noDateKeyword "SELECT DATE"
|
||||
,q dateApp "SELECT DATE('2000-01-01')"
|
||||
,q dateIden "SELECT DATE"
|
||||
,f ansi2011 "SELECT DATE('2000-01-01')"
|
||||
,f ansi2011 "SELECT DATE"
|
||||
,f dateApp "SELECT DATE"
|
||||
,f dateIden "SELECT DATE('2000-01-01')"
|
||||
-- show this never being allowed as an alias
|
||||
,f ansi2011 "SELECT a date"
|
||||
,f dateApp "SELECT a date"
|
||||
,f dateIden "SELECT a date"
|
||||
]
|
||||
where
|
||||
failTests = [(ansi2011,"SELECT DATE('2000-01-01')")
|
||||
,(ansi2011,"SELECT DATE")
|
||||
,(dateApp,"SELECT DATE")
|
||||
,(dateIden,"SELECT DATE('2000-01-01')")
|
||||
-- show this never being allowed as an alias
|
||||
,(ansi2011,"SELECT a date")
|
||||
,(dateApp,"SELECT a date")
|
||||
,(dateIden,"SELECT a date")
|
||||
]
|
||||
passTests = [(ansi2011,"SELECT a b")
|
||||
,(noDateKeyword,"SELECT DATE('2000-01-01')")
|
||||
,(noDateKeyword,"SELECT DATE")
|
||||
,(dateApp,"SELECT DATE('2000-01-01')")
|
||||
,(dateIden,"SELECT DATE")
|
||||
]
|
||||
noDateKeyword = ansi2011 {diKeywords = filter (/="date") (diKeywords ansi2011)}
|
||||
dateIden = ansi2011 {diIdentifierKeywords = "date" : diIdentifierKeywords ansi2011}
|
||||
dateApp = ansi2011 {diAppKeywords = "date" : diAppKeywords ansi2011}
|
||||
q :: HasCallStack => Dialect -> Text -> TestItem
|
||||
q d src = testParseQueryExpr d src
|
||||
f :: HasCallStack => Dialect -> Text -> TestItem
|
||||
f d src = testParseQueryExprFails d src
|
||||
|
|
|
@ -3,19 +3,26 @@ module Language.SQL.SimpleSQL.EmptyStatement where
|
|||
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
emptyStatementTests :: TestItem
|
||||
emptyStatementTests = Group "empty statement"
|
||||
[ TestStatement ansi2011 ";" EmptyStatement
|
||||
, TestStatements ansi2011 ";" [EmptyStatement]
|
||||
, TestStatements ansi2011 ";;" [EmptyStatement, EmptyStatement]
|
||||
, TestStatements ansi2011 ";;;" [EmptyStatement, EmptyStatement, EmptyStatement]
|
||||
, TestStatement ansi2011 "/* comment */ ;" EmptyStatement
|
||||
, TestStatements ansi2011 "" []
|
||||
, TestStatements ansi2011 "/* comment */" []
|
||||
, TestStatements ansi2011 "/* comment */ ;" [EmptyStatement]
|
||||
, TestStatements ansi2011 "/* comment */ ; /* comment */ ;"
|
||||
[ s ";" EmptyStatement
|
||||
, t ";" [EmptyStatement]
|
||||
, t ";;" [EmptyStatement, EmptyStatement]
|
||||
, t ";;;" [EmptyStatement, EmptyStatement, EmptyStatement]
|
||||
, s "/* comment */ ;" EmptyStatement
|
||||
, t "" []
|
||||
, t "/* comment */" []
|
||||
, t "/* comment */ ;" [EmptyStatement]
|
||||
, t "/* comment */ ; /* comment */ ;"
|
||||
[EmptyStatement, EmptyStatement]
|
||||
, TestStatements ansi2011 "/* comment */ ; /* comment */ ; /* comment */ ;"
|
||||
, t "/* comment */ ; /* comment */ ; /* comment */ ;"
|
||||
[EmptyStatement, EmptyStatement, EmptyStatement]
|
||||
]
|
||||
where
|
||||
s :: HasCallStack => Text -> Statement -> TestItem
|
||||
s src a = testStatement ansi2011 src a
|
||||
t :: HasCallStack => Text -> [Statement] -> TestItem
|
||||
t src a = testStatements ansi2011 src a
|
||||
|
|
|
@ -1,156 +1,82 @@
|
|||
|
||||
{-
|
||||
Want to work on the error messages. Ultimately, parsec won't give the
|
||||
best error message for a parser combinator library in haskell. Should
|
||||
check out the alternatives such as polyparse and uu-parsing.
|
||||
See the file examples/ErrorMessagesTool.hs for some work on this
|
||||
|
||||
For now the plan is to try to get the best out of parsec. Skip heavy
|
||||
work on this until the parser is more left factored?
|
||||
|
||||
Ideas:
|
||||
|
||||
1. generate large lists of invalid syntax
|
||||
2. create table of the sql source and the error message
|
||||
3. save these tables and compare from version to version. Want to
|
||||
catch improvements and regressions and investigate. Have to do this
|
||||
manually
|
||||
|
||||
= generating bad sql source
|
||||
|
||||
take good sql statements or expressions. Convert them into sequences
|
||||
of tokens - want to preserve the whitespace and comments perfectly
|
||||
here. Then modify these lists by either adding a token, removing a
|
||||
token, or modifying a token (including creating bad tokens of raw
|
||||
strings which don't represent anything than can be tokenized.
|
||||
|
||||
Now can see the error message for all of these bad strings. Probably
|
||||
have to generate and prune this list manually in stages since there
|
||||
will be too many.
|
||||
|
||||
Contexts:
|
||||
|
||||
another area to focus on is contexts: for instance, we have a set of
|
||||
e.g. 1000 bad scalar expressions with error messages. Now can put
|
||||
those bad scalar expressions into various contexts and see that the
|
||||
error messages are still good.
|
||||
|
||||
plan:
|
||||
|
||||
1. create a list of all the value expression, with some variations for
|
||||
each
|
||||
2. manually create some error variations for each expression
|
||||
3. create a renderer which will create a csv of the expressions and
|
||||
the errors
|
||||
this is to load as a spreadsheet to investigate more
|
||||
4. create a renderer for the csv which will create a markdown file for
|
||||
the website. this is to demonstrate the error messages in the
|
||||
documentation
|
||||
|
||||
Then create some contexts for all of these: inside another value
|
||||
expression, or inside a query expression. Do the same: render and
|
||||
review the error messages.
|
||||
|
||||
Then, create some query expressions to focus on the non value
|
||||
expression parts.
|
||||
-}
|
||||
|
||||
|
||||
module Language.SQL.SimpleSQL.ErrorMessages where
|
||||
|
||||
{-import Language.SQL.SimpleSQL.Parser
|
||||
import Data.List
|
||||
import Text.Groom
|
||||
|
||||
valueExpressions :: [String]
|
||||
valueExpressions =
|
||||
["10.."
|
||||
,"..10"
|
||||
,"10e1e2"
|
||||
,"10e--3"
|
||||
,"1a"
|
||||
,"1%"
|
||||
|
||||
,"'b'ad'"
|
||||
,"'bad"
|
||||
,"bad'"
|
||||
|
||||
,"interval '5' ay"
|
||||
,"interval '5' day (4.4)"
|
||||
,"interval '5' day (a)"
|
||||
,"intervala '5' day"
|
||||
,"interval 'x' day (3"
|
||||
,"interval 'x' day 3)"
|
||||
|
||||
,"1badiden"
|
||||
,"$"
|
||||
,"!"
|
||||
,"*.a"
|
||||
|
||||
,"??"
|
||||
,"3?"
|
||||
,"?a"
|
||||
|
||||
,"row"
|
||||
,"row 1,2"
|
||||
,"row(1,2"
|
||||
,"row 1,2)"
|
||||
,"row(1 2)"
|
||||
|
||||
,"f("
|
||||
,"f)"
|
||||
|
||||
,"f(a"
|
||||
,"f a)"
|
||||
,"f(a b)"
|
||||
|
||||
{-
|
||||
TODO:
|
||||
case
|
||||
operators
|
||||
-}
|
||||
|
||||
,"a + (b + c"
|
||||
add simple test to check the error and quoting on later line in multi
|
||||
line input for lexing and parsing; had a regression here that made it
|
||||
to a release
|
||||
|
||||
{-
|
||||
casts
|
||||
subqueries: + whole set of parentheses use
|
||||
in list
|
||||
'keyword' functions
|
||||
aggregates
|
||||
window functions
|
||||
-}
|
||||
|
||||
|
||||
]
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
module Language.SQL.SimpleSQL.ErrorMessages
|
||||
(errorMessageTests
|
||||
) where
|
||||
|
||||
queryExpressions :: [String]
|
||||
queryExpressions =
|
||||
map sl1 valueExpressions
|
||||
++ map sl2 valueExpressions
|
||||
++ map sl3 valueExpressions
|
||||
++
|
||||
["select a from t inner jin u"]
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Parse
|
||||
import qualified Language.SQL.SimpleSQL.Lex as L
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
--import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.Expectations
|
||||
import Test.Hspec (it)
|
||||
import Debug.Trace
|
||||
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
|
||||
import qualified Text.RawString.QQ as R
|
||||
|
||||
errorMessageTests :: TestItem
|
||||
errorMessageTests = Group "error messages"
|
||||
[gp (parseQueryExpr ansi2011 "" Nothing) prettyError [R.r|
|
||||
|
||||
select
|
||||
a
|
||||
from t
|
||||
where
|
||||
something
|
||||
order by 1,2,3 where
|
||||
|
||||
|]
|
||||
[R.r|8:16:
|
||||
|
|
||||
8 | order by 1,2,3 where
|
||||
| ^^^^^
|
||||
unexpected where
|
||||
|]
|
||||
,gp (L.lexSQL ansi2011 False "" Nothing) L.prettyError [R.r|
|
||||
|
||||
select
|
||||
a
|
||||
from t
|
||||
where
|
||||
something
|
||||
order by 1,2,3 $@
|
||||
|
||||
|]
|
||||
[R.r|8:16:
|
||||
|
|
||||
8 | order by 1,2,3 $@
|
||||
| ^
|
||||
unexpected '$'
|
||||
|]
|
||||
]
|
||||
where
|
||||
sl1 x = "select " ++ x ++ " from t"
|
||||
sl2 x = "select " ++ x ++ ", y from t"
|
||||
sl3 x = "select " ++ x ++ " fom t"
|
||||
|
||||
valExprs :: [String] -> [(String,String)]
|
||||
valExprs = map parseOne
|
||||
where
|
||||
parseOne x = let p = parseValueExpr "" Nothing x
|
||||
in (x,either peFormattedError (\x -> "ERROR: parsed ok " ++ groom x) p)
|
||||
|
||||
|
||||
queryExprs :: [String] -> [(String,String)]
|
||||
queryExprs = map parseOne
|
||||
where
|
||||
parseOne x = let p = parseQueryExpr "" Nothing x
|
||||
in (x,either peFormattedError (\x -> "ERROR: parsed ok " ++ groom x) p)
|
||||
|
||||
|
||||
pExprs :: [String] -> [String] -> String
|
||||
pExprs x y =
|
||||
let l = valExprs x ++ queryExprs y
|
||||
in intercalate "\n\n\n\n" $ map (\(a,b) -> a ++ "\n" ++ b) l
|
||||
-}
|
||||
gp :: (Show a, HasCallStack) => (Text -> Either e a) -> (e -> Text) -> Text -> Text -> TestItem
|
||||
gp parse pret src err =
|
||||
GeneralParseFailTest src err $
|
||||
it (T.unpack src) $
|
||||
let f1 = parse src
|
||||
ex = shouldFailWith pret
|
||||
quickTrace =
|
||||
case f1 of
|
||||
Left f | pret f /= err ->
|
||||
trace (T.unpack ("check\n[" <> pret f <>"]\n["<> err <> "]\n"))
|
||||
_ -> id
|
||||
in quickTrace (f1 `ex` err)
|
||||
|
|
61
tests/Language/SQL/SimpleSQL/Expectations.hs
Normal file
61
tests/Language/SQL/SimpleSQL/Expectations.hs
Normal file
|
@ -0,0 +1,61 @@
|
|||
|
||||
module Language.SQL.SimpleSQL.Expectations
|
||||
(shouldParseA
|
||||
,shouldParseL
|
||||
,shouldParse1
|
||||
,shouldFail
|
||||
,shouldSucceed
|
||||
,shouldFailWith
|
||||
) where
|
||||
|
||||
|
||||
import Language.SQL.SimpleSQL.Parse
|
||||
import qualified Language.SQL.SimpleSQL.Lex as Lex
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Data.Text (Text)
|
||||
|
||||
import Test.Hspec.Expectations
|
||||
(Expectation
|
||||
,HasCallStack
|
||||
,expectationFailure
|
||||
)
|
||||
|
||||
import Test.Hspec
|
||||
(shouldBe
|
||||
)
|
||||
|
||||
shouldParseA :: (HasCallStack,Eq a, Show a) => Either ParseError a -> a -> Expectation
|
||||
shouldParseA = shouldParse1 (T.unpack . prettyError)
|
||||
|
||||
shouldParseL :: (HasCallStack,Eq a, Show a) => Either Lex.ParseError a -> a -> Expectation
|
||||
shouldParseL = shouldParse1 (T.unpack . Lex.prettyError)
|
||||
|
||||
shouldParse1 :: (HasCallStack, Show a, Eq a) =>
|
||||
(e -> String)
|
||||
-> Either e a
|
||||
-> a
|
||||
-> Expectation
|
||||
shouldParse1 prettyErr r v = case r of
|
||||
Left e ->
|
||||
expectationFailure $
|
||||
"expected: "
|
||||
++ show v
|
||||
++ "\nbut parsing failed with error:\n"
|
||||
++ prettyErr e
|
||||
Right x -> x `shouldBe` v
|
||||
|
||||
shouldFail :: (HasCallStack, Show a) => Either e a -> Expectation
|
||||
shouldFail r = case r of
|
||||
Left _ -> (1 :: Int) `shouldBe` 1
|
||||
Right a -> expectationFailure $ "expected parse failure, but succeeded with " <> show a
|
||||
|
||||
shouldFailWith :: (HasCallStack, Show a) => (e -> Text) -> Either e a -> Text -> Expectation
|
||||
shouldFailWith p r e = case r of
|
||||
Left e1 -> p e1 `shouldBe` e
|
||||
Right a -> expectationFailure $ "expected parse failure, but succeeded with " <> show a
|
||||
|
||||
shouldSucceed :: (HasCallStack) => (e -> String) -> Either e a -> Expectation
|
||||
shouldSucceed pe r = case r of
|
||||
Left e -> expectationFailure $ "expected parse success, but got: " <> pe e
|
||||
Right _ -> (1 :: Int) `shouldBe` 1
|
|
@ -6,24 +6,24 @@ module Language.SQL.SimpleSQL.FullQueries (fullQueriesTests) where
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
fullQueriesTests :: TestItem
|
||||
fullQueriesTests = Group "queries" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("select count(*) from t"
|
||||
,toQueryExpr $ makeSelect
|
||||
fullQueriesTests = Group "queries" $
|
||||
[q "select count(*) from t"
|
||||
$ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(App [Name Nothing "count"] [Star], Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
}
|
||||
)
|
||||
|
||||
,("select a, sum(c+d) as s\n\
|
||||
,q "select a, sum(c+d) as s\n\
|
||||
\ from t,u\n\
|
||||
\ where a > 5\n\
|
||||
\ group by a\n\
|
||||
\ having count(1) > 5\n\
|
||||
\ order by s"
|
||||
,toQueryExpr $ makeSelect
|
||||
$ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Iden [Name Nothing "a"], Nothing)
|
||||
,(App [Name Nothing "sum"]
|
||||
[BinOp (Iden [Name Nothing "c"])
|
||||
|
@ -36,5 +36,8 @@ fullQueriesTests = Group "queries" $ map (uncurry (TestQueryExpr ansi2011))
|
|||
[Name Nothing ">"] (NumLit "5")
|
||||
,msOrderBy = [SortSpec (Iden [Name Nothing "s"]) DirDefault NullsOrderDefault]
|
||||
}
|
||||
)
|
||||
|
||||
]
|
||||
where
|
||||
q :: HasCallStack => Text -> QueryExpr -> TestItem
|
||||
q src a = testQueryExpr ansi2011 src a
|
||||
|
|
|
@ -6,6 +6,8 @@ module Language.SQL.SimpleSQL.GroupBy (groupByTests) where
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
|
||||
groupByTests :: TestItem
|
||||
|
@ -15,23 +17,31 @@ groupByTests = Group "groupByTests"
|
|||
,randomGroupBy
|
||||
]
|
||||
|
||||
q :: HasCallStack => Text -> QueryExpr -> TestItem
|
||||
q src a = testQueryExpr ansi2011 src a
|
||||
|
||||
p :: HasCallStack => Text -> TestItem
|
||||
p src = testParseQueryExpr ansi2011 src
|
||||
|
||||
|
||||
|
||||
simpleGroupBy :: TestItem
|
||||
simpleGroupBy = Group "simpleGroupBy" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("select a,sum(b) from t group by a"
|
||||
,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
|
||||
simpleGroupBy = Group "simpleGroupBy"
|
||||
[q "select a,sum(b) from t group by a"
|
||||
$ toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
|
||||
,(App [Name Nothing "sum"] [Iden [Name Nothing "b"]],Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
,msGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]]
|
||||
})
|
||||
}
|
||||
|
||||
,("select a,b,sum(c) from t group by a,b"
|
||||
,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
|
||||
,q "select a,b,sum(c) from t group by a,b"
|
||||
$ toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
|
||||
,(Iden [Name Nothing "b"],Nothing)
|
||||
,(App [Name Nothing "sum"] [Iden [Name Nothing "c"]],Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
,msGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]
|
||||
,SimpleGroup $ Iden [Name Nothing "b"]]
|
||||
})
|
||||
}
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -40,15 +50,15 @@ sure which sql version they were introduced, 1999 or 2003 I think).
|
|||
-}
|
||||
|
||||
newGroupBy :: TestItem
|
||||
newGroupBy = Group "newGroupBy" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("select * from t group by ()", ms [GroupingParens []])
|
||||
,("select * from t group by grouping sets ((), (a))"
|
||||
,ms [GroupingSets [GroupingParens []
|
||||
,GroupingParens [SimpleGroup $ Iden [Name Nothing "a"]]]])
|
||||
,("select * from t group by cube(a,b)"
|
||||
,ms [Cube [SimpleGroup $ Iden [Name Nothing "a"], SimpleGroup $ Iden [Name Nothing "b"]]])
|
||||
,("select * from t group by rollup(a,b)"
|
||||
,ms [Rollup [SimpleGroup $ Iden [Name Nothing "a"], SimpleGroup $ Iden [Name Nothing "b"]]])
|
||||
newGroupBy = Group "newGroupBy"
|
||||
[q "select * from t group by ()" $ ms [GroupingParens []]
|
||||
,q "select * from t group by grouping sets ((), (a))"
|
||||
$ ms [GroupingSets [GroupingParens []
|
||||
,GroupingParens [SimpleGroup $ Iden [Name Nothing "a"]]]]
|
||||
,q "select * from t group by cube(a,b)"
|
||||
$ ms [Cube [SimpleGroup $ Iden [Name Nothing "a"], SimpleGroup $ Iden [Name Nothing "b"]]]
|
||||
,q "select * from t group by rollup(a,b)"
|
||||
$ ms [Rollup [SimpleGroup $ Iden [Name Nothing "a"], SimpleGroup $ Iden [Name Nothing "b"]]]
|
||||
]
|
||||
where
|
||||
ms g = toQueryExpr $ makeSelect {msSelectList = [(Star,Nothing)]
|
||||
|
@ -56,21 +66,21 @@ newGroupBy = Group "newGroupBy" $ map (uncurry (TestQueryExpr ansi2011))
|
|||
,msGroupBy = g}
|
||||
|
||||
randomGroupBy :: TestItem
|
||||
randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
||||
["select * from t GROUP BY a"
|
||||
,"select * from t GROUP BY GROUPING SETS((a))"
|
||||
,"select * from t GROUP BY a,b,c"
|
||||
,"select * from t GROUP BY GROUPING SETS((a,b,c))"
|
||||
,"select * from t GROUP BY ROLLUP(a,b)"
|
||||
,"select * from t GROUP BY GROUPING SETS((a,b),\n\
|
||||
randomGroupBy = Group "randomGroupBy"
|
||||
[p "select * from t GROUP BY a"
|
||||
,p "select * from t GROUP BY GROUPING SETS((a))"
|
||||
,p "select * from t GROUP BY a,b,c"
|
||||
,p "select * from t GROUP BY GROUPING SETS((a,b,c))"
|
||||
,p "select * from t GROUP BY ROLLUP(a,b)"
|
||||
,p "select * from t GROUP BY GROUPING SETS((a,b),\n\
|
||||
\(a),\n\
|
||||
\() )"
|
||||
,"select * from t GROUP BY ROLLUP(b,a)"
|
||||
,"select * from t GROUP BY GROUPING SETS((b,a),\n\
|
||||
,p "select * from t GROUP BY ROLLUP(b,a)"
|
||||
,p "select * from t GROUP BY GROUPING SETS((b,a),\n\
|
||||
\(b),\n\
|
||||
\() )"
|
||||
,"select * from t GROUP BY CUBE(a,b,c)"
|
||||
,"select * from t GROUP BY GROUPING SETS((a,b,c),\n\
|
||||
,p "select * from t GROUP BY CUBE(a,b,c)"
|
||||
,p "select * from t GROUP BY GROUPING SETS((a,b,c),\n\
|
||||
\(a,b),\n\
|
||||
\(a,c),\n\
|
||||
\(b,c),\n\
|
||||
|
@ -78,33 +88,33 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
\(b),\n\
|
||||
\(c),\n\
|
||||
\() )"
|
||||
,"select * from t GROUP BY ROLLUP(Province, County, City)"
|
||||
,"select * from t GROUP BY ROLLUP(Province, (County, City))"
|
||||
,"select * from t GROUP BY ROLLUP(Province, (County, City))"
|
||||
,"select * from t GROUP BY GROUPING SETS((Province, County, City),\n\
|
||||
,p "select * from t GROUP BY ROLLUP(Province, County, City)"
|
||||
,p "select * from t GROUP BY ROLLUP(Province, (County, City))"
|
||||
,p "select * from t GROUP BY ROLLUP(Province, (County, City))"
|
||||
,p "select * from t GROUP BY GROUPING SETS((Province, County, City),\n\
|
||||
\(Province),\n\
|
||||
\() )"
|
||||
,"select * from t GROUP BY GROUPING SETS((Province, County, City),\n\
|
||||
,p "select * from t GROUP BY GROUPING SETS((Province, County, City),\n\
|
||||
\(Province, County),\n\
|
||||
\(Province),\n\
|
||||
\() )"
|
||||
,"select * from t GROUP BY a, ROLLUP(b,c)"
|
||||
,"select * from t GROUP BY GROUPING SETS((a,b,c),\n\
|
||||
,p "select * from t GROUP BY a, ROLLUP(b,c)"
|
||||
,p "select * from t GROUP BY GROUPING SETS((a,b,c),\n\
|
||||
\(a,b),\n\
|
||||
\(a) )"
|
||||
,"select * from t GROUP BY a, b, ROLLUP(c,d)"
|
||||
,"select * from t GROUP BY GROUPING SETS((a,b,c,d),\n\
|
||||
,p "select * from t GROUP BY a, b, ROLLUP(c,d)"
|
||||
,p "select * from t GROUP BY GROUPING SETS((a,b,c,d),\n\
|
||||
\(a,b,c),\n\
|
||||
\(a,b) )"
|
||||
,"select * from t GROUP BY ROLLUP(a), ROLLUP(b,c)"
|
||||
,"select * from t GROUP BY GROUPING SETS((a,b,c),\n\
|
||||
,p "select * from t GROUP BY ROLLUP(a), ROLLUP(b,c)"
|
||||
,p "select * from t GROUP BY GROUPING SETS((a,b,c),\n\
|
||||
\(a,b),\n\
|
||||
\(a),\n\
|
||||
\(b,c),\n\
|
||||
\(b),\n\
|
||||
\() )"
|
||||
,"select * from t GROUP BY ROLLUP(a), CUBE(b,c)"
|
||||
,"select * from t GROUP BY GROUPING SETS((a,b,c),\n\
|
||||
,p "select * from t GROUP BY ROLLUP(a), CUBE(b,c)"
|
||||
,p "select * from t GROUP BY GROUPING SETS((a,b,c),\n\
|
||||
\(a,b),\n\
|
||||
\(a,c),\n\
|
||||
\(a),\n\
|
||||
|
@ -112,8 +122,8 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
\(b),\n\
|
||||
\(c),\n\
|
||||
\() )"
|
||||
,"select * from t GROUP BY CUBE(a,b), ROLLUP(c,d)"
|
||||
,"select * from t GROUP BY GROUPING SETS((a,b,c,d),\n\
|
||||
,p "select * from t GROUP BY CUBE(a,b), ROLLUP(c,d)"
|
||||
,p "select * from t GROUP BY GROUPING SETS((a,b,c,d),\n\
|
||||
\(a,b,c),\n\
|
||||
\(a,b),\n\
|
||||
\(a,c,d),\n\
|
||||
|
@ -125,16 +135,16 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
\(c,d),\n\
|
||||
\(c),\n\
|
||||
\() )"
|
||||
,"select * from t GROUP BY a, ROLLUP(a,b)"
|
||||
,"select * from t GROUP BY GROUPING SETS((a,b),\n\
|
||||
,p "select * from t GROUP BY a, ROLLUP(a,b)"
|
||||
,p "select * from t GROUP BY GROUPING SETS((a,b),\n\
|
||||
\(a) )"
|
||||
,"select * from t GROUP BY Region,\n\
|
||||
,p "select * from t GROUP BY Region,\n\
|
||||
\ROLLUP(Sales_Person, WEEK(Sales_Date)),\n\
|
||||
\CUBE(YEAR(Sales_Date), MONTH (Sales_Date))"
|
||||
,"select * from t GROUP BY ROLLUP (Region, Sales_Person, WEEK(Sales_Date),\n\
|
||||
,p "select * from t GROUP BY ROLLUP (Region, Sales_Person, WEEK(Sales_Date),\n\
|
||||
\YEAR(Sales_Date), MONTH(Sales_Date) )"
|
||||
|
||||
,"SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
,p "SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
\DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
|
||||
\SALES_PERSON, SUM(SALES) AS UNITS_SOLD\n\
|
||||
\FROM SALES\n\
|
||||
|
@ -142,7 +152,7 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
\GROUP BY WEEK(SALES_DATE), DAYOFWEEK(SALES_DATE), SALES_PERSON\n\
|
||||
\ORDER BY WEEK, DAY_WEEK, SALES_PERSON"
|
||||
|
||||
,"SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
,p "SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
\DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
|
||||
\SALES_PERSON, SUM(SALES) AS UNITS_SOLD\n\
|
||||
\FROM SALES\n\
|
||||
|
@ -151,7 +161,7 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
\(DAYOFWEEK(SALES_DATE), SALES_PERSON))\n\
|
||||
\ORDER BY WEEK, DAY_WEEK, SALES_PERSON"
|
||||
|
||||
,"SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
,p "SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
\DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
|
||||
\SALES_PERSON, SUM(SALES) AS UNITS_SOLD\n\
|
||||
\FROM SALES\n\
|
||||
|
@ -159,7 +169,7 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
\GROUP BY ROLLUP ( WEEK(SALES_DATE), DAYOFWEEK(SALES_DATE), SALES_PERSON )\n\
|
||||
\ORDER BY WEEK, DAY_WEEK, SALES_PERSON"
|
||||
|
||||
,"SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
,p "SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
\DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
|
||||
\SALES_PERSON, SUM(SALES) AS UNITS_SOLD\n\
|
||||
\FROM SALES\n\
|
||||
|
@ -167,7 +177,7 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
\GROUP BY CUBE ( WEEK(SALES_DATE), DAYOFWEEK(SALES_DATE), SALES_PERSON )\n\
|
||||
\ORDER BY WEEK, DAY_WEEK, SALES_PERSON"
|
||||
|
||||
,"SELECT SALES_PERSON,\n\
|
||||
,p "SELECT SALES_PERSON,\n\
|
||||
\MONTH(SALES_DATE) AS MONTH,\n\
|
||||
\SUM(SALES) AS UNITS_SOLD\n\
|
||||
\FROM SALES\n\
|
||||
|
@ -176,21 +186,21 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
\)\n\
|
||||
\ORDER BY SALES_PERSON, MONTH"
|
||||
|
||||
,"SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
,p "SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
\DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
|
||||
\SUM(SALES) AS UNITS_SOLD\n\
|
||||
\FROM SALES\n\
|
||||
\GROUP BY ROLLUP ( WEEK(SALES_DATE), DAYOFWEEK(SALES_DATE) )\n\
|
||||
\ORDER BY WEEK, DAY_WEEK"
|
||||
|
||||
,"SELECT MONTH(SALES_DATE) AS MONTH,\n\
|
||||
,p "SELECT MONTH(SALES_DATE) AS MONTH,\n\
|
||||
\REGION,\n\
|
||||
\SUM(SALES) AS UNITS_SOLD\n\
|
||||
\FROM SALES\n\
|
||||
\GROUP BY ROLLUP ( MONTH(SALES_DATE), REGION )\n\
|
||||
\ORDER BY MONTH, REGION"
|
||||
|
||||
,"SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
,p "SELECT WEEK(SALES_DATE) AS WEEK,\n\
|
||||
\DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
|
||||
\MONTH(SALES_DATE) AS MONTH,\n\
|
||||
\REGION,\n\
|
||||
|
@ -200,7 +210,7 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
\ROLLUP( MONTH(SALES_DATE), REGION ) )\n\
|
||||
\ORDER BY WEEK, DAY_WEEK, MONTH, REGION"
|
||||
|
||||
,"SELECT R1, R2,\n\
|
||||
,p "SELECT R1, R2,\n\
|
||||
\WEEK(SALES_DATE) AS WEEK,\n\
|
||||
\DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
|
||||
\MONTH(SALES_DATE) AS MONTH,\n\
|
||||
|
@ -211,7 +221,7 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
\(R2,ROLLUP( MONTH(SALES_DATE), REGION ) ))\n\
|
||||
\ORDER BY WEEK, DAY_WEEK, MONTH, REGION"
|
||||
|
||||
{-,"SELECT COALESCE(R1,R2) AS GROUP,\n\
|
||||
{-,p "SELECT COALESCE(R1,R2) AS GROUP,\n\
|
||||
\WEEK(SALES_DATE) AS WEEK,\n\
|
||||
\DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
|
||||
\MONTH(SALES_DATE) AS MONTH,\n\
|
||||
|
@ -226,7 +236,7 @@ randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
|
|||
-- decimal as a function not allowed due to the reserved keyword
|
||||
-- handling: todo, review if this is ansi standard function or
|
||||
-- if there are places where reserved keywords can still be used
|
||||
,"SELECT MONTH(SALES_DATE) AS MONTH,\n\
|
||||
,p "SELECT MONTH(SALES_DATE) AS MONTH,\n\
|
||||
\REGION,\n\
|
||||
\SUM(SALES) AS UNITS_SOLD,\n\
|
||||
\MAX(SALES) AS BEST_SALE,\n\
|
||||
|
|
|
@ -23,6 +23,7 @@ import Language.SQL.SimpleSQL.Lex
|
|||
(Token(..)
|
||||
,tokenListWillPrintAndLex
|
||||
)
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Data.Text (Text)
|
||||
|
@ -39,50 +40,57 @@ lexerTests = Group "lexerTests" $
|
|||
,sqlServerLexerTests
|
||||
,oracleLexerTests
|
||||
,mySqlLexerTests
|
||||
,odbcLexerTests]
|
||||
,odbcLexerTests
|
||||
]
|
||||
|
||||
-- quick sanity tests to see something working
|
||||
bootstrapTests :: TestItem
|
||||
bootstrapTests = Group "bootstrap tests" [Group "bootstrap tests" $
|
||||
map (uncurry (LexTest ansi2011)) (
|
||||
[("iden", [Identifier Nothing "iden"])
|
||||
,("'string'", [SqlString "'" "'" "string"])
|
||||
bootstrapTests = Group "bootstrap tests" $
|
||||
[t "iden" [Identifier Nothing "iden"]
|
||||
|
||||
,(" ", [Whitespace " "])
|
||||
,("\t ", [Whitespace "\t "])
|
||||
,(" \n ", [Whitespace " \n "])
|
||||
,t "\"a1normal \"\" iden\"" [Identifier (Just ("\"","\"")) "a1normal \"\" iden"]
|
||||
|
||||
,("--", [LineComment "--"])
|
||||
,("--\n", [LineComment "--\n"])
|
||||
,("--stuff", [LineComment "--stuff"])
|
||||
,("-- stuff", [LineComment "-- stuff"])
|
||||
,("-- stuff\n", [LineComment "-- stuff\n"])
|
||||
,("--\nstuff", [LineComment "--\n", Identifier Nothing "stuff"])
|
||||
,("-- com \nstuff", [LineComment "-- com \n", Identifier Nothing "stuff"])
|
||||
,t "'string'" [SqlString "'" "'" "string"]
|
||||
|
||||
,("/*test1*/", [BlockComment "/*test1*/"])
|
||||
,("/**/", [BlockComment "/**/"])
|
||||
,("/***/", [BlockComment "/***/"])
|
||||
,("/* * */", [BlockComment "/* * */"])
|
||||
,("/*test*/", [BlockComment "/*test*/"])
|
||||
,("/*te/*st*/", [BlockComment "/*te/*st*/"])
|
||||
,("/*te*st*/", [BlockComment "/*te*st*/"])
|
||||
,("/*lines\nmore lines*/", [BlockComment "/*lines\nmore lines*/"])
|
||||
,("/*test1*/\n", [BlockComment "/*test1*/", Whitespace "\n"])
|
||||
,("/*test1*/stuff", [BlockComment "/*test1*/", Identifier Nothing "stuff"])
|
||||
,t " " [Whitespace " "]
|
||||
,t "\t " [Whitespace "\t "]
|
||||
,t " \n " [Whitespace " \n "]
|
||||
|
||||
,("1", [SqlNumber "1"])
|
||||
,("42", [SqlNumber "42"])
|
||||
,t "--" [LineComment "--"]
|
||||
,t "--\n" [LineComment "--\n"]
|
||||
,t "--stuff" [LineComment "--stuff"]
|
||||
,t "-- stuff" [LineComment "-- stuff"]
|
||||
,t "-- stuff\n" [LineComment "-- stuff\n"]
|
||||
,t "--\nstuff" [LineComment "--\n", Identifier Nothing "stuff"]
|
||||
,t "-- com \nstuff" [LineComment "-- com \n", Identifier Nothing "stuff"]
|
||||
|
||||
-- have to fix the dialect handling in the tests
|
||||
--,("$1", [PositionalArg 1])
|
||||
--,("$200", [PositionalArg 200])
|
||||
,t "/*test1*/" [BlockComment "/*test1*/"]
|
||||
,t "/**/" [BlockComment "/**/"]
|
||||
,t "/***/" [BlockComment "/***/"]
|
||||
,t "/* * */" [BlockComment "/* * */"]
|
||||
,t "/*test*/" [BlockComment "/*test*/"]
|
||||
,t "/*te/*st*/*/" [BlockComment "/*te/*st*/*/"]
|
||||
,t "/*te*st*/" [BlockComment "/*te*st*/"]
|
||||
,t "/*lines\nmore lines*/" [BlockComment "/*lines\nmore lines*/"]
|
||||
,t "/*test1*/\n" [BlockComment "/*test1*/", Whitespace "\n"]
|
||||
,t "/*test1*/stuff" [BlockComment "/*test1*/", Identifier Nothing "stuff"]
|
||||
|
||||
,(":test", [PrefixedVariable ':' "test"])
|
||||
,t "1" [SqlNumber "1"]
|
||||
,t "42" [SqlNumber "42"]
|
||||
|
||||
] ++ map (\a -> (a, [Symbol a])) (
|
||||
,tp "$1" [PositionalArg 1]
|
||||
,tp "$200" [PositionalArg 200]
|
||||
|
||||
,t ":test" [PrefixedVariable ':' "test"]
|
||||
|
||||
] ++ map (\a -> t a [Symbol a]) (
|
||||
["!=", "<>", ">=", "<=", "||"]
|
||||
++ map T.singleton ("(),-+*/<>=." :: [Char])))]
|
||||
++ map T.singleton ("(),-+*/<>=." :: [Char]))
|
||||
where
|
||||
t :: HasCallStack => Text -> [Token] -> TestItem
|
||||
t src ast = testLex ansi2011 src ast
|
||||
tp :: HasCallStack => Text -> [Token] -> TestItem
|
||||
tp src ast = testLex ansi2011{diPositionalArg=True} src ast
|
||||
|
||||
|
||||
ansiLexerTable :: [(Text,[Token])]
|
||||
|
@ -103,7 +111,7 @@ ansiLexerTable =
|
|||
)
|
||||
-- quoted identifiers with embedded double quotes
|
||||
-- the lexer doesn't unescape the quotes
|
||||
++ [("\"normal \"\" iden\"", [Identifier (Just ("\"","\"")) "normal \"\" iden"])]
|
||||
++ [("\"anormal \"\" iden\"", [Identifier (Just ("\"","\"")) "anormal \"\" iden"])]
|
||||
-- strings
|
||||
-- the lexer doesn't apply escapes at all
|
||||
++ [("'string'", [SqlString "'" "'" "string"])
|
||||
|
@ -137,39 +145,44 @@ ansiLexerTable =
|
|||
|
||||
ansiLexerTests :: TestItem
|
||||
ansiLexerTests = Group "ansiLexerTests" $
|
||||
[Group "ansi lexer token tests" $ [LexTest ansi2011 s t | (s,t) <- ansiLexerTable]
|
||||
[Group "ansi lexer token tests" $ [l s t | (s,t) <- ansiLexerTable]
|
||||
,Group "ansi generated combination lexer tests" $
|
||||
[ LexTest ansi2011 (s <> s1) (t <> t1)
|
||||
| (s,t) <- ansiLexerTable
|
||||
, (s1,t1) <- ansiLexerTable
|
||||
, tokenListWillPrintAndLex ansi2011 $ t <> t1
|
||||
[ l (s <> s1) (t <> t1)
|
||||
| (s,t) <- ansiLexerTable
|
||||
, (s1,t1) <- ansiLexerTable
|
||||
, tokenListWillPrintAndLex ansi2011 $ t <> t1
|
||||
|
||||
]
|
||||
]
|
||||
,Group "ansiadhoclexertests" $
|
||||
map (uncurry $ LexTest ansi2011)
|
||||
[("", [])
|
||||
,("-- line com\nstuff", [LineComment "-- line com\n",Identifier Nothing "stuff"])
|
||||
] ++
|
||||
[-- want to make sure this gives a parse error
|
||||
LexFails ansi2011 "*/"
|
||||
-- combinations of pipes: make sure they fail because they could be
|
||||
-- ambiguous and it is really unclear when they are or not, and
|
||||
-- what the result is even when they are not ambiguous
|
||||
,LexFails ansi2011 "|||"
|
||||
,LexFails ansi2011 "||||"
|
||||
,LexFails ansi2011 "|||||"
|
||||
-- another user experience thing: make sure extra trailing
|
||||
-- number chars are rejected rather than attempting to parse
|
||||
-- if the user means to write something that is rejected by this code,
|
||||
-- then they can use whitespace to make it clear and then it will parse
|
||||
,LexFails ansi2011 "12e3e4"
|
||||
,LexFails ansi2011 "12e3e4"
|
||||
,LexFails ansi2011 "12e3e4"
|
||||
,LexFails ansi2011 "12e3.4"
|
||||
,LexFails ansi2011 "12.4.5"
|
||||
,LexFails ansi2011 "12.4e5.6"
|
||||
,LexFails ansi2011 "12.4e5e7"]
|
||||
]
|
||||
[l "" []
|
||||
,l "-- line com\nstuff" [LineComment "-- line com\n",Identifier Nothing "stuff"]
|
||||
] ++
|
||||
[-- want to make sure this gives a parse error
|
||||
f "*/"
|
||||
-- combinations of pipes: make sure they fail because they could be
|
||||
-- ambiguous and it is really unclear when they are or not, and
|
||||
-- what the result is even when they are not ambiguous
|
||||
,f "|||"
|
||||
,f "||||"
|
||||
,f "|||||"
|
||||
-- another user experience thing: make sure extra trailing
|
||||
-- number chars are rejected rather than attempting to parse
|
||||
-- if the user means to write something that is rejected by this code,
|
||||
-- then they can use whitespace to make it clear and then it will parse
|
||||
,f "12e3e4"
|
||||
,f "12e3e4"
|
||||
,f "12e3e4"
|
||||
,f "12e3.4"
|
||||
,f "12.4.5"
|
||||
,f "12.4e5.6"
|
||||
,f "12.4e5e7"]
|
||||
]
|
||||
where
|
||||
l :: HasCallStack => Text -> [Token] -> TestItem
|
||||
l src ast = testLex ansi2011 src ast
|
||||
f :: HasCallStack => Text -> TestItem
|
||||
f src = lexFails ansi2011 src
|
||||
|
||||
|
||||
{-
|
||||
todo: lexing tests
|
||||
|
@ -303,22 +316,21 @@ somePostgresOpsWhichWontAddTrailingPlusMinus l =
|
|||
, not (T.last x `T.elem` "+-")
|
||||
]
|
||||
|
||||
|
||||
postgresLexerTests :: TestItem
|
||||
postgresLexerTests = Group "postgresLexerTests" $
|
||||
[Group "postgres lexer token tests" $
|
||||
[LexTest postgres s t | (s,t) <- postgresLexerTable]
|
||||
[l s t | (s,t) <- postgresLexerTable]
|
||||
,Group "postgres generated lexer token tests" $
|
||||
[LexTest postgres s t | (s,t) <- postgresShortOperatorTable ++ postgresExtraOperatorTable]
|
||||
[l s t | (s,t) <- postgresShortOperatorTable ++ postgresExtraOperatorTable]
|
||||
,Group "postgres generated combination lexer tests" $
|
||||
[ LexTest postgres (s <> s1) (t <> t1)
|
||||
[ l (s <> s1) (t <> t1)
|
||||
| (s,t) <- postgresLexerTable ++ postgresShortOperatorTable
|
||||
, (s1,t1) <- postgresLexerTable ++ postgresShortOperatorTable
|
||||
, tokenListWillPrintAndLex postgres $ t ++ t1
|
||||
|
||||
]
|
||||
,Group "generated postgres edgecase lexertests" $
|
||||
[LexTest postgres s t
|
||||
[l s t
|
||||
| (s,t) <- edgeCaseCommentOps
|
||||
++ edgeCasePlusMinusOps
|
||||
++ edgeCasePlusMinusComments]
|
||||
|
@ -326,22 +338,23 @@ postgresLexerTests = Group "postgresLexerTests" $
|
|||
,Group "adhoc postgres lexertests" $
|
||||
-- need more tests for */ to make sure it is caught if it is in the middle of a
|
||||
-- sequence of symbol letters
|
||||
[LexFails postgres "*/"
|
||||
,LexFails postgres ":::"
|
||||
,LexFails postgres "::::"
|
||||
,LexFails postgres ":::::"
|
||||
,LexFails postgres "@*/"
|
||||
,LexFails postgres "-*/"
|
||||
,LexFails postgres "12e3e4"
|
||||
,LexFails postgres "12e3e4"
|
||||
,LexFails postgres "12e3e4"
|
||||
,LexFails postgres "12e3.4"
|
||||
,LexFails postgres "12.4.5"
|
||||
,LexFails postgres "12.4e5.6"
|
||||
,LexFails postgres "12.4e5e7"
|
||||
[f "*/"
|
||||
,f ":::"
|
||||
,f "::::"
|
||||
,f ":::::"
|
||||
,f "@*/"
|
||||
,f "-*/"
|
||||
,f "12e3e4"
|
||||
,f "12e3e4"
|
||||
,f "12e3e4"
|
||||
,f "12e3.4"
|
||||
,f "12.4.5"
|
||||
,f "12.4e5.6"
|
||||
,f "12.4e5e7"
|
||||
-- special case allow this to lex to 1 .. 2
|
||||
-- this is for 'for loops' in plpgsql
|
||||
,LexTest postgres "1..2" [SqlNumber "1", Symbol "..", SqlNumber "2"]]
|
||||
,l "1..2" [SqlNumber "1", Symbol "..", SqlNumber "2"]
|
||||
]
|
||||
]
|
||||
where
|
||||
edgeCaseCommentOps =
|
||||
|
@ -365,14 +378,21 @@ postgresLexerTests = Group "postgresLexerTests" $
|
|||
,("-/**/", [Symbol "-", BlockComment "/**/"])
|
||||
,("+/**/", [Symbol "+", BlockComment "/**/"])
|
||||
]
|
||||
l :: HasCallStack => Text -> [Token] -> TestItem
|
||||
l src ast = testLex postgres src ast
|
||||
f :: HasCallStack => Text -> TestItem
|
||||
f src = lexFails postgres src
|
||||
|
||||
sqlServerLexerTests :: TestItem
|
||||
sqlServerLexerTests = Group "sqlServerLexTests" $
|
||||
[ LexTest sqlserver s t | (s,t) <-
|
||||
[l s t | (s,t) <-
|
||||
[("@variable", [(PrefixedVariable '@' "variable")])
|
||||
,("#variable", [(PrefixedVariable '#' "variable")])
|
||||
,("[quoted identifier]", [(Identifier (Just ("[", "]")) "quoted identifier")])
|
||||
]]
|
||||
where
|
||||
l :: HasCallStack => Text -> [Token] -> TestItem
|
||||
l src ast = testLex sqlserver src ast
|
||||
|
||||
oracleLexerTests :: TestItem
|
||||
oracleLexerTests = Group "oracleLexTests" $
|
||||
|
@ -380,19 +400,29 @@ oracleLexerTests = Group "oracleLexTests" $
|
|||
|
||||
mySqlLexerTests :: TestItem
|
||||
mySqlLexerTests = Group "mySqlLexerTests" $
|
||||
[ LexTest mysql s t | (s,t) <-
|
||||
[ l s t | (s,t) <-
|
||||
[("`quoted identifier`", [(Identifier (Just ("`", "`")) "quoted identifier")])
|
||||
]
|
||||
]
|
||||
where
|
||||
l :: HasCallStack => Text -> [Token] -> TestItem
|
||||
l src ast = testLex mysql src ast
|
||||
|
||||
odbcLexerTests :: TestItem
|
||||
odbcLexerTests = Group "odbcLexTests" $
|
||||
[ LexTest sqlserver {diOdbc = True} s t | (s,t) <-
|
||||
[ lo s t | (s,t) <-
|
||||
[("{}", [Symbol "{", Symbol "}"])
|
||||
]]
|
||||
++ [LexFails sqlserver {diOdbc = False} "{"
|
||||
,LexFails sqlserver {diOdbc = False} "}"]
|
||||
++ [lno "{"
|
||||
,lno "}"]
|
||||
where
|
||||
lo :: HasCallStack => Text -> [Token] -> TestItem
|
||||
lo src ast = testLex (sqlserver {diOdbc = True}) src ast
|
||||
lno :: HasCallStack => Text -> TestItem
|
||||
lno src = lexFails (sqlserver{diOdbc = False}) src
|
||||
|
||||
|
||||
combos :: [Char] -> Int -> [Text]
|
||||
combos _ 0 = [T.empty]
|
||||
combos l n = [ T.cons x tl | x <- l, tl <- combos l (n - 1) ]
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ module Language.SQL.SimpleSQL.MySQL (mySQLTests) where
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
|
||||
mySQLTests :: TestItem
|
||||
mySQLTests = Group "mysql dialect"
|
||||
|
@ -21,21 +22,16 @@ limit syntax
|
|||
-}
|
||||
|
||||
backtickQuotes :: TestItem
|
||||
backtickQuotes = Group "backtickQuotes" (map (uncurry (TestScalarExpr mysql))
|
||||
[("`test`", Iden [Name (Just ("`","`")) "test"])
|
||||
]
|
||||
++ [ParseScalarExprFails ansi2011 "`test`"]
|
||||
)
|
||||
backtickQuotes = Group "backtickQuotes"
|
||||
[testScalarExpr mysql "`test`" $ Iden [Name (Just ("`","`")) "test"]
|
||||
,testParseScalarExprFails ansi2011 "`test`"]
|
||||
|
||||
limit :: TestItem
|
||||
limit = Group "queries" ( map (uncurry (TestQueryExpr mysql))
|
||||
[("select * from t limit 5"
|
||||
,toQueryExpr $ sel {msFetchFirst = Just (NumLit "5")}
|
||||
)
|
||||
]
|
||||
++ [ParseQueryExprFails mysql "select a from t fetch next 10 rows only;"
|
||||
,ParseQueryExprFails ansi2011 "select * from t limit 5"]
|
||||
)
|
||||
limit = Group "queries"
|
||||
[testQueryExpr mysql "select * from t limit 5"
|
||||
$ toQueryExpr $ sel {msFetchFirst = Just (NumLit "5")}
|
||||
,testParseQueryExprFails mysql "select a from t fetch next 10 rows only;"
|
||||
,testParseQueryExprFails ansi2011 "select * from t limit 5"]
|
||||
where
|
||||
sel = makeSelect
|
||||
{msSelectList = [(Star, Nothing)]
|
||||
|
|
|
@ -4,6 +4,8 @@ module Language.SQL.SimpleSQL.Odbc (odbcTests) where
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
odbcTests :: TestItem
|
||||
odbcTests = Group "odbc" [
|
||||
|
@ -30,14 +32,14 @@ odbcTests = Group "odbc" [
|
|||
,iden "SQL_DATE"])
|
||||
]
|
||||
,Group "outer join" [
|
||||
TestQueryExpr ansi2011 {diOdbc=True}
|
||||
q
|
||||
"select * from {oj t1 left outer join t2 on expr}"
|
||||
$ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star,Nothing)]
|
||||
,msFrom = [TROdbc $ TRJoin (TRSimple [Name Nothing "t1"]) False JLeft (TRSimple [Name Nothing "t2"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])]}]
|
||||
,Group "check parsing bugs" [
|
||||
TestQueryExpr ansi2011 {diOdbc=True}
|
||||
q
|
||||
"select {fn CONVERT(cint,SQL_BIGINT)} from t;"
|
||||
$ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(OdbcFunc (ap "CONVERT"
|
||||
|
@ -46,7 +48,12 @@ odbcTests = Group "odbc" [
|
|||
,msFrom = [TRSimple [Name Nothing "t"]]}]
|
||||
]
|
||||
where
|
||||
e = TestScalarExpr ansi2011 {diOdbc = True}
|
||||
e :: HasCallStack => Text -> ScalarExpr -> TestItem
|
||||
e src ast = testScalarExpr ansi2011{diOdbc = True} src ast
|
||||
|
||||
q :: HasCallStack => Text -> QueryExpr -> TestItem
|
||||
q src ast = testQueryExpr ansi2011{diOdbc = True} src ast
|
||||
|
||||
--tsql = ParseProcSql defaultParseFlags {pfDialect=sqlServerDialect}
|
||||
ap n = App [Name Nothing n]
|
||||
iden n = Iden [Name Nothing n]
|
||||
|
|
|
@ -6,6 +6,7 @@ module Language.SQL.SimpleSQL.Oracle (oracleTests) where
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
|
||||
oracleTests :: TestItem
|
||||
oracleTests = Group "oracle dialect"
|
||||
|
@ -13,18 +14,18 @@ oracleTests = Group "oracle dialect"
|
|||
|
||||
|
||||
oracleLobUnits :: TestItem
|
||||
oracleLobUnits = Group "oracleLobUnits" (map (uncurry (TestScalarExpr oracle))
|
||||
[("cast (a as varchar2(3 char))"
|
||||
,Cast (Iden [Name Nothing "a"]) (
|
||||
PrecLengthTypeName [Name Nothing "varchar2"] 3 Nothing (Just PrecCharacters)))
|
||||
,("cast (a as varchar2(3 byte))"
|
||||
,Cast (Iden [Name Nothing "a"]) (
|
||||
PrecLengthTypeName [Name Nothing "varchar2"] 3 Nothing (Just PrecOctets)))
|
||||
]
|
||||
++ [TestStatement oracle
|
||||
oracleLobUnits = Group "oracleLobUnits"
|
||||
[testScalarExpr oracle "cast (a as varchar2(3 char))"
|
||||
$ Cast (Iden [Name Nothing "a"]) (
|
||||
PrecLengthTypeName [Name Nothing "varchar2"] 3 Nothing (Just PrecCharacters))
|
||||
,testScalarExpr oracle "cast (a as varchar2(3 byte))"
|
||||
$ Cast (Iden [Name Nothing "a"]) (
|
||||
PrecLengthTypeName [Name Nothing "varchar2"] 3 Nothing (Just PrecOctets))
|
||||
,testStatement oracle
|
||||
"create table t (a varchar2(55 BYTE));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a")
|
||||
(PrecLengthTypeName [Name Nothing "varchar2"] 55 Nothing (Just PrecOctets))
|
||||
Nothing []]]
|
||||
)
|
||||
Nothing []]
|
||||
]
|
||||
|
||||
|
|
|
@ -9,9 +9,11 @@ revisited when the dialect support is added.
|
|||
module Language.SQL.SimpleSQL.Postgres (postgresTests) where
|
||||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
postgresTests :: TestItem
|
||||
postgresTests = Group "postgresTests" $ map (ParseQueryExpr ansi2011)
|
||||
postgresTests = Group "postgresTests"
|
||||
|
||||
{-
|
||||
lexical syntax section
|
||||
|
@ -22,129 +24,129 @@ TODO: get all the commented out tests working
|
|||
[-- "SELECT 'foo'\n\
|
||||
-- \'bar';" -- this should parse as select 'foobar'
|
||||
-- ,
|
||||
"SELECT name, (SELECT max(pop) FROM cities\n\
|
||||
t "SELECT name, (SELECT max(pop) FROM cities\n\
|
||||
\ WHERE cities.state = states.name)\n\
|
||||
\ FROM states;"
|
||||
,"SELECT ROW(1,2.5,'this is a test');"
|
||||
,t "SELECT ROW(1,2.5,'this is a test');"
|
||||
|
||||
,"SELECT ROW(t.*, 42) FROM t;"
|
||||
,"SELECT ROW(t.f1, t.f2, 42) FROM t;"
|
||||
,"SELECT getf1(CAST(ROW(11,'this is a test',2.5) AS myrowtype));"
|
||||
,t "SELECT ROW(t.*, 42) FROM t;"
|
||||
,t "SELECT ROW(t.f1, t.f2, 42) FROM t;"
|
||||
,t "SELECT getf1(CAST(ROW(11,'this is a test',2.5) AS myrowtype));"
|
||||
|
||||
,"SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same');"
|
||||
,t "SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same');"
|
||||
|
||||
-- table is a reservered keyword?
|
||||
--,"SELECT ROW(table.*) IS NULL FROM table;"
|
||||
,"SELECT ROW(tablex.*) IS NULL FROM tablex;"
|
||||
--,t "SELECT ROW(table.*) IS NULL FROM table;"
|
||||
,t "SELECT ROW(tablex.*) IS NULL FROM tablex;"
|
||||
|
||||
,"SELECT true OR somefunc();"
|
||||
,t "SELECT true OR somefunc();"
|
||||
|
||||
,"SELECT somefunc() OR true;"
|
||||
,t "SELECT somefunc() OR true;"
|
||||
|
||||
-- queries section
|
||||
|
||||
,"SELECT * FROM t1 CROSS JOIN t2;"
|
||||
,"SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;"
|
||||
,"SELECT * FROM t1 INNER JOIN t2 USING (num);"
|
||||
,"SELECT * FROM t1 NATURAL INNER JOIN t2;"
|
||||
,"SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num;"
|
||||
,"SELECT * FROM t1 LEFT JOIN t2 USING (num);"
|
||||
,"SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num;"
|
||||
,"SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num;"
|
||||
,"SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = 'xxx';"
|
||||
,"SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx';"
|
||||
,t "SELECT * FROM t1 CROSS JOIN t2;"
|
||||
,t "SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;"
|
||||
,t "SELECT * FROM t1 INNER JOIN t2 USING (num);"
|
||||
,t "SELECT * FROM t1 NATURAL INNER JOIN t2;"
|
||||
,t "SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num;"
|
||||
,t "SELECT * FROM t1 LEFT JOIN t2 USING (num);"
|
||||
,t "SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num;"
|
||||
,t "SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num;"
|
||||
,t "SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = 'xxx';"
|
||||
,t "SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx';"
|
||||
|
||||
,"SELECT * FROM some_very_long_table_name s\n\
|
||||
,t "SELECT * FROM some_very_long_table_name s\n\
|
||||
\JOIN another_fairly_long_name a ON s.id = a.num;"
|
||||
,"SELECT * FROM people AS mother JOIN people AS child\n\
|
||||
,t "SELECT * FROM people AS mother JOIN people AS child\n\
|
||||
\ ON mother.id = child.mother_id;"
|
||||
,"SELECT * FROM my_table AS a CROSS JOIN my_table AS b;"
|
||||
,"SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b;"
|
||||
,"SELECT * FROM getfoo(1) AS t1;"
|
||||
,"SELECT * FROM foo\n\
|
||||
,t "SELECT * FROM my_table AS a CROSS JOIN my_table AS b;"
|
||||
,t "SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b;"
|
||||
,t "SELECT * FROM getfoo(1) AS t1;"
|
||||
,t "SELECT * FROM foo\n\
|
||||
\ WHERE foosubid IN (\n\
|
||||
\ SELECT foosubid\n\
|
||||
\ FROM getfoo(foo.fooid) z\n\
|
||||
\ WHERE z.fooid = foo.fooid\n\
|
||||
\ );"
|
||||
{-,"SELECT *\n\
|
||||
{-,t "SELECT *\n\
|
||||
\ FROM dblink('dbname=mydb', 'SELECT proname, prosrc FROM pg_proc')\n\
|
||||
\ AS t1(proname name, prosrc text)\n\
|
||||
\ WHERE proname LIKE 'bytea%';"-} -- types in the alias??
|
||||
|
||||
,"SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss;"
|
||||
,"SELECT * FROM foo, bar WHERE bar.id = foo.bar_id;"
|
||||
,t "SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss;"
|
||||
,t "SELECT * FROM foo, bar WHERE bar.id = foo.bar_id;"
|
||||
|
||||
{-,"SELECT p1.id, p2.id, v1, v2\n\
|
||||
{-,t "SELECT p1.id, p2.id, v1, v2\n\
|
||||
\FROM polygons p1, polygons p2,\n\
|
||||
\ LATERAL vertices(p1.poly) v1,\n\
|
||||
\ LATERAL vertices(p2.poly) v2\n\
|
||||
\WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;"-} -- <-> operator?
|
||||
|
||||
{-,"SELECT p1.id, p2.id, v1, v2\n\
|
||||
{-,t "SELECT p1.id, p2.id, v1, v2\n\
|
||||
\FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1,\n\
|
||||
\ polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2\n\
|
||||
\WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;"-}
|
||||
|
||||
,"SELECT m.name\n\
|
||||
,t "SELECT m.name\n\
|
||||
\FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true\n\
|
||||
\WHERE pname IS NULL;"
|
||||
|
||||
|
||||
,"SELECT * FROM fdt WHERE c1 > 5"
|
||||
,t "SELECT * FROM fdt WHERE c1 > 5"
|
||||
|
||||
,"SELECT * FROM fdt WHERE c1 IN (1, 2, 3)"
|
||||
,t "SELECT * FROM fdt WHERE c1 IN (1, 2, 3)"
|
||||
|
||||
,"SELECT * FROM fdt WHERE c1 IN (SELECT c1 FROM t2)"
|
||||
,t "SELECT * FROM fdt WHERE c1 IN (SELECT c1 FROM t2)"
|
||||
|
||||
,"SELECT * FROM fdt WHERE c1 IN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10)"
|
||||
,t "SELECT * FROM fdt WHERE c1 IN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10)"
|
||||
|
||||
,"SELECT * FROM fdt WHERE c1 BETWEEN \n\
|
||||
,t "SELECT * FROM fdt WHERE c1 BETWEEN \n\
|
||||
\ (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10) AND 100"
|
||||
|
||||
,"SELECT * FROM fdt WHERE EXISTS (SELECT c1 FROM t2 WHERE c2 > fdt.c1)"
|
||||
,t "SELECT * FROM fdt WHERE EXISTS (SELECT c1 FROM t2 WHERE c2 > fdt.c1)"
|
||||
|
||||
,"SELECT * FROM test1;"
|
||||
,t "SELECT * FROM test1;"
|
||||
|
||||
,"SELECT x FROM test1 GROUP BY x;"
|
||||
,"SELECT x, sum(y) FROM test1 GROUP BY x;"
|
||||
,t "SELECT x FROM test1 GROUP BY x;"
|
||||
,t "SELECT x, sum(y) FROM test1 GROUP BY x;"
|
||||
-- s.date changed to s.datex because of reserved keyword
|
||||
-- handling, not sure if this is correct or not for ansi sql
|
||||
,"SELECT product_id, p.name, (sum(s.units) * p.price) AS sales\n\
|
||||
,t "SELECT product_id, p.name, (sum(s.units) * p.price) AS sales\n\
|
||||
\ FROM products p LEFT JOIN sales s USING (product_id)\n\
|
||||
\ GROUP BY product_id, p.name, p.price;"
|
||||
|
||||
,"SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;"
|
||||
,"SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';"
|
||||
,"SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit\n\
|
||||
,t "SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;"
|
||||
,t "SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';"
|
||||
,t "SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit\n\
|
||||
\ FROM products p LEFT JOIN sales s USING (product_id)\n\
|
||||
\ WHERE s.datex > CURRENT_DATE - INTERVAL '4 weeks'\n\
|
||||
\ GROUP BY product_id, p.name, p.price, p.cost\n\
|
||||
\ HAVING sum(p.price * s.units) > 5000;"
|
||||
|
||||
,"SELECT a, b, c FROM t"
|
||||
,t "SELECT a, b, c FROM t"
|
||||
|
||||
,"SELECT tbl1.a, tbl2.a, tbl1.b FROM t"
|
||||
,t "SELECT tbl1.a, tbl2.a, tbl1.b FROM t"
|
||||
|
||||
,"SELECT tbl1.*, tbl2.a FROM t"
|
||||
,t "SELECT tbl1.*, tbl2.a FROM t"
|
||||
|
||||
,"SELECT a AS value, b + c AS sum FROM t"
|
||||
,t "SELECT a AS value, b + c AS sum FROM t"
|
||||
|
||||
,"SELECT a \"value\", b + c AS sum FROM t"
|
||||
,t "SELECT a \"value\", b + c AS sum FROM t"
|
||||
|
||||
,"SELECT DISTINCT select_list t"
|
||||
,t "SELECT DISTINCT select_list t"
|
||||
|
||||
,"VALUES (1, 'one'), (2, 'two'), (3, 'three');"
|
||||
,t "VALUES (1, 'one'), (2, 'two'), (3, 'three');"
|
||||
|
||||
,"SELECT 1 AS column1, 'one' AS column2\n\
|
||||
,t "SELECT 1 AS column1, 'one' AS column2\n\
|
||||
\UNION ALL\n\
|
||||
\SELECT 2, 'two'\n\
|
||||
\UNION ALL\n\
|
||||
\SELECT 3, 'three';"
|
||||
|
||||
,"SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS t (num,letter);"
|
||||
,t "SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS t (num,letter);"
|
||||
|
||||
,"WITH regional_sales AS (\n\
|
||||
,t "WITH regional_sales AS (\n\
|
||||
\ SELECT region, SUM(amount) AS total_sales\n\
|
||||
\ FROM orders\n\
|
||||
\ GROUP BY region\n\
|
||||
|
@ -161,14 +163,14 @@ TODO: get all the commented out tests working
|
|||
\WHERE region IN (SELECT region FROM top_regions)\n\
|
||||
\GROUP BY region, product;"
|
||||
|
||||
,"WITH RECURSIVE t(n) AS (\n\
|
||||
,t "WITH RECURSIVE t(n) AS (\n\
|
||||
\ VALUES (1)\n\
|
||||
\ UNION ALL\n\
|
||||
\ SELECT n+1 FROM t WHERE n < 100\n\
|
||||
\)\n\
|
||||
\SELECT sum(n) FROM t"
|
||||
|
||||
,"WITH RECURSIVE included_parts(sub_part, part, quantity) AS (\n\
|
||||
,t "WITH RECURSIVE included_parts(sub_part, part, quantity) AS (\n\
|
||||
\ SELECT sub_part, part, quantity FROM parts WHERE part = 'our_product'\n\
|
||||
\ UNION ALL\n\
|
||||
\ SELECT p.sub_part, p.part, p.quantity\n\
|
||||
|
@ -179,7 +181,7 @@ TODO: get all the commented out tests working
|
|||
\FROM included_parts\n\
|
||||
\GROUP BY sub_part"
|
||||
|
||||
,"WITH RECURSIVE search_graph(id, link, data, depth) AS (\n\
|
||||
,t "WITH RECURSIVE search_graph(id, link, data, depth) AS (\n\
|
||||
\ SELECT g.id, g.link, g.data, 1\n\
|
||||
\ FROM graph g\n\
|
||||
\ UNION ALL\n\
|
||||
|
@ -189,7 +191,7 @@ TODO: get all the commented out tests working
|
|||
\)\n\
|
||||
\SELECT * FROM search_graph;"
|
||||
|
||||
{-,"WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (\n\
|
||||
{-,t "WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (\n\
|
||||
\ SELECT g.id, g.link, g.data, 1,\n\
|
||||
\ ARRAY[g.id],\n\
|
||||
\ false\n\
|
||||
|
@ -203,7 +205,7 @@ TODO: get all the commented out tests working
|
|||
\)\n\
|
||||
\SELECT * FROM search_graph;"-} -- ARRAY
|
||||
|
||||
{-,"WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (\n\
|
||||
{-,t "WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (\n\
|
||||
\ SELECT g.id, g.link, g.data, 1,\n\
|
||||
\ ARRAY[ROW(g.f1, g.f2)],\n\
|
||||
\ false\n\
|
||||
|
@ -217,7 +219,7 @@ TODO: get all the commented out tests working
|
|||
\)\n\
|
||||
\SELECT * FROM search_graph;"-} -- ARRAY
|
||||
|
||||
,"WITH RECURSIVE t(n) AS (\n\
|
||||
,t "WITH RECURSIVE t(n) AS (\n\
|
||||
\ SELECT 1\n\
|
||||
\ UNION ALL\n\
|
||||
\ SELECT n+1 FROM t\n\
|
||||
|
@ -226,19 +228,19 @@ TODO: get all the commented out tests working
|
|||
|
||||
-- select page reference
|
||||
|
||||
,"SELECT f.title, f.did, d.name, f.date_prod, f.kind\n\
|
||||
,t "SELECT f.title, f.did, d.name, f.date_prod, f.kind\n\
|
||||
\ FROM distributors d, films f\n\
|
||||
\ WHERE f.did = d.did"
|
||||
|
||||
,"SELECT kind, sum(len) AS total\n\
|
||||
,t "SELECT kind, sum(len) AS total\n\
|
||||
\ FROM films\n\
|
||||
\ GROUP BY kind\n\
|
||||
\ HAVING sum(len) < interval '5 hours';"
|
||||
|
||||
,"SELECT * FROM distributors ORDER BY name;"
|
||||
,"SELECT * FROM distributors ORDER BY 2;"
|
||||
,t "SELECT * FROM distributors ORDER BY name;"
|
||||
,t "SELECT * FROM distributors ORDER BY 2;"
|
||||
|
||||
,"SELECT distributors.name\n\
|
||||
,t "SELECT distributors.name\n\
|
||||
\ FROM distributors\n\
|
||||
\ WHERE distributors.name LIKE 'W%'\n\
|
||||
\UNION\n\
|
||||
|
@ -246,14 +248,14 @@ TODO: get all the commented out tests working
|
|||
\ FROM actors\n\
|
||||
\ WHERE actors.name LIKE 'W%';"
|
||||
|
||||
,"WITH t AS (\n\
|
||||
,t "WITH t AS (\n\
|
||||
\ SELECT random() as x FROM generate_series(1, 3)\n\
|
||||
\ )\n\
|
||||
\SELECT * FROM t\n\
|
||||
\UNION ALL\n\
|
||||
\SELECT * FROM t"
|
||||
|
||||
,"WITH RECURSIVE employee_recursive(distance, employee_name, manager_name) AS (\n\
|
||||
,t "WITH RECURSIVE employee_recursive(distance, employee_name, manager_name) AS (\n\
|
||||
\ SELECT 1, employee_name, manager_name\n\
|
||||
\ FROM employee\n\
|
||||
\ WHERE manager_name = 'Mary'\n\
|
||||
|
@ -264,16 +266,19 @@ TODO: get all the commented out tests working
|
|||
\ )\n\
|
||||
\SELECT distance, employee_name FROM employee_recursive;"
|
||||
|
||||
,"SELECT m.name AS mname, pname\n\
|
||||
,t "SELECT m.name AS mname, pname\n\
|
||||
\FROM manufacturers m, LATERAL get_product_names(m.id) pname;"
|
||||
|
||||
,"SELECT m.name AS mname, pname\n\
|
||||
,t "SELECT m.name AS mname, pname\n\
|
||||
\FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true;"
|
||||
|
||||
,"SELECT 2+2;"
|
||||
,t "SELECT 2+2;"
|
||||
|
||||
-- simple-sql-parser doesn't support where without from
|
||||
-- this can be added for the postgres dialect when it is written
|
||||
--,"SELECT distributors.* WHERE distributors.name = 'Westward';"
|
||||
--,t "SELECT distributors.* WHERE distributors.name = 'Westward';"
|
||||
|
||||
]
|
||||
where
|
||||
t :: HasCallStack => Text -> TestItem
|
||||
t src = testParseQueryExpr postgres src
|
||||
|
|
|
@ -12,7 +12,8 @@ module Language.SQL.SimpleSQL.QueryExprComponents (queryExprComponentTests) wher
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
queryExprComponentTests :: TestItem
|
||||
queryExprComponentTests = Group "queryExprComponentTests"
|
||||
|
@ -31,10 +32,10 @@ queryExprComponentTests = Group "queryExprComponentTests"
|
|||
|
||||
|
||||
duplicates :: TestItem
|
||||
duplicates = Group "duplicates" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("select a from t" ,ms SQDefault)
|
||||
,("select all a from t" ,ms All)
|
||||
,("select distinct a from t", ms Distinct)
|
||||
duplicates = Group "duplicates"
|
||||
[q "select a from t" $ ms SQDefault
|
||||
,q "select all a from t" $ ms All
|
||||
,q "select distinct a from t" $ ms Distinct
|
||||
]
|
||||
where
|
||||
ms d = toQueryExpr $ makeSelect
|
||||
|
@ -43,77 +44,77 @@ duplicates = Group "duplicates" $ map (uncurry (TestQueryExpr ansi2011))
|
|||
,msFrom = [TRSimple [Name Nothing "t"]]}
|
||||
|
||||
selectLists :: TestItem
|
||||
selectLists = Group "selectLists" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("select 1",
|
||||
toQueryExpr $ makeSelect {msSelectList = [(NumLit "1",Nothing)]})
|
||||
selectLists = Group "selectLists"
|
||||
[q "select 1"
|
||||
$ toQueryExpr $ makeSelect {msSelectList = [(NumLit "1",Nothing)]}
|
||||
|
||||
,("select a"
|
||||
,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)]})
|
||||
,q "select a"
|
||||
$ toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)]}
|
||||
|
||||
,("select a,b"
|
||||
,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
|
||||
,(Iden [Name Nothing "b"],Nothing)]})
|
||||
,q "select a,b"
|
||||
$ toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
|
||||
,(Iden [Name Nothing "b"],Nothing)]}
|
||||
|
||||
,("select 1+2,3+4"
|
||||
,toQueryExpr $ makeSelect {msSelectList =
|
||||
,q "select 1+2,3+4"
|
||||
$ toQueryExpr $ makeSelect {msSelectList =
|
||||
[(BinOp (NumLit "1") [Name Nothing "+"] (NumLit "2"),Nothing)
|
||||
,(BinOp (NumLit "3") [Name Nothing "+"] (NumLit "4"),Nothing)]})
|
||||
,(BinOp (NumLit "3") [Name Nothing "+"] (NumLit "4"),Nothing)]}
|
||||
|
||||
,("select a as a, /*comment*/ b as b"
|
||||
,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "a")
|
||||
,(Iden [Name Nothing "b"], Just $ Name Nothing "b")]})
|
||||
,q "select a as a, /*comment*/ b as b"
|
||||
$ toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "a")
|
||||
,(Iden [Name Nothing "b"], Just $ Name Nothing "b")]}
|
||||
|
||||
,("select a a, b b"
|
||||
,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "a")
|
||||
,(Iden [Name Nothing "b"], Just $ Name Nothing "b")]})
|
||||
,q "select a a, b b"
|
||||
$ toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "a")
|
||||
,(Iden [Name Nothing "b"], Just $ Name Nothing "b")]}
|
||||
|
||||
,("select a + b * c"
|
||||
,toQueryExpr $ makeSelect {msSelectList =
|
||||
,q "select a + b * c"
|
||||
$ toQueryExpr $ makeSelect {msSelectList =
|
||||
[(BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"]
|
||||
(BinOp (Iden [Name Nothing "b"]) [Name Nothing "*"] (Iden [Name Nothing "c"]))
|
||||
,Nothing)]})
|
||||
,Nothing)]}
|
||||
|
||||
]
|
||||
|
||||
whereClause :: TestItem
|
||||
whereClause = Group "whereClause" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("select a from t where a = 5"
|
||||
,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)]
|
||||
whereClause = Group "whereClause"
|
||||
[q "select a from t where a = 5"
|
||||
$ toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
,msWhere = Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "5")})
|
||||
,msWhere = Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "5")}
|
||||
]
|
||||
|
||||
having :: TestItem
|
||||
having = Group "having" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("select a,sum(b) from t group by a having sum(b) > 5"
|
||||
,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
|
||||
having = Group "having"
|
||||
[q "select a,sum(b) from t group by a having sum(b) > 5"
|
||||
$ toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
|
||||
,(App [Name Nothing "sum"] [Iden [Name Nothing "b"]],Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
,msGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]]
|
||||
,msHaving = Just $ BinOp (App [Name Nothing "sum"] [Iden [Name Nothing "b"]])
|
||||
[Name Nothing ">"] (NumLit "5")
|
||||
})
|
||||
}
|
||||
]
|
||||
|
||||
orderBy :: TestItem
|
||||
orderBy = Group "orderBy" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("select a from t order by a"
|
||||
,ms [SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault])
|
||||
orderBy = Group "orderBy"
|
||||
[q "select a from t order by a"
|
||||
$ ms [SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault]
|
||||
|
||||
,("select a from t order by a, b"
|
||||
,ms [SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault
|
||||
,SortSpec (Iden [Name Nothing "b"]) DirDefault NullsOrderDefault])
|
||||
,q "select a from t order by a, b"
|
||||
$ ms [SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault
|
||||
,SortSpec (Iden [Name Nothing "b"]) DirDefault NullsOrderDefault]
|
||||
|
||||
,("select a from t order by a asc"
|
||||
,ms [SortSpec (Iden [Name Nothing "a"]) Asc NullsOrderDefault])
|
||||
,q "select a from t order by a asc"
|
||||
$ ms [SortSpec (Iden [Name Nothing "a"]) Asc NullsOrderDefault]
|
||||
|
||||
,("select a from t order by a desc, b desc"
|
||||
,ms [SortSpec (Iden [Name Nothing "a"]) Desc NullsOrderDefault
|
||||
,SortSpec (Iden [Name Nothing "b"]) Desc NullsOrderDefault])
|
||||
,q "select a from t order by a desc, b desc"
|
||||
$ ms [SortSpec (Iden [Name Nothing "a"]) Desc NullsOrderDefault
|
||||
,SortSpec (Iden [Name Nothing "b"]) Desc NullsOrderDefault]
|
||||
|
||||
,("select a from t order by a desc nulls first, b desc nulls last"
|
||||
,ms [SortSpec (Iden [Name Nothing "a"]) Desc NullsFirst
|
||||
,SortSpec (Iden [Name Nothing "b"]) Desc NullsLast])
|
||||
,q "select a from t order by a desc nulls first, b desc nulls last"
|
||||
$ ms [SortSpec (Iden [Name Nothing "a"]) Desc NullsFirst
|
||||
,SortSpec (Iden [Name Nothing "b"]) Desc NullsLast]
|
||||
|
||||
]
|
||||
where
|
||||
|
@ -122,20 +123,20 @@ orderBy = Group "orderBy" $ map (uncurry (TestQueryExpr ansi2011))
|
|||
,msOrderBy = o}
|
||||
|
||||
offsetFetch :: TestItem
|
||||
offsetFetch = Group "offsetFetch" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
offsetFetch = Group "offsetFetch"
|
||||
[-- ansi standard
|
||||
("select a from t offset 5 rows fetch next 10 rows only"
|
||||
,ms (Just $ NumLit "5") (Just $ NumLit "10"))
|
||||
,("select a from t offset 5 rows;"
|
||||
,ms (Just $ NumLit "5") Nothing)
|
||||
,("select a from t fetch next 10 row only;"
|
||||
,ms Nothing (Just $ NumLit "10"))
|
||||
,("select a from t offset 5 row fetch first 10 row only"
|
||||
,ms (Just $ NumLit "5") (Just $ NumLit "10"))
|
||||
q "select a from t offset 5 rows fetch next 10 rows only"
|
||||
$ ms (Just $ NumLit "5") (Just $ NumLit "10")
|
||||
,q "select a from t offset 5 rows;"
|
||||
$ ms (Just $ NumLit "5") Nothing
|
||||
,q "select a from t fetch next 10 row only;"
|
||||
$ ms Nothing (Just $ NumLit "10")
|
||||
,q "select a from t offset 5 row fetch first 10 row only"
|
||||
$ ms (Just $ NumLit "5") (Just $ NumLit "10")
|
||||
-- postgres: disabled, will add back when postgres
|
||||
-- dialect is added
|
||||
--,("select a from t limit 10 offset 5"
|
||||
-- ,ms (Just $ NumLit "5") (Just $ NumLit "10"))
|
||||
--,q "select a from t limit 10 offset 5"
|
||||
-- $ ms (Just $ NumLit "5") (Just $ NumLit "10"))
|
||||
]
|
||||
where
|
||||
ms o l = toQueryExpr $ makeSelect
|
||||
|
@ -145,23 +146,23 @@ offsetFetch = Group "offsetFetch" $ map (uncurry (TestQueryExpr ansi2011))
|
|||
,msFetchFirst = l}
|
||||
|
||||
combos :: TestItem
|
||||
combos = Group "combos" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("select a from t union select b from u"
|
||||
,QueryExprSetOp mst Union SQDefault Respectively msu)
|
||||
combos = Group "combos"
|
||||
[q "select a from t union select b from u"
|
||||
$ QueryExprSetOp mst Union SQDefault Respectively msu
|
||||
|
||||
,("select a from t intersect select b from u"
|
||||
,QueryExprSetOp mst Intersect SQDefault Respectively msu)
|
||||
,q "select a from t intersect select b from u"
|
||||
$ QueryExprSetOp mst Intersect SQDefault Respectively msu
|
||||
|
||||
,("select a from t except all select b from u"
|
||||
,QueryExprSetOp mst Except All Respectively msu)
|
||||
,q "select a from t except all select b from u"
|
||||
$ QueryExprSetOp mst Except All Respectively msu
|
||||
|
||||
,("select a from t union distinct corresponding \
|
||||
,q "select a from t union distinct corresponding \
|
||||
\select b from u"
|
||||
,QueryExprSetOp mst Union Distinct Corresponding msu)
|
||||
$ QueryExprSetOp mst Union Distinct Corresponding msu
|
||||
|
||||
,("select a from t union select a from t union select a from t"
|
||||
,QueryExprSetOp (QueryExprSetOp mst Union SQDefault Respectively mst)
|
||||
Union SQDefault Respectively mst)
|
||||
,q "select a from t union select a from t union select a from t"
|
||||
$ QueryExprSetOp (QueryExprSetOp mst Union SQDefault Respectively mst)
|
||||
Union SQDefault Respectively mst
|
||||
]
|
||||
where
|
||||
mst = toQueryExpr $ makeSelect
|
||||
|
@ -173,20 +174,20 @@ combos = Group "combos" $ map (uncurry (TestQueryExpr ansi2011))
|
|||
|
||||
|
||||
withQueries :: TestItem
|
||||
withQueries = Group "with queries" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("with u as (select a from t) select a from u"
|
||||
,With False [(Alias (Name Nothing "u") Nothing, ms1)] ms2)
|
||||
withQueries = Group "with queries"
|
||||
[q "with u as (select a from t) select a from u"
|
||||
$ With False [(Alias (Name Nothing "u") Nothing, ms1)] ms2
|
||||
|
||||
,("with u(b) as (select a from t) select a from u"
|
||||
,With False [(Alias (Name Nothing "u") (Just [Name Nothing "b"]), ms1)] ms2)
|
||||
,q "with u(b) as (select a from t) select a from u"
|
||||
$ With False [(Alias (Name Nothing "u") (Just [Name Nothing "b"]), ms1)] ms2
|
||||
|
||||
,("with x as (select a from t),\n\
|
||||
,q "with x as (select a from t),\n\
|
||||
\ u as (select a from x)\n\
|
||||
\select a from u"
|
||||
,With False [(Alias (Name Nothing "x") Nothing, ms1), (Alias (Name Nothing "u") Nothing,ms3)] ms2)
|
||||
$ With False [(Alias (Name Nothing "x") Nothing, ms1), (Alias (Name Nothing "u") Nothing,ms3)] ms2
|
||||
|
||||
,("with recursive u as (select a from t) select a from u"
|
||||
,With True [(Alias (Name Nothing "u") Nothing, ms1)] ms2)
|
||||
,q "with recursive u as (select a from t) select a from u"
|
||||
$ With True [(Alias (Name Nothing "u") Nothing, ms1)] ms2
|
||||
]
|
||||
where
|
||||
ms c t = toQueryExpr $ makeSelect
|
||||
|
@ -197,13 +198,16 @@ withQueries = Group "with queries" $ map (uncurry (TestQueryExpr ansi2011))
|
|||
ms3 = ms "a" "x"
|
||||
|
||||
values :: TestItem
|
||||
values = Group "values" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("values (1,2),(3,4)"
|
||||
,Values [[NumLit "1", NumLit "2"]
|
||||
,[NumLit "3", NumLit "4"]])
|
||||
values = Group "values"
|
||||
[q "values (1,2),(3,4)"
|
||||
$ Values [[NumLit "1", NumLit "2"]
|
||||
,[NumLit "3", NumLit "4"]]
|
||||
]
|
||||
|
||||
tables :: TestItem
|
||||
tables = Group "tables" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("table tbl", Table [Name Nothing "tbl"])
|
||||
tables = Group "tables"
|
||||
[q "table tbl" $ Table [Name Nothing "tbl"]
|
||||
]
|
||||
|
||||
q :: HasCallStack => Text -> QueryExpr -> TestItem
|
||||
q src ast = testQueryExpr ansi2011 src ast
|
||||
|
|
|
@ -9,19 +9,23 @@ module Language.SQL.SimpleSQL.QueryExprs (queryExprsTests) where
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
queryExprsTests :: TestItem
|
||||
queryExprsTests = Group "query exprs" $ map (uncurry (TestStatements ansi2011))
|
||||
[("select 1",[ms])
|
||||
,("select 1;",[ms])
|
||||
,("select 1;select 1",[ms,ms])
|
||||
,(" select 1;select 1; ",[ms,ms])
|
||||
,("SELECT CURRENT_TIMESTAMP;"
|
||||
,[SelectStatement $ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Iden [Name Nothing "CURRENT_TIMESTAMP"],Nothing)]}])
|
||||
,("SELECT \"CURRENT_TIMESTAMP\";"
|
||||
,[SelectStatement $ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Iden [Name (Just ("\"","\"")) "CURRENT_TIMESTAMP"],Nothing)]}])
|
||||
queryExprsTests = Group "query exprs"
|
||||
[q "select 1" [ms]
|
||||
,q "select 1;" [ms]
|
||||
,q "select 1;select 1" [ms,ms]
|
||||
,q " select 1;select 1; " [ms,ms]
|
||||
,q "SELECT CURRENT_TIMESTAMP;"
|
||||
[SelectStatement $ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Iden [Name Nothing "CURRENT_TIMESTAMP"],Nothing)]}]
|
||||
,q "SELECT \"CURRENT_TIMESTAMP\";"
|
||||
[SelectStatement $ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Iden [Name (Just ("\"","\"")) "CURRENT_TIMESTAMP"],Nothing)]}]
|
||||
]
|
||||
where
|
||||
ms = SelectStatement $ toQueryExpr $ makeSelect {msSelectList = [(NumLit "1",Nothing)]}
|
||||
q :: HasCallStack => Text -> [Statement] -> TestItem
|
||||
q src ast = testStatements ansi2011 src ast
|
||||
|
|
|
@ -11,6 +11,8 @@ module Language.SQL.SimpleSQL.SQL2011AccessControl (sql2011AccessControlTests) w
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
sql2011AccessControlTests :: TestItem
|
||||
sql2011AccessControlTests = Group "sql 2011 access control tests" [
|
||||
|
@ -78,128 +80,107 @@ sql2011AccessControlTests = Group "sql 2011 access control tests" [
|
|||
| CURRENT_ROLE
|
||||
-}
|
||||
|
||||
(TestStatement ansi2011
|
||||
"grant all privileges on tbl1 to role1"
|
||||
s "grant all privileges on tbl1 to role1"
|
||||
$ GrantPrivilege [PrivAll]
|
||||
(PrivTable [Name Nothing "tbl1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant all privileges on tbl1 to role1,role2"
|
||||
,s "grant all privileges on tbl1 to role1,role2"
|
||||
$ GrantPrivilege [PrivAll]
|
||||
(PrivTable [Name Nothing "tbl1"])
|
||||
[Name Nothing "role1",Name Nothing "role2"] WithoutGrantOption)
|
||||
[Name Nothing "role1",Name Nothing "role2"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant all privileges on tbl1 to role1 with grant option"
|
||||
,s "grant all privileges on tbl1 to role1 with grant option"
|
||||
$ GrantPrivilege [PrivAll]
|
||||
(PrivTable [Name Nothing "tbl1"])
|
||||
[Name Nothing "role1"] WithGrantOption)
|
||||
[Name Nothing "role1"] WithGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant all privileges on table tbl1 to role1"
|
||||
,s "grant all privileges on table tbl1 to role1"
|
||||
$ GrantPrivilege [PrivAll]
|
||||
(PrivTable [Name Nothing "tbl1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant all privileges on domain mydom to role1"
|
||||
,s "grant all privileges on domain mydom to role1"
|
||||
$ GrantPrivilege [PrivAll]
|
||||
(PrivDomain [Name Nothing "mydom"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant all privileges on type t1 to role1"
|
||||
,s "grant all privileges on type t1 to role1"
|
||||
$ GrantPrivilege [PrivAll]
|
||||
(PrivType [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant all privileges on sequence s1 to role1"
|
||||
,s "grant all privileges on sequence s1 to role1"
|
||||
$ GrantPrivilege [PrivAll]
|
||||
(PrivSequence [Name Nothing "s1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant select on table t1 to role1"
|
||||
,s "grant select on table t1 to role1"
|
||||
$ GrantPrivilege [PrivSelect []]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant select(a,b) on table t1 to role1"
|
||||
,s "grant select(a,b) on table t1 to role1"
|
||||
$ GrantPrivilege [PrivSelect [Name Nothing "a", Name Nothing "b"]]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant delete on table t1 to role1"
|
||||
,s "grant delete on table t1 to role1"
|
||||
$ GrantPrivilege [PrivDelete]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant insert on table t1 to role1"
|
||||
,s "grant insert on table t1 to role1"
|
||||
$ GrantPrivilege [PrivInsert []]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant insert(a,b) on table t1 to role1"
|
||||
,s "grant insert(a,b) on table t1 to role1"
|
||||
$ GrantPrivilege [PrivInsert [Name Nothing "a", Name Nothing "b"]]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant update on table t1 to role1"
|
||||
,s "grant update on table t1 to role1"
|
||||
$ GrantPrivilege [PrivUpdate []]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant update(a,b) on table t1 to role1"
|
||||
,s "grant update(a,b) on table t1 to role1"
|
||||
$ GrantPrivilege [PrivUpdate [Name Nothing "a", Name Nothing "b"]]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant references on table t1 to role1"
|
||||
,s "grant references on table t1 to role1"
|
||||
$ GrantPrivilege [PrivReferences []]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant references(a,b) on table t1 to role1"
|
||||
,s "grant references(a,b) on table t1 to role1"
|
||||
$ GrantPrivilege [PrivReferences [Name Nothing "a", Name Nothing "b"]]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant usage on table t1 to role1"
|
||||
,s "grant usage on table t1 to role1"
|
||||
$ GrantPrivilege [PrivUsage]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant trigger on table t1 to role1"
|
||||
,s "grant trigger on table t1 to role1"
|
||||
$ GrantPrivilege [PrivTrigger]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant execute on specific function f to role1"
|
||||
,s "grant execute on specific function f to role1"
|
||||
$ GrantPrivilege [PrivExecute]
|
||||
(PrivFunction [Name Nothing "f"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant select,delete on table t1 to role1"
|
||||
,s "grant select,delete on table t1 to role1"
|
||||
$ GrantPrivilege [PrivSelect [], PrivDelete]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] WithoutGrantOption)
|
||||
[Name Nothing "role1"] WithoutGrantOption
|
||||
|
||||
{-
|
||||
skipping for now:
|
||||
|
@ -224,9 +205,8 @@ functions, etc., by argument types since they can be overloaded
|
|||
CREATE ROLE <role name> [ WITH ADMIN <grantor> ]
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"create role rolee"
|
||||
$ CreateRole (Name Nothing "rolee"))
|
||||
,s "create role rolee"
|
||||
$ CreateRole (Name Nothing "rolee")
|
||||
|
||||
|
||||
{-
|
||||
|
@ -242,18 +222,15 @@ functions, etc., by argument types since they can be overloaded
|
|||
<role name>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant role1 to public"
|
||||
$ GrantRole [Name Nothing "role1"] [Name Nothing "public"] WithoutAdminOption)
|
||||
,s "grant role1 to public"
|
||||
$ GrantRole [Name Nothing "role1"] [Name Nothing "public"] WithoutAdminOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant role1,role2 to role3,role4"
|
||||
,s "grant role1,role2 to role3,role4"
|
||||
$ GrantRole [Name Nothing "role1",Name Nothing "role2"]
|
||||
[Name Nothing "role3", Name Nothing "role4"] WithoutAdminOption)
|
||||
[Name Nothing "role3", Name Nothing "role4"] WithoutAdminOption
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"grant role1 to role3 with admin option"
|
||||
$ GrantRole [Name Nothing "role1"] [Name Nothing "role3"] WithAdminOption)
|
||||
,s "grant role1 to role3 with admin option"
|
||||
$ GrantRole [Name Nothing "role1"] [Name Nothing "role3"] WithAdminOption
|
||||
|
||||
|
||||
{-
|
||||
|
@ -263,9 +240,8 @@ functions, etc., by argument types since they can be overloaded
|
|||
DROP ROLE <role name>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"drop role rolee"
|
||||
$ DropRole (Name Nothing "rolee"))
|
||||
,s "drop role rolee"
|
||||
$ DropRole (Name Nothing "rolee")
|
||||
|
||||
|
||||
{-
|
||||
|
@ -287,17 +263,16 @@ functions, etc., by argument types since they can be overloaded
|
|||
-}
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"revoke select on t1 from role1"
|
||||
,s "revoke select on t1 from role1"
|
||||
$ RevokePrivilege NoGrantOptionFor [PrivSelect []]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1"] DefaultDropBehaviour)
|
||||
[Name Nothing "role1"] DefaultDropBehaviour
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"revoke grant option for select on t1 from role1,role2 cascade"
|
||||
$ RevokePrivilege GrantOptionFor [PrivSelect []]
|
||||
(PrivTable [Name Nothing "t1"])
|
||||
[Name Nothing "role1",Name Nothing "role2"] Cascade)
|
||||
[Name Nothing "role1",Name Nothing "role2"] Cascade
|
||||
|
||||
|
||||
{-
|
||||
|
@ -311,20 +286,19 @@ functions, etc., by argument types since they can be overloaded
|
|||
<role name>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"revoke role1 from role2"
|
||||
,s "revoke role1 from role2"
|
||||
$ RevokeRole NoAdminOptionFor [Name Nothing "role1"]
|
||||
[Name Nothing "role2"] DefaultDropBehaviour)
|
||||
[Name Nothing "role2"] DefaultDropBehaviour
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"revoke role1,role2 from role3,role4"
|
||||
,s "revoke role1,role2 from role3,role4"
|
||||
$ RevokeRole NoAdminOptionFor [Name Nothing "role1",Name Nothing "role2"]
|
||||
[Name Nothing "role3",Name Nothing "role4"] DefaultDropBehaviour)
|
||||
[Name Nothing "role3",Name Nothing "role4"] DefaultDropBehaviour
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"revoke admin option for role1 from role2 cascade"
|
||||
$ RevokeRole AdminOptionFor [Name Nothing "role1"] [Name Nothing "role2"] Cascade)
|
||||
|
||||
,s "revoke admin option for role1 from role2 cascade"
|
||||
$ RevokeRole AdminOptionFor [Name Nothing "role1"] [Name Nothing "role2"] Cascade
|
||||
|
||||
]
|
||||
|
||||
s :: HasCallStack => Text -> Statement -> TestItem
|
||||
s src ast = testStatement ansi2011 src ast
|
||||
|
|
|
@ -12,6 +12,8 @@ module Language.SQL.SimpleSQL.SQL2011Bits (sql2011BitsTests) where
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
sql2011BitsTests :: TestItem
|
||||
sql2011BitsTests = Group "sql 2011 bits tests" [
|
||||
|
@ -27,9 +29,7 @@ sql2011BitsTests = Group "sql 2011 bits tests" [
|
|||
BEGIN is not in the standard!
|
||||
-}
|
||||
|
||||
(TestStatement ansi2011
|
||||
"start transaction"
|
||||
$ StartTransaction)
|
||||
s "start transaction" StartTransaction
|
||||
|
||||
{-
|
||||
17.2 <set transaction statement>
|
||||
|
@ -84,9 +84,8 @@ BEGIN is not in the standard!
|
|||
<savepoint name>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"savepoint difficult_bit"
|
||||
$ Savepoint $ Name Nothing "difficult_bit")
|
||||
,s "savepoint difficult_bit"
|
||||
$ Savepoint $ Name Nothing "difficult_bit"
|
||||
|
||||
|
||||
{-
|
||||
|
@ -96,9 +95,8 @@ BEGIN is not in the standard!
|
|||
RELEASE SAVEPOINT <savepoint specifier>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"release savepoint difficult_bit"
|
||||
$ ReleaseSavepoint $ Name Nothing "difficult_bit")
|
||||
,s "release savepoint difficult_bit"
|
||||
$ ReleaseSavepoint $ Name Nothing "difficult_bit"
|
||||
|
||||
|
||||
{-
|
||||
|
@ -108,13 +106,9 @@ BEGIN is not in the standard!
|
|||
COMMIT [ WORK ] [ AND [ NO ] CHAIN ]
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"commit"
|
||||
$ Commit)
|
||||
,s "commit" Commit
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"commit work"
|
||||
$ Commit)
|
||||
,s "commit work" Commit
|
||||
|
||||
|
||||
{-
|
||||
|
@ -127,17 +121,12 @@ BEGIN is not in the standard!
|
|||
TO SAVEPOINT <savepoint specifier>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"rollback"
|
||||
$ Rollback Nothing)
|
||||
,s "rollback" $ Rollback Nothing
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"rollback work"
|
||||
$ Rollback Nothing)
|
||||
,s "rollback work" $ Rollback Nothing
|
||||
|
||||
,(TestStatement ansi2011
|
||||
"rollback to savepoint difficult_bit"
|
||||
$ Rollback $ Just $ Name Nothing "difficult_bit")
|
||||
,s "rollback to savepoint difficult_bit"
|
||||
$ Rollback $ Just $ Name Nothing "difficult_bit"
|
||||
|
||||
|
||||
{-
|
||||
|
@ -232,3 +221,6 @@ BEGIN is not in the standard!
|
|||
-}
|
||||
|
||||
]
|
||||
|
||||
s :: HasCallStack => Text -> Statement -> TestItem
|
||||
s src ast = testStatement ansi2011 src ast
|
||||
|
|
|
@ -7,6 +7,8 @@ module Language.SQL.SimpleSQL.SQL2011DataManipulation (sql2011DataManipulationTe
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
sql2011DataManipulationTests :: TestItem
|
||||
sql2011DataManipulationTests = Group "sql 2011 data manipulation tests"
|
||||
|
@ -111,20 +113,20 @@ sql2011DataManipulationTests = Group "sql 2011 data manipulation tests"
|
|||
[ WHERE <search condition> ]
|
||||
-}
|
||||
|
||||
(TestStatement ansi2011 "delete from t"
|
||||
$ Delete [Name Nothing "t"] Nothing Nothing)
|
||||
s "delete from t"
|
||||
$ Delete [Name Nothing "t"] Nothing Nothing
|
||||
|
||||
,(TestStatement ansi2011 "delete from t as u"
|
||||
$ Delete [Name Nothing "t"] (Just (Name Nothing "u")) Nothing)
|
||||
,s "delete from t as u"
|
||||
$ Delete [Name Nothing "t"] (Just (Name Nothing "u")) Nothing
|
||||
|
||||
,(TestStatement ansi2011 "delete from t where x = 5"
|
||||
,s "delete from t where x = 5"
|
||||
$ Delete [Name Nothing "t"] Nothing
|
||||
(Just $ BinOp (Iden [Name Nothing "x"]) [Name Nothing "="] (NumLit "5")))
|
||||
(Just $ BinOp (Iden [Name Nothing "x"]) [Name Nothing "="] (NumLit "5"))
|
||||
|
||||
|
||||
,(TestStatement ansi2011 "delete from t as u where u.x = 5"
|
||||
,s "delete from t as u where u.x = 5"
|
||||
$ Delete [Name Nothing "t"] (Just (Name Nothing "u"))
|
||||
(Just $ BinOp (Iden [Name Nothing "u", Name Nothing "x"]) [Name Nothing "="] (NumLit "5")))
|
||||
(Just $ BinOp (Iden [Name Nothing "u", Name Nothing "x"]) [Name Nothing "="] (NumLit "5"))
|
||||
|
||||
{-
|
||||
14.10 <truncate table statement>
|
||||
|
@ -137,14 +139,14 @@ sql2011DataManipulationTests = Group "sql 2011 data manipulation tests"
|
|||
| RESTART IDENTITY
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011 "truncate table t"
|
||||
$ Truncate [Name Nothing "t"] DefaultIdentityRestart)
|
||||
,s "truncate table t"
|
||||
$ Truncate [Name Nothing "t"] DefaultIdentityRestart
|
||||
|
||||
,(TestStatement ansi2011 "truncate table t continue identity"
|
||||
$ Truncate [Name Nothing "t"] ContinueIdentity)
|
||||
,s "truncate table t continue identity"
|
||||
$ Truncate [Name Nothing "t"] ContinueIdentity
|
||||
|
||||
,(TestStatement ansi2011 "truncate table t restart identity"
|
||||
$ Truncate [Name Nothing "t"] RestartIdentity)
|
||||
,s "truncate table t restart identity"
|
||||
$ Truncate [Name Nothing "t"] RestartIdentity
|
||||
|
||||
|
||||
{-
|
||||
|
@ -182,37 +184,37 @@ sql2011DataManipulationTests = Group "sql 2011 data manipulation tests"
|
|||
<column name list>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011 "insert into t select * from u"
|
||||
,s "insert into t select * from u"
|
||||
$ Insert [Name Nothing "t"] Nothing
|
||||
$ InsertQuery $ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star, Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "u"]]})
|
||||
,msFrom = [TRSimple [Name Nothing "u"]]}
|
||||
|
||||
,(TestStatement ansi2011 "insert into t(a,b,c) select * from u"
|
||||
,s "insert into t(a,b,c) select * from u"
|
||||
$ Insert [Name Nothing "t"] (Just [Name Nothing "a", Name Nothing "b", Name Nothing "c"])
|
||||
$ InsertQuery $ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star, Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "u"]]})
|
||||
,msFrom = [TRSimple [Name Nothing "u"]]}
|
||||
|
||||
,(TestStatement ansi2011 "insert into t default values"
|
||||
$ Insert [Name Nothing "t"] Nothing DefaultInsertValues)
|
||||
,s "insert into t default values"
|
||||
$ Insert [Name Nothing "t"] Nothing DefaultInsertValues
|
||||
|
||||
,(TestStatement ansi2011 "insert into t values(1,2)"
|
||||
,s "insert into t values(1,2)"
|
||||
$ Insert [Name Nothing "t"] Nothing
|
||||
$ InsertQuery $ Values [[NumLit "1", NumLit "2"]])
|
||||
$ InsertQuery $ Values [[NumLit "1", NumLit "2"]]
|
||||
|
||||
,(TestStatement ansi2011 "insert into t values (1,2),(3,4)"
|
||||
,s "insert into t values (1,2),(3,4)"
|
||||
$ Insert [Name Nothing "t"] Nothing
|
||||
$ InsertQuery $ Values [[NumLit "1", NumLit "2"]
|
||||
,[NumLit "3", NumLit "4"]])
|
||||
,[NumLit "3", NumLit "4"]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"insert into t values (default,null,array[],multiset[])"
|
||||
$ Insert [Name Nothing "t"] Nothing
|
||||
$ InsertQuery $ Values [[Iden [Name Nothing "default"]
|
||||
,Iden [Name Nothing "null"]
|
||||
,Array (Iden [Name Nothing "array"]) []
|
||||
,MultisetCtor []]])
|
||||
,MultisetCtor []]]
|
||||
|
||||
|
||||
{-
|
||||
|
@ -456,32 +458,32 @@ FROM CentralOfficeAccounts;
|
|||
-}
|
||||
|
||||
|
||||
,(TestStatement ansi2011 "update t set a=b"
|
||||
,s "update t set a=b"
|
||||
$ Update [Name Nothing "t"] Nothing
|
||||
[Set [Name Nothing "a"] (Iden [Name Nothing "b"])] Nothing)
|
||||
[Set [Name Nothing "a"] (Iden [Name Nothing "b"])] Nothing
|
||||
|
||||
,(TestStatement ansi2011 "update t set a=b, c=5"
|
||||
,s "update t set a=b, c=5"
|
||||
$ Update [Name Nothing "t"] Nothing
|
||||
[Set [Name Nothing "a"] (Iden [Name Nothing "b"])
|
||||
,Set [Name Nothing "c"] (NumLit "5")] Nothing)
|
||||
,Set [Name Nothing "c"] (NumLit "5")] Nothing
|
||||
|
||||
|
||||
,(TestStatement ansi2011 "update t set a=b where a>5"
|
||||
,s "update t set a=b where a>5"
|
||||
$ Update [Name Nothing "t"] Nothing
|
||||
[Set [Name Nothing "a"] (Iden [Name Nothing "b"])]
|
||||
$ Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5"))
|
||||
$ Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5")
|
||||
|
||||
|
||||
,(TestStatement ansi2011 "update t as u set a=b where u.a>5"
|
||||
,s "update t as u set a=b where u.a>5"
|
||||
$ Update [Name Nothing "t"] (Just $ Name Nothing "u")
|
||||
[Set [Name Nothing "a"] (Iden [Name Nothing "b"])]
|
||||
$ Just $ BinOp (Iden [Name Nothing "u",Name Nothing "a"])
|
||||
[Name Nothing ">"] (NumLit "5"))
|
||||
[Name Nothing ">"] (NumLit "5")
|
||||
|
||||
,(TestStatement ansi2011 "update t set (a,b)=(3,5)"
|
||||
,s "update t set (a,b)=(3,5)"
|
||||
$ Update [Name Nothing "t"] Nothing
|
||||
[SetMultiple [[Name Nothing "a"],[Name Nothing "b"]]
|
||||
[NumLit "3", NumLit "5"]] Nothing)
|
||||
[NumLit "3", NumLit "5"]] Nothing
|
||||
|
||||
|
||||
|
||||
|
@ -553,3 +555,6 @@ declare local temporary table t (a int) [on commit {preserve | delete} rows]
|
|||
|
||||
|
||||
]
|
||||
|
||||
s :: HasCallStack => Text -> Statement -> TestItem
|
||||
s src ast = testStatement ansi2011 src ast
|
||||
|
|
|
@ -37,6 +37,7 @@ import Language.SQL.SimpleSQL.TestTypes
|
|||
import Language.SQL.SimpleSQL.Syntax
|
||||
|
||||
import Data.Text (Text)
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
|
||||
sql2011QueryTests :: TestItem
|
||||
sql2011QueryTests = Group "sql 2011 query tests"
|
||||
|
@ -515,19 +516,19 @@ generalLiterals = Group "general literals"
|
|||
|
||||
characterStringLiterals :: TestItem
|
||||
characterStringLiterals = Group "character string literals"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("'a regular string literal'"
|
||||
,StringLit "'" "'" "a regular string literal")
|
||||
,("'something' ' some more' 'and more'"
|
||||
,StringLit "'" "'" "something some moreand more")
|
||||
,("'something' \n ' some more' \t 'and more'"
|
||||
,StringLit "'" "'" "something some moreand more")
|
||||
,("'something' -- a comment\n ' some more' /*another comment*/ 'and more'"
|
||||
,StringLit "'" "'" "something some moreand more")
|
||||
,("'a quote: '', stuff'"
|
||||
,StringLit "'" "'" "a quote: '', stuff")
|
||||
,("''"
|
||||
,StringLit "'" "'" "")
|
||||
$
|
||||
[e "'a regular string literal'"
|
||||
$ StringLit "'" "'" "a regular string literal"
|
||||
,e "'something' ' some more' 'and more'"
|
||||
$ StringLit "'" "'" "something some moreand more"
|
||||
,e "'something' \n ' some more' \t 'and more'"
|
||||
$ StringLit "'" "'" "something some moreand more"
|
||||
,e "'something' -- a comment\n ' some more' /*another comment*/ 'and more'"
|
||||
$ StringLit "'" "'" "something some moreand more"
|
||||
,e "'a quote: '', stuff'"
|
||||
$ StringLit "'" "'" "a quote: '', stuff"
|
||||
,e "''"
|
||||
$ StringLit "'" "'" ""
|
||||
|
||||
{-
|
||||
I'm not sure how this should work. Maybe the parser should reject non
|
||||
|
@ -535,8 +536,8 @@ ascii characters in strings and identifiers unless the current SQL
|
|||
character set allows them.
|
||||
-}
|
||||
|
||||
,("_francais 'français'"
|
||||
,TypedLit (TypeName [Name Nothing "_francais"]) "français")
|
||||
,e "_francais 'français'"
|
||||
$ TypedLit (TypeName [Name Nothing "_francais"]) "français"
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -547,9 +548,9 @@ character set allows them.
|
|||
|
||||
nationalCharacterStringLiterals :: TestItem
|
||||
nationalCharacterStringLiterals = Group "national character string literals"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("N'something'", StringLit "N'" "'" "something")
|
||||
,("n'something'", StringLit "n'" "'" "something")
|
||||
$
|
||||
[e "N'something'" $ StringLit "N'" "'" "something"
|
||||
,e "n'something'" $ StringLit "n'" "'" "something"
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -566,8 +567,8 @@ nationalCharacterStringLiterals = Group "national character string literals"
|
|||
|
||||
unicodeCharacterStringLiterals :: TestItem
|
||||
unicodeCharacterStringLiterals = Group "unicode character string literals"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("U&'something'", StringLit "U&'" "'" "something")
|
||||
$
|
||||
[e "U&'something'" $ StringLit "U&'" "'" "something"
|
||||
{-,("u&'something' escape ="
|
||||
,Escape (StringLit "u&'" "'" "something") '=')
|
||||
,("u&'something' uescape ="
|
||||
|
@ -587,9 +588,9 @@ TODO: unicode escape
|
|||
|
||||
binaryStringLiterals :: TestItem
|
||||
binaryStringLiterals = Group "binary string literals"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$
|
||||
[--("B'101010'", CSStringLit "B" "101010")
|
||||
("X'7f7f7f'", StringLit "X'" "'" "7f7f7f")
|
||||
e "X'7f7f7f'" $ StringLit "X'" "'" "7f7f7f"
|
||||
--,("X'7f7f7f' escape z", Escape (StringLit "X'" "'" "7f7f7f") 'z')
|
||||
]
|
||||
|
||||
|
@ -619,33 +620,32 @@ binaryStringLiterals = Group "binary string literals"
|
|||
|
||||
numericLiterals :: TestItem
|
||||
numericLiterals = Group "numeric literals"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("11", NumLit "11")
|
||||
,("11.11", NumLit "11.11")
|
||||
[e "11" $ NumLit "11"
|
||||
,e "11.11" $ NumLit "11.11"
|
||||
|
||||
,("11E23", NumLit "11E23")
|
||||
,("11E+23", NumLit "11E+23")
|
||||
,("11E-23", NumLit "11E-23")
|
||||
,e "11E23" $ NumLit "11E23"
|
||||
,e "11E+23" $ NumLit "11E+23"
|
||||
,e "11E-23" $ NumLit "11E-23"
|
||||
|
||||
,("11.11E23", NumLit "11.11E23")
|
||||
,("11.11E+23", NumLit "11.11E+23")
|
||||
,("11.11E-23", NumLit "11.11E-23")
|
||||
,e "11.11E23" $ NumLit "11.11E23"
|
||||
,e "11.11E+23" $ NumLit "11.11E+23"
|
||||
,e "11.11E-23" $ NumLit "11.11E-23"
|
||||
|
||||
,("+11E23", PrefixOp [Name Nothing "+"] $ NumLit "11E23")
|
||||
,("+11E+23", PrefixOp [Name Nothing "+"] $ NumLit "11E+23")
|
||||
,("+11E-23", PrefixOp [Name Nothing "+"] $ NumLit "11E-23")
|
||||
,("+11.11E23", PrefixOp [Name Nothing "+"] $ NumLit "11.11E23")
|
||||
,("+11.11E+23", PrefixOp [Name Nothing "+"] $ NumLit "11.11E+23")
|
||||
,("+11.11E-23", PrefixOp [Name Nothing "+"] $ NumLit "11.11E-23")
|
||||
,e "+11E23" $ PrefixOp [Name Nothing "+"] $ NumLit "11E23"
|
||||
,e "+11E+23" $ PrefixOp [Name Nothing "+"] $ NumLit "11E+23"
|
||||
,e "+11E-23" $ PrefixOp [Name Nothing "+"] $ NumLit "11E-23"
|
||||
,e "+11.11E23" $ PrefixOp [Name Nothing "+"] $ NumLit "11.11E23"
|
||||
,e "+11.11E+23" $ PrefixOp [Name Nothing "+"] $ NumLit "11.11E+23"
|
||||
,e "+11.11E-23" $ PrefixOp [Name Nothing "+"] $ NumLit "11.11E-23"
|
||||
|
||||
,("-11E23", PrefixOp [Name Nothing "-"] $ NumLit "11E23")
|
||||
,("-11E+23", PrefixOp [Name Nothing "-"] $ NumLit "11E+23")
|
||||
,("-11E-23", PrefixOp [Name Nothing "-"] $ NumLit "11E-23")
|
||||
,("-11.11E23", PrefixOp [Name Nothing "-"] $ NumLit "11.11E23")
|
||||
,("-11.11E+23", PrefixOp [Name Nothing "-"] $ NumLit "11.11E+23")
|
||||
,("-11.11E-23", PrefixOp [Name Nothing "-"] $ NumLit "11.11E-23")
|
||||
,e "-11E23" $ PrefixOp [Name Nothing "-"] $ NumLit "11E23"
|
||||
,e "-11E+23" $ PrefixOp [Name Nothing "-"] $ NumLit "11E+23"
|
||||
,e "-11E-23" $ PrefixOp [Name Nothing "-"] $ NumLit "11E-23"
|
||||
,e "-11.11E23" $ PrefixOp [Name Nothing "-"] $ NumLit "11.11E23"
|
||||
,e "-11.11E+23" $ PrefixOp [Name Nothing "-"] $ NumLit "11.11E+23"
|
||||
,e "-11.11E-23" $ PrefixOp [Name Nothing "-"] $ NumLit "11.11E-23"
|
||||
|
||||
,("11.11e23", NumLit "11.11e23")
|
||||
,e "11.11e23" $ NumLit "11.11e23"
|
||||
|
||||
]
|
||||
|
||||
|
@ -729,33 +729,30 @@ dateTimeLiterals = Group "datetime literals"
|
|||
|
||||
intervalLiterals :: TestItem
|
||||
intervalLiterals = Group "intervalLiterals literals"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("interval '1'", TypedLit (TypeName [Name Nothing "interval"]) "1")
|
||||
,("interval '1' day"
|
||||
,IntervalLit Nothing "1" (Itf "day" Nothing) Nothing)
|
||||
,("interval '1' day(3)"
|
||||
,IntervalLit Nothing "1" (Itf "day" $ Just (3,Nothing)) Nothing)
|
||||
,("interval + '1' day(3)"
|
||||
,IntervalLit (Just Plus) "1" (Itf "day" $ Just (3,Nothing)) Nothing)
|
||||
,("interval - '1' second(2,2)"
|
||||
,IntervalLit (Just Minus) "1" (Itf "second" $ Just (2,Just 2)) Nothing)
|
||||
,("interval '1' year to month"
|
||||
,IntervalLit Nothing "1" (Itf "year" Nothing)
|
||||
(Just $ Itf "month" Nothing))
|
||||
|
||||
,("interval '1' year(4) to second(2,3) "
|
||||
,IntervalLit Nothing "1" (Itf "year" $ Just (4,Nothing))
|
||||
(Just $ Itf "second" $ Just (2, Just 3)))
|
||||
[e "interval '1'" $ TypedLit (TypeName [Name Nothing "interval"]) "1"
|
||||
,e "interval '1' day"
|
||||
$ IntervalLit Nothing "1" (Itf "day" Nothing) Nothing
|
||||
,e "interval '1' day(3)"
|
||||
$ IntervalLit Nothing "1" (Itf "day" $ Just (3,Nothing)) Nothing
|
||||
,e "interval + '1' day(3)"
|
||||
$ IntervalLit (Just Plus) "1" (Itf "day" $ Just (3,Nothing)) Nothing
|
||||
,e "interval - '1' second(2,2)"
|
||||
$ IntervalLit (Just Minus) "1" (Itf "second" $ Just (2,Just 2)) Nothing
|
||||
,e "interval '1' year to month"
|
||||
$ IntervalLit Nothing "1" (Itf "year" Nothing)
|
||||
(Just $ Itf "month" Nothing)
|
||||
,e "interval '1' year(4) to second(2,3) "
|
||||
$ IntervalLit Nothing "1" (Itf "year" $ Just (4,Nothing))
|
||||
(Just $ Itf "second" $ Just (2, Just 3))
|
||||
]
|
||||
|
||||
-- <boolean literal> ::= TRUE | FALSE | UNKNOWN
|
||||
|
||||
booleanLiterals :: TestItem
|
||||
booleanLiterals = Group "boolean literals"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("true", Iden [Name Nothing "true"])
|
||||
,("false", Iden [Name Nothing "false"])
|
||||
,("unknown", Iden [Name Nothing "unknown"])
|
||||
[e "true" $ Iden [Name Nothing "true"]
|
||||
,e "false" $ Iden [Name Nothing "false"]
|
||||
,e "unknown" $ Iden [Name Nothing "unknown"]
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -774,16 +771,15 @@ Specify names.
|
|||
|
||||
identifiers :: TestItem
|
||||
identifiers = Group "identifiers"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("test",Iden [Name Nothing "test"])
|
||||
,("_test",Iden [Name Nothing "_test"])
|
||||
,("t1",Iden [Name Nothing "t1"])
|
||||
,("a.b",Iden [Name Nothing "a", Name Nothing "b"])
|
||||
,("a.b.c",Iden [Name Nothing "a", Name Nothing "b", Name Nothing "c"])
|
||||
,("\"quoted iden\"", Iden [Name (Just ("\"","\"")) "quoted iden"])
|
||||
,("\"quoted \"\" iden\"", Iden [Name (Just ("\"","\"")) "quoted \"\" iden"])
|
||||
,("U&\"quoted iden\"", Iden [Name (Just ("U&\"","\"")) "quoted iden"])
|
||||
,("U&\"quoted \"\" iden\"", Iden [Name (Just ("U&\"","\"")) "quoted \"\" iden"])
|
||||
[e "test" $ Iden [Name Nothing "test"]
|
||||
,e "_test" $ Iden [Name Nothing "_test"]
|
||||
,e "t1" $ Iden [Name Nothing "t1"]
|
||||
,e "a.b" $ Iden [Name Nothing "a", Name Nothing "b"]
|
||||
,e "a.b.c" $ Iden [Name Nothing "a", Name Nothing "b", Name Nothing "c"]
|
||||
,e "\"quoted iden\"" $ Iden [Name (Just ("\"", "\"")) "quoted iden"]
|
||||
,e "\"quoted \"\" iden\"" $ Iden [Name (Just ("\"", "\"")) "quoted \"\" iden"]
|
||||
,e "U&\"quoted iden\"" $ Iden [Name (Just ("U&\"", "\"")) "quoted iden"]
|
||||
,e "U&\"quoted \"\" iden\"" $ Iden [Name (Just ("U&\"", "\"")) "quoted \"\" iden"]
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -1220,11 +1216,11 @@ expression
|
|||
|
||||
typeNameTests :: TestItem
|
||||
typeNameTests = Group "type names"
|
||||
[Group "type names" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[Group "type names" $ map (uncurry (testScalarExpr ansi2011))
|
||||
$ concatMap makeSimpleTests $ fst typeNames
|
||||
,Group "generated casts" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
,Group "generated casts" $ map (uncurry (testScalarExpr ansi2011))
|
||||
$ concatMap makeCastTests $ fst typeNames
|
||||
,Group "generated typename" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
,Group "generated typename" $ map (uncurry (testScalarExpr ansi2011))
|
||||
$ concatMap makeTests $ snd typeNames]
|
||||
where
|
||||
makeSimpleTests (ctn, stn) =
|
||||
|
@ -1247,12 +1243,10 @@ Define a field of a row type.
|
|||
|
||||
fieldDefinition :: TestItem
|
||||
fieldDefinition = Group "field definition"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("cast('(1,2)' as row(a int,b char))"
|
||||
,Cast (StringLit "'" "'" "(1,2)")
|
||||
[e "cast('(1,2)' as row(a int,b char))"
|
||||
$ Cast (StringLit "'" "'" "(1,2)")
|
||||
$ RowTypeName [(Name Nothing "a", TypeName [Name Nothing "int"])
|
||||
,(Name Nothing "b", TypeName [Name Nothing "char"])])]
|
||||
|
||||
,(Name Nothing "b", TypeName [Name Nothing "char"])]]
|
||||
{-
|
||||
== 6.3 <value expression primary>
|
||||
|
||||
|
@ -1329,9 +1323,8 @@ valueExpressions = Group "value expressions"
|
|||
|
||||
parenthesizedScalarExpression :: TestItem
|
||||
parenthesizedScalarExpression = Group "parenthesized value expression"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("(3)", Parens (NumLit "3"))
|
||||
,("((3))", Parens $ Parens (NumLit "3"))
|
||||
[e "(3)" $ Parens (NumLit "3")
|
||||
,e "((3))" $ Parens $ Parens (NumLit "3")
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -1367,8 +1360,7 @@ Specify one or more values, host parameters, SQL parameters, dynamic parameters,
|
|||
|
||||
generalValueSpecification :: TestItem
|
||||
generalValueSpecification = Group "general value specification"
|
||||
$ map (uncurry (TestScalarExpr ansi2011)) $
|
||||
map mkIden ["CURRENT_DEFAULT_TRANSFORM_GROUP"
|
||||
$ map mkIden ["CURRENT_DEFAULT_TRANSFORM_GROUP"
|
||||
,"CURRENT_PATH"
|
||||
,"CURRENT_ROLE"
|
||||
,"CURRENT_USER"
|
||||
|
@ -1377,7 +1369,7 @@ generalValueSpecification = Group "general value specification"
|
|||
,"USER"
|
||||
,"VALUE"]
|
||||
where
|
||||
mkIden nm = (nm,Iden [Name Nothing nm])
|
||||
mkIden nm = e nm $ Iden [Name Nothing nm]
|
||||
|
||||
{-
|
||||
TODO: add the missing bits
|
||||
|
@ -1423,12 +1415,11 @@ TODO: add the missing bits
|
|||
|
||||
parameterSpecification :: TestItem
|
||||
parameterSpecification = Group "parameter specification"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[(":hostparam", HostParameter ":hostparam" Nothing)
|
||||
,(":hostparam indicator :another_host_param"
|
||||
,HostParameter ":hostparam" $ Just ":another_host_param")
|
||||
,("?", Parameter)
|
||||
,(":h[3]", Array (HostParameter ":h" Nothing) [NumLit "3"])
|
||||
[e ":hostparam" $ HostParameter ":hostparam" Nothing
|
||||
,e ":hostparam indicator :another_host_param"
|
||||
$ HostParameter ":hostparam" $ Just ":another_host_param"
|
||||
,e "?" $ Parameter
|
||||
,e ":h[3]" $ Array (HostParameter ":h" Nothing) [NumLit "3"]
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -1462,11 +1453,10 @@ Specify a value whose data type is to be inferred from its context.
|
|||
contextuallyTypedValueSpecification :: TestItem
|
||||
contextuallyTypedValueSpecification =
|
||||
Group "contextually typed value specification"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("null", Iden [Name Nothing "null"])
|
||||
,("array[]", Array (Iden [Name Nothing "array"]) [])
|
||||
,("multiset[]", MultisetCtor [])
|
||||
,("default", Iden [Name Nothing "default"])
|
||||
[e "null" $ Iden [Name Nothing "null"]
|
||||
,e "array[]" $ Array (Iden [Name Nothing "array"]) []
|
||||
,e "multiset[]" $ MultisetCtor []
|
||||
,e "default" $ Iden [Name Nothing "default"]
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -1482,8 +1472,7 @@ Disambiguate a <period>-separated chain of identifiers.
|
|||
|
||||
identifierChain :: TestItem
|
||||
identifierChain = Group "identifier chain"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("a.b", Iden [Name Nothing "a",Name Nothing "b"])]
|
||||
[e "a.b" $ Iden [Name Nothing "a",Name Nothing "b"]]
|
||||
|
||||
{-
|
||||
== 6.7 <column reference>
|
||||
|
@ -1498,8 +1487,7 @@ Reference a column.
|
|||
|
||||
columnReference :: TestItem
|
||||
columnReference = Group "column reference"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("module.a.b", Iden [Name Nothing "module",Name Nothing "a",Name Nothing "b"])]
|
||||
[e "module.a.b" $ Iden [Name Nothing "module",Name Nothing "a",Name Nothing "b"]]
|
||||
|
||||
{-
|
||||
== 6.8 <SQL parameter reference>
|
||||
|
@ -1523,19 +1511,19 @@ Specify a value derived by the application of a function to an argument.
|
|||
|
||||
setFunctionSpecification :: TestItem
|
||||
setFunctionSpecification = Group "set function specification"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("SELECT SalesQuota, SUM(SalesYTD) TotalSalesYTD,\n\
|
||||
$
|
||||
[q "SELECT SalesQuota, SUM(SalesYTD) TotalSalesYTD,\n\
|
||||
\ GROUPING(SalesQuota) AS Grouping\n\
|
||||
\FROM Sales.SalesPerson\n\
|
||||
\GROUP BY ROLLUP(SalesQuota);"
|
||||
,toQueryExpr $ makeSelect
|
||||
$ toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Iden [Name Nothing "SalesQuota"],Nothing)
|
||||
,(App [Name Nothing "SUM"] [Iden [Name Nothing "SalesYTD"]]
|
||||
,Just (Name Nothing "TotalSalesYTD"))
|
||||
,(App [Name Nothing "GROUPING"] [Iden [Name Nothing "SalesQuota"]]
|
||||
,Just (Name Nothing "Grouping"))]
|
||||
,msFrom = [TRSimple [Name Nothing "Sales",Name Nothing "SalesPerson"]]
|
||||
,msGroupBy = [Rollup [SimpleGroup (Iden [Name Nothing "SalesQuota"])]]})
|
||||
,msGroupBy = [Rollup [SimpleGroup (Iden [Name Nothing "SalesQuota"])]]}
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -1732,9 +1720,8 @@ Specify a data conversion.
|
|||
|
||||
castSpecification :: TestItem
|
||||
castSpecification = Group "cast specification"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("cast(a as int)"
|
||||
,Cast (Iden [Name Nothing "a"]) (TypeName [Name Nothing "int"]))
|
||||
[e "cast(a as int)"
|
||||
$ Cast (Iden [Name Nothing "a"]) (TypeName [Name Nothing "int"])
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -1748,8 +1735,7 @@ Return the next value of a sequence generator.
|
|||
|
||||
nextScalarExpression :: TestItem
|
||||
nextScalarExpression = Group "next value expression"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("next value for a.b", NextValueFor [Name Nothing "a", Name Nothing "b"])
|
||||
[e "next value for a.b" $ NextValueFor [Name Nothing "a", Name Nothing "b"]
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -1763,11 +1749,10 @@ Reference a field of a row value.
|
|||
|
||||
fieldReference :: TestItem
|
||||
fieldReference = Group "field reference"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("f(something).a"
|
||||
,BinOp (App [Name Nothing "f"] [Iden [Name Nothing "something"]])
|
||||
[e "f(something).a"
|
||||
$ BinOp (App [Name Nothing "f"] [Iden [Name Nothing "something"]])
|
||||
[Name Nothing "."]
|
||||
(Iden [Name Nothing "a"]))
|
||||
(Iden [Name Nothing "a"])
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -1889,17 +1874,16 @@ Return an element of an array.
|
|||
|
||||
arrayElementReference :: TestItem
|
||||
arrayElementReference = Group "array element reference"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("something[3]"
|
||||
,Array (Iden [Name Nothing "something"]) [NumLit "3"])
|
||||
,("(something(a))[x]"
|
||||
,Array (Parens (App [Name Nothing "something"] [Iden [Name Nothing "a"]]))
|
||||
[Iden [Name Nothing "x"]])
|
||||
,("(something(a))[x][y] "
|
||||
,Array (
|
||||
[e "something[3]"
|
||||
$ Array (Iden [Name Nothing "something"]) [NumLit "3"]
|
||||
,e "(something(a))[x]"
|
||||
$ Array (Parens (App [Name Nothing "something"] [Iden [Name Nothing "a"]]))
|
||||
[Iden [Name Nothing "x"]]
|
||||
,e "(something(a))[x][y] "
|
||||
$ Array (
|
||||
Array (Parens (App [Name Nothing "something"] [Iden [Name Nothing "a"]]))
|
||||
[Iden [Name Nothing "x"]])
|
||||
[Iden [Name Nothing "y"]])
|
||||
[Iden [Name Nothing "y"]]
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -1914,9 +1898,8 @@ Return the sole element of a multiset of one element.
|
|||
|
||||
multisetElementReference :: TestItem
|
||||
multisetElementReference = Group "multisetElementReference"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("element(something)"
|
||||
,App [Name Nothing "element"] [Iden [Name Nothing "something"]])
|
||||
[e "element(something)"
|
||||
$ App [Name Nothing "element"] [Iden [Name Nothing "something"]]
|
||||
]
|
||||
|
||||
{-
|
||||
|
@ -1966,13 +1949,12 @@ Specify a numeric value.
|
|||
|
||||
numericScalarExpression :: TestItem
|
||||
numericScalarExpression = Group "numeric value expression"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("a + b", binOp "+")
|
||||
,("a - b", binOp "-")
|
||||
,("a * b", binOp "*")
|
||||
,("a / b", binOp "/")
|
||||
,("+a", prefOp "+")
|
||||
,("-a", prefOp "-")
|
||||
[e "a + b" $ binOp "+"
|
||||
,e "a - b" $ binOp "-"
|
||||
,e "a * b" $ binOp "*"
|
||||
,e "a / b" $ binOp "/"
|
||||
,e "+a" $ prefOp "+"
|
||||
,e "-a" $ prefOp "-"
|
||||
]
|
||||
where
|
||||
binOp o = BinOp (Iden [Name Nothing "a"]) [Name Nothing o] (Iden [Name Nothing "b"])
|
||||
|
@ -2439,17 +2421,16 @@ Specify a boolean value.
|
|||
|
||||
booleanScalarExpression :: TestItem
|
||||
booleanScalarExpression = Group "booleab value expression"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("a or b", BinOp a [Name Nothing "or"] b)
|
||||
,("a and b", BinOp a [Name Nothing "and"] b)
|
||||
,("not a", PrefixOp [Name Nothing "not"] a)
|
||||
,("a is true", postfixOp "is true")
|
||||
,("a is false", postfixOp "is false")
|
||||
,("a is unknown", postfixOp "is unknown")
|
||||
,("a is not true", postfixOp "is not true")
|
||||
,("a is not false", postfixOp "is not false")
|
||||
,("a is not unknown", postfixOp "is not unknown")
|
||||
,("(a or b)", Parens $ BinOp a [Name Nothing "or"] b)
|
||||
[e "a or b" $ BinOp a [Name Nothing "or"] b
|
||||
,e "a and b" $ BinOp a [Name Nothing "and"] b
|
||||
,e "not a" $ PrefixOp [Name Nothing "not"] a
|
||||
,e "a is true" $ postfixOp "is true"
|
||||
,e "a is false" $ postfixOp "is false"
|
||||
,e "a is unknown" $ postfixOp "is unknown"
|
||||
,e "a is not true" $ postfixOp "is not true"
|
||||
,e "a is not false" $ postfixOp "is not false"
|
||||
,e "a is not unknown" $ postfixOp "is not unknown"
|
||||
,e "(a or b)" $ Parens $ BinOp a [Name Nothing "or"] b
|
||||
]
|
||||
where
|
||||
a = Iden [Name Nothing "a"]
|
||||
|
@ -2520,23 +2501,22 @@ Specify construction of an array.
|
|||
|
||||
arrayValueConstructor :: TestItem
|
||||
arrayValueConstructor = Group "array value constructor"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("array[1,2,3]"
|
||||
,Array (Iden [Name Nothing "array"])
|
||||
[NumLit "1", NumLit "2", NumLit "3"])
|
||||
,("array[a,b,c]"
|
||||
,Array (Iden [Name Nothing "array"])
|
||||
[Iden [Name Nothing "a"], Iden [Name Nothing "b"], Iden [Name Nothing "c"]])
|
||||
,("array(select * from t)"
|
||||
,ArrayCtor (toQueryExpr $ makeSelect
|
||||
[e "array[1,2,3]"
|
||||
$ Array (Iden [Name Nothing "array"])
|
||||
[NumLit "1", NumLit "2", NumLit "3"]
|
||||
,e "array[a,b,c]"
|
||||
$ Array (Iden [Name Nothing "array"])
|
||||
[Iden [Name Nothing "a"], Iden [Name Nothing "b"], Iden [Name Nothing "c"]]
|
||||
,e "array(select * from t)"
|
||||
$ ArrayCtor (toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star,Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]}))
|
||||
,("array(select * from t order by a)"
|
||||
,ArrayCtor (toQueryExpr $ makeSelect
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]})
|
||||
,e "array(select * from t order by a)"
|
||||
$ ArrayCtor (toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star,Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
,msOrderBy = [SortSpec (Iden [Name Nothing "a"])
|
||||
DirDefault NullsOrderDefault]}))
|
||||
DirDefault NullsOrderDefault]})
|
||||
]
|
||||
|
||||
|
||||
|
@ -2560,7 +2540,7 @@ Specify a multiset value.
|
|||
|
||||
multisetScalarExpression :: TestItem
|
||||
multisetScalarExpression = Group "multiset value expression"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$ map (uncurry (testScalarExpr ansi2011))
|
||||
[("a multiset union b"
|
||||
,MultisetBinOp (Iden [Name Nothing "a"]) Union SQDefault (Iden [Name Nothing "b"]))
|
||||
,("a multiset union all b"
|
||||
|
@ -2592,7 +2572,7 @@ special case term.
|
|||
|
||||
multisetValueFunction :: TestItem
|
||||
multisetValueFunction = Group "multiset value function"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$ map (uncurry (testScalarExpr ansi2011))
|
||||
[("set(a)", App [Name Nothing "set"] [Iden [Name Nothing "a"]])
|
||||
]
|
||||
|
||||
|
@ -2622,7 +2602,7 @@ Specify construction of a multiset.
|
|||
|
||||
multisetValueConstructor :: TestItem
|
||||
multisetValueConstructor = Group "multiset value constructor"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$ map (uncurry (testScalarExpr ansi2011))
|
||||
[("multiset[a,b,c]", MultisetCtor[Iden [Name Nothing "a"]
|
||||
,Iden [Name Nothing "b"], Iden [Name Nothing "c"]])
|
||||
,("multiset(select * from t)", MultisetQueryCtor ms)
|
||||
|
@ -2702,7 +2682,7 @@ Specify a value or list of values to be constructed into a row.
|
|||
|
||||
rowValueConstructor :: TestItem
|
||||
rowValueConstructor = Group "row value constructor"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$ map (uncurry (testScalarExpr ansi2011))
|
||||
[("(a,b)"
|
||||
,SpecialOp [Name Nothing "rowctor"] [Iden [Name Nothing "a"], Iden [Name Nothing "b"]])
|
||||
,("row(1)",App [Name Nothing "row"] [NumLit "1"])
|
||||
|
@ -2755,7 +2735,7 @@ Specify a set of <row value expression>s to be constructed into a table.
|
|||
|
||||
tableValueConstructor :: TestItem
|
||||
tableValueConstructor = Group "table value constructor"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[("values (1,2), (a+b,(select count(*) from t));"
|
||||
,Values [[NumLit "1", NumLit "2"]
|
||||
,[BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"]
|
||||
|
@ -2792,7 +2772,7 @@ Specify a table derived from one or more tables.
|
|||
|
||||
fromClause :: TestItem
|
||||
fromClause = Group "fromClause"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[("select * from tbl1,tbl2"
|
||||
,toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star, Nothing)]
|
||||
|
@ -2809,7 +2789,7 @@ Reference a table.
|
|||
|
||||
tableReference :: TestItem
|
||||
tableReference = Group "table reference"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[("select * from t", toQueryExpr sel)
|
||||
|
||||
{-
|
||||
|
@ -2994,7 +2974,7 @@ Specify a table derived from a Cartesian product, inner join, or outer join.
|
|||
|
||||
joinedTable :: TestItem
|
||||
joinedTable = Group "joined table"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[("select * from a cross join b"
|
||||
,sel $ TRJoin a False JCross b Nothing)
|
||||
,("select * from a join b on true"
|
||||
|
@ -3053,7 +3033,7 @@ the result of the preceding <from clause>.
|
|||
|
||||
whereClause :: TestItem
|
||||
whereClause = Group "where clause"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[("select * from t where a = 5"
|
||||
,toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star,Nothing)]
|
||||
|
@ -3115,7 +3095,7 @@ clause> to the result of the previously specified clause.
|
|||
|
||||
groupByClause :: TestItem
|
||||
groupByClause = Group "group by clause"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[("select a,sum(x) from t group by a"
|
||||
,toQueryExpr $ ms [SimpleGroup $ Iden [Name Nothing "a"]])
|
||||
,("select a,sum(x) from t group by a collate c"
|
||||
|
@ -3170,7 +3150,7 @@ not satisfy a <search condition>.
|
|||
|
||||
havingClause :: TestItem
|
||||
havingClause = Group "having clause"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[("select a,sum(x) from t group by a having sum(x) > 1000"
|
||||
,toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Iden [Name Nothing "a"], Nothing)
|
||||
|
@ -3297,7 +3277,7 @@ Specify a table derived from the result of a <table expression>.
|
|||
|
||||
querySpecification :: TestItem
|
||||
querySpecification = Group "query specification"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[("select a from t",toQueryExpr ms)
|
||||
,("select all a from t",toQueryExpr $ ms {msSetQuantifier = All})
|
||||
,("select distinct a from t",toQueryExpr $ ms {msSetQuantifier = Distinct})
|
||||
|
@ -3369,7 +3349,7 @@ withQueryExpression= Group "with query expression"
|
|||
|
||||
setOpQueryExpression :: TestItem
|
||||
setOpQueryExpression= Group "set operation query expression"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
-- todo: complete setop query expression tests
|
||||
[{-("select * from t union select * from t"
|
||||
,undefined)
|
||||
|
@ -3408,7 +3388,7 @@ everywhere
|
|||
|
||||
explicitTableQueryExpression :: TestItem
|
||||
explicitTableQueryExpression= Group "explicit table query expression"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[("table t", Table [Name Nothing "t"])
|
||||
]
|
||||
|
||||
|
@ -3432,7 +3412,7 @@ explicitTableQueryExpression= Group "explicit table query expression"
|
|||
|
||||
orderOffsetFetchQueryExpression :: TestItem
|
||||
orderOffsetFetchQueryExpression = Group "order, offset, fetch query expression"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[-- todo: finish tests for order offset and fetch
|
||||
("select a from t order by a"
|
||||
,toQueryExpr $ ms {msOrderBy = [SortSpec (Iden [Name Nothing "a"])
|
||||
|
@ -3597,7 +3577,7 @@ Specify a comparison of two row values.
|
|||
|
||||
comparisonPredicates :: TestItem
|
||||
comparisonPredicates = Group "comparison predicates"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$ map (uncurry (testScalarExpr ansi2011))
|
||||
$ map mkOp ["=", "<>", "<", ">", "<=", ">="]
|
||||
<> [("ROW(a) = ROW(b)"
|
||||
,BinOp (App [Name Nothing "ROW"] [a])
|
||||
|
@ -3815,7 +3795,7 @@ Specify a quantified comparison.
|
|||
|
||||
quantifiedComparisonPredicate :: TestItem
|
||||
quantifiedComparisonPredicate = Group "quantified comparison predicate"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$ map (uncurry (testScalarExpr ansi2011))
|
||||
|
||||
[("a = any (select * from t)"
|
||||
,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "="] CPAny ms)
|
||||
|
@ -3844,7 +3824,7 @@ Specify a test for a non-empty set.
|
|||
|
||||
existsPredicate :: TestItem
|
||||
existsPredicate = Group "exists predicate"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$ map (uncurry (testScalarExpr ansi2011))
|
||||
[("exists(select * from t where a = 4)"
|
||||
,SubQueryExpr SqExists
|
||||
$ toQueryExpr $ makeSelect
|
||||
|
@ -3865,7 +3845,7 @@ Specify a test for the absence of duplicate rows.
|
|||
|
||||
uniquePredicate :: TestItem
|
||||
uniquePredicate = Group "unique predicate"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$ map (uncurry (testScalarExpr ansi2011))
|
||||
[("unique(select * from t where a = 4)"
|
||||
,SubQueryExpr SqUnique
|
||||
$ toQueryExpr $ makeSelect
|
||||
|
@ -3905,7 +3885,7 @@ Specify a test for matching rows.
|
|||
|
||||
matchPredicate :: TestItem
|
||||
matchPredicate = Group "match predicate"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$ map (uncurry (testScalarExpr ansi2011))
|
||||
[("a match (select a from t)"
|
||||
,Match (Iden [Name Nothing "a"]) False $ toQueryExpr ms)
|
||||
,("(a,b) match (select a,b from t)"
|
||||
|
@ -4273,7 +4253,7 @@ Specify a default collation.
|
|||
|
||||
collateClause :: TestItem
|
||||
collateClause = Group "collate clause"
|
||||
$ map (uncurry (TestScalarExpr ansi2011))
|
||||
$ map (uncurry (testScalarExpr ansi2011))
|
||||
[("a collate my_collation"
|
||||
,Collate (Iden [Name Nothing "a"]) [Name Nothing "my_collation"])]
|
||||
|
||||
|
@ -4386,7 +4366,7 @@ Specify a value computed from a collection of rows.
|
|||
|
||||
aggregateFunction :: TestItem
|
||||
aggregateFunction = Group "aggregate function"
|
||||
$ map (uncurry (TestScalarExpr ansi2011)) $
|
||||
$ map (uncurry (testScalarExpr ansi2011)) $
|
||||
[("count(*)",App [Name Nothing "count"] [Star])
|
||||
,("count(*) filter (where something > 5)"
|
||||
,AggregateApp [Name Nothing "count"] SQDefault [Star] [] fil)
|
||||
|
@ -4483,7 +4463,7 @@ Specify a sort order.
|
|||
|
||||
sortSpecificationList :: TestItem
|
||||
sortSpecificationList = Group "sort specification list"
|
||||
$ map (uncurry (TestQueryExpr ansi2011))
|
||||
$ map (uncurry (testQueryExpr ansi2011))
|
||||
[("select * from t order by a"
|
||||
,toQueryExpr $ ms {msOrderBy = [SortSpec (Iden [Name Nothing "a"])
|
||||
DirDefault NullsOrderDefault]})
|
||||
|
@ -4518,3 +4498,10 @@ sortSpecificationList = Group "sort specification list"
|
|||
ms = makeSelect
|
||||
{msSelectList = [(Star,Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]}
|
||||
|
||||
|
||||
q :: HasCallStack => Text -> QueryExpr -> TestItem
|
||||
q src ast = testQueryExpr ansi2011 src ast
|
||||
|
||||
e :: HasCallStack => Text -> ScalarExpr -> TestItem
|
||||
e src ast = testScalarExpr ansi2011 src ast
|
||||
|
|
|
@ -10,6 +10,8 @@ module Language.SQL.SimpleSQL.SQL2011Schema (sql2011SchemaTests) where
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
sql2011SchemaTests :: TestItem
|
||||
sql2011SchemaTests = Group "sql 2011 schema tests"
|
||||
|
@ -25,8 +27,8 @@ sql2011SchemaTests = Group "sql 2011 schema tests"
|
|||
[ <schema element>... ]
|
||||
-}
|
||||
|
||||
(TestStatement ansi2011 "create schema my_schema"
|
||||
$ CreateSchema [Name Nothing "my_schema"])
|
||||
s "create schema my_schema"
|
||||
$ CreateSchema [Name Nothing "my_schema"]
|
||||
|
||||
{-
|
||||
todo: schema name can have .
|
||||
|
@ -86,12 +88,12 @@ add schema element support:
|
|||
-}
|
||||
|
||||
|
||||
,(TestStatement ansi2011 "drop schema my_schema"
|
||||
$ DropSchema [Name Nothing "my_schema"] DefaultDropBehaviour)
|
||||
,(TestStatement ansi2011 "drop schema my_schema cascade"
|
||||
$ DropSchema [Name Nothing "my_schema"] Cascade)
|
||||
,(TestStatement ansi2011 "drop schema my_schema restrict"
|
||||
$ DropSchema [Name Nothing "my_schema"] Restrict)
|
||||
,s "drop schema my_schema"
|
||||
$ DropSchema [Name Nothing "my_schema"] DefaultDropBehaviour
|
||||
,s "drop schema my_schema cascade"
|
||||
$ DropSchema [Name Nothing "my_schema"] Cascade
|
||||
,s "drop schema my_schema restrict"
|
||||
$ DropSchema [Name Nothing "my_schema"] Restrict
|
||||
|
||||
{-
|
||||
11.3 <table definition>
|
||||
|
@ -103,10 +105,10 @@ add schema element support:
|
|||
[ ON COMMIT <table commit action> ROWS ]
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011 "create table t (a int, b int);"
|
||||
,s "create table t (a int, b int);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
|
||||
,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []])
|
||||
,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []]
|
||||
|
||||
|
||||
{-
|
||||
|
@ -321,35 +323,35 @@ todo: constraint characteristics
|
|||
-}
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int not null);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing ColNotNullConstraint]])
|
||||
[ColConstraintDef Nothing ColNotNullConstraint]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int constraint a_not_null not null);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef (Just [Name Nothing "a_not_null"]) ColNotNullConstraint]])
|
||||
[ColConstraintDef (Just [Name Nothing "a_not_null"]) ColNotNullConstraint]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int unique);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing ColUniqueConstraint]])
|
||||
[ColConstraintDef Nothing ColUniqueConstraint]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int primary key);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing (ColPrimaryKeyConstraint False)]])
|
||||
[ColConstraintDef Nothing (ColPrimaryKeyConstraint False)]]
|
||||
|
||||
,(TestStatement ansi2011 { diAutoincrement = True }
|
||||
,testStatement ansi2011{ diAutoincrement = True }
|
||||
"create table t (a int primary key autoincrement);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing (ColPrimaryKeyConstraint True)]])
|
||||
[ColConstraintDef Nothing (ColPrimaryKeyConstraint True)]]
|
||||
|
||||
{-
|
||||
references t(a,b)
|
||||
|
@ -358,102 +360,102 @@ references t(a,b)
|
|||
on delete ""
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing DefaultReferenceMatch
|
||||
DefaultReferentialAction DefaultReferentialAction]])
|
||||
DefaultReferentialAction DefaultReferentialAction]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u(a));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] (Just $ Name Nothing "a") DefaultReferenceMatch
|
||||
DefaultReferentialAction DefaultReferentialAction]])
|
||||
DefaultReferentialAction DefaultReferentialAction]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u match full);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing MatchFull
|
||||
DefaultReferentialAction DefaultReferentialAction]])
|
||||
DefaultReferentialAction DefaultReferentialAction]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u match partial);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing MatchPartial
|
||||
DefaultReferentialAction DefaultReferentialAction]])
|
||||
DefaultReferentialAction DefaultReferentialAction]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u match simple);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing MatchSimple
|
||||
DefaultReferentialAction DefaultReferentialAction]])
|
||||
DefaultReferentialAction DefaultReferentialAction]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u on update cascade );"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing DefaultReferenceMatch
|
||||
RefCascade DefaultReferentialAction]])
|
||||
RefCascade DefaultReferentialAction]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u on update set null );"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing DefaultReferenceMatch
|
||||
RefSetNull DefaultReferentialAction]])
|
||||
RefSetNull DefaultReferentialAction]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u on update set default );"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing DefaultReferenceMatch
|
||||
RefSetDefault DefaultReferentialAction]])
|
||||
RefSetDefault DefaultReferentialAction]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u on update no action );"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing DefaultReferenceMatch
|
||||
RefNoAction DefaultReferentialAction]])
|
||||
RefNoAction DefaultReferentialAction]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u on delete cascade );"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing DefaultReferenceMatch
|
||||
DefaultReferentialAction RefCascade]])
|
||||
DefaultReferentialAction RefCascade]]
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u on update cascade on delete restrict );"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing DefaultReferenceMatch
|
||||
RefCascade RefRestrict]])
|
||||
RefCascade RefRestrict]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int references u on delete restrict on update cascade );"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing $ ColReferencesConstraint
|
||||
[Name Nothing "u"] Nothing DefaultReferenceMatch
|
||||
RefCascade RefRestrict]])
|
||||
RefCascade RefRestrict]]
|
||||
|
||||
{-
|
||||
TODO: try combinations and permutations of column constraints and
|
||||
|
@ -461,12 +463,12 @@ options
|
|||
-}
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int check (a>5));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
|
||||
[ColConstraintDef Nothing
|
||||
(ColCheckConstraint $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5"))]])
|
||||
(ColCheckConstraint $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5"))]]
|
||||
|
||||
|
||||
|
||||
|
@ -478,18 +480,18 @@ options
|
|||
[ <left paren> <common sequence generator options> <right paren> ]
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011 "create table t (a int generated always as identity);"
|
||||
,s "create table t (a int generated always as identity);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
|
||||
(Just $ IdentityColumnSpec GeneratedAlways []) []])
|
||||
(Just $ IdentityColumnSpec GeneratedAlways []) []]
|
||||
|
||||
,(TestStatement ansi2011 "create table t (a int generated by default as identity);"
|
||||
,s "create table t (a int generated by default as identity);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
|
||||
(Just $ IdentityColumnSpec GeneratedByDefault []) []])
|
||||
(Just $ IdentityColumnSpec GeneratedByDefault []) []]
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int generated always as identity\n\
|
||||
\ ( start with 5 increment by 5 maxvalue 500 minvalue 5 cycle ));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
|
@ -499,9 +501,9 @@ options
|
|||
,SGOIncrementBy 5
|
||||
,SGOMaxValue 500
|
||||
,SGOMinValue 5
|
||||
,SGOCycle]) []])
|
||||
,SGOCycle]) []]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int generated always as identity\n\
|
||||
\ ( start with -4 no maxvalue no minvalue no cycle ));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
|
@ -510,7 +512,7 @@ options
|
|||
[SGOStartWith (-4)
|
||||
,SGONoMaxValue
|
||||
,SGONoMinValue
|
||||
,SGONoCycle]) []])
|
||||
,SGONoCycle]) []]
|
||||
|
||||
{-
|
||||
I think <common sequence generator options> is supposed to just
|
||||
|
@ -531,14 +533,14 @@ generated always (valueexpr)
|
|||
<left paren> <value expression> <right paren>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int, \n\
|
||||
\ a2 int generated always as (a * 2));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
|
||||
,TableColumnDef $ ColumnDef (Name Nothing "a2") (TypeName [Name Nothing "int"])
|
||||
(Just $ GenerationClause
|
||||
(BinOp (Iden [Name Nothing "a"]) [Name Nothing "*"] (NumLit "2"))) []])
|
||||
(BinOp (Iden [Name Nothing "a"]) [Name Nothing "*"] (NumLit "2"))) []]
|
||||
|
||||
|
||||
|
||||
|
@ -563,10 +565,10 @@ generated always (valueexpr)
|
|||
-}
|
||||
|
||||
|
||||
,(TestStatement ansi2011 "create table t (a int default 0);"
|
||||
,s "create table t (a int default 0);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
|
||||
(Just $ DefaultClause $ NumLit "0") []])
|
||||
(Just $ DefaultClause $ NumLit "0") []]
|
||||
|
||||
|
||||
|
||||
|
@ -597,40 +599,40 @@ generated always (valueexpr)
|
|||
<column name list>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int, unique (a));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
|
||||
,TableConstraintDef Nothing $ TableUniqueConstraint [Name Nothing "a"]
|
||||
])
|
||||
]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int, constraint a_unique unique (a));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
|
||||
,TableConstraintDef (Just [Name Nothing "a_unique"]) $
|
||||
TableUniqueConstraint [Name Nothing "a"]
|
||||
])
|
||||
]
|
||||
|
||||
-- todo: test permutations of column defs and table constraints
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int, b int, unique (a,b));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
|
||||
,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
|
||||
,TableConstraintDef Nothing $
|
||||
TableUniqueConstraint [Name Nothing "a", Name Nothing "b"]
|
||||
])
|
||||
]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int, b int, primary key (a,b));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
[TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
|
||||
,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
|
||||
,TableConstraintDef Nothing $
|
||||
TablePrimaryKeyConstraint [Name Nothing "a", Name Nothing "b"]
|
||||
])
|
||||
]
|
||||
|
||||
|
||||
{-
|
||||
|
@ -649,7 +651,7 @@ defintely skip
|
|||
-}
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int, b int,\n\
|
||||
\ foreign key (a,b) references u(c,d) match full on update cascade on delete restrict );"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
|
@ -661,9 +663,9 @@ defintely skip
|
|||
[Name Nothing "u"]
|
||||
(Just [Name Nothing "c", Name Nothing "d"])
|
||||
MatchFull RefCascade RefRestrict
|
||||
])
|
||||
]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int,\n\
|
||||
\ constraint tfku1 foreign key (a) references u);"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
|
@ -674,9 +676,9 @@ defintely skip
|
|||
[Name Nothing "u"]
|
||||
Nothing DefaultReferenceMatch
|
||||
DefaultReferentialAction DefaultReferentialAction
|
||||
])
|
||||
]
|
||||
|
||||
,(TestStatement ansi2011 { diNonCommaSeparatedConstraints = True }
|
||||
,testStatement ansi2011{ diNonCommaSeparatedConstraints = True }
|
||||
"create table t (a int, b int,\n\
|
||||
\ foreign key (a) references u(c)\n\
|
||||
\ foreign key (b) references v(d));"
|
||||
|
@ -697,7 +699,7 @@ defintely skip
|
|||
(Just [Name Nothing "d"])
|
||||
DefaultReferenceMatch
|
||||
DefaultReferentialAction DefaultReferentialAction
|
||||
])
|
||||
]
|
||||
|
||||
|
||||
{-
|
||||
|
@ -755,7 +757,7 @@ defintely skip
|
|||
CHECK <left paren> <search condition> <right paren>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int, b int, \n\
|
||||
\ check (a > b));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
|
@ -764,10 +766,10 @@ defintely skip
|
|||
,TableConstraintDef Nothing $
|
||||
TableCheckConstraint
|
||||
(BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (Iden [Name Nothing "b"]))
|
||||
])
|
||||
]
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create table t (a int, b int, \n\
|
||||
\ constraint agtb check (a > b));"
|
||||
$ CreateTable [Name Nothing "t"]
|
||||
|
@ -776,7 +778,7 @@ defintely skip
|
|||
,TableConstraintDef (Just [Name Nothing "agtb"]) $
|
||||
TableCheckConstraint
|
||||
(BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (Iden [Name Nothing "b"]))
|
||||
])
|
||||
]
|
||||
|
||||
|
||||
{-
|
||||
|
@ -810,11 +812,10 @@ alter table t add a int
|
|||
alter table t add a int unique not null check (a>0)
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t add column a int"
|
||||
$ AlterTable [Name Nothing "t"] $ AddColumnDef
|
||||
$ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
|
||||
)
|
||||
|
||||
{-
|
||||
todo: more add column
|
||||
|
@ -844,10 +845,10 @@ todo: more add column
|
|||
-}
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t alter column c set default 0"
|
||||
$ AlterTable [Name Nothing "t"] $ AlterColumnSetDefault (Name Nothing "c")
|
||||
$ NumLit "0")
|
||||
$ NumLit "0"
|
||||
|
||||
{-
|
||||
11.14 <drop column default clause>
|
||||
|
@ -856,9 +857,9 @@ todo: more add column
|
|||
DROP DEFAULT
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t alter column c drop default"
|
||||
$ AlterTable [Name Nothing "t"] $ AlterColumnDropDefault (Name Nothing "c"))
|
||||
$ AlterTable [Name Nothing "t"] $ AlterColumnDropDefault (Name Nothing "c")
|
||||
|
||||
|
||||
{-
|
||||
|
@ -868,9 +869,9 @@ todo: more add column
|
|||
SET NOT NULL
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t alter column c set not null"
|
||||
$ AlterTable [Name Nothing "t"] $ AlterColumnSetNotNull (Name Nothing "c"))
|
||||
$ AlterTable [Name Nothing "t"] $ AlterColumnSetNotNull (Name Nothing "c")
|
||||
|
||||
{-
|
||||
11.16 <drop column not null clause>
|
||||
|
@ -879,9 +880,9 @@ todo: more add column
|
|||
DROP NOT NULL
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t alter column c drop not null"
|
||||
$ AlterTable [Name Nothing "t"] $ AlterColumnDropNotNull (Name Nothing "c"))
|
||||
$ AlterTable [Name Nothing "t"] $ AlterColumnDropNotNull (Name Nothing "c")
|
||||
|
||||
{-
|
||||
11.17 <add column scope clause>
|
||||
|
@ -900,10 +901,10 @@ todo: more add column
|
|||
SET DATA TYPE <data type>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t alter column c set data type int;"
|
||||
$ AlterTable [Name Nothing "t"] $
|
||||
AlterColumnSetDataType (Name Nothing "c") (TypeName [Name Nothing "int"]))
|
||||
AlterColumnSetDataType (Name Nothing "c") (TypeName [Name Nothing "int"])
|
||||
|
||||
|
||||
|
||||
|
@ -1001,20 +1002,20 @@ included in the generated plan above
|
|||
DROP [ COLUMN ] <column name> <drop behavior>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t drop column c"
|
||||
$ AlterTable [Name Nothing "t"] $
|
||||
DropColumn (Name Nothing "c") DefaultDropBehaviour)
|
||||
DropColumn (Name Nothing "c") DefaultDropBehaviour
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t drop c cascade"
|
||||
$ AlterTable [Name Nothing "t"] $
|
||||
DropColumn (Name Nothing "c") Cascade)
|
||||
DropColumn (Name Nothing "c") Cascade
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t drop c restrict"
|
||||
$ AlterTable [Name Nothing "t"] $
|
||||
DropColumn (Name Nothing "c") Restrict)
|
||||
DropColumn (Name Nothing "c") Restrict
|
||||
|
||||
|
||||
|
||||
|
@ -1025,17 +1026,17 @@ included in the generated plan above
|
|||
ADD <table constraint definition>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t add constraint c unique (a,b)"
|
||||
$ AlterTable [Name Nothing "t"] $
|
||||
AddTableConstraintDef (Just [Name Nothing "c"])
|
||||
$ TableUniqueConstraint [Name Nothing "a", Name Nothing "b"])
|
||||
$ TableUniqueConstraint [Name Nothing "a", Name Nothing "b"]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t add unique (a,b)"
|
||||
$ AlterTable [Name Nothing "t"] $
|
||||
AddTableConstraintDef Nothing
|
||||
$ TableUniqueConstraint [Name Nothing "a", Name Nothing "b"])
|
||||
$ TableUniqueConstraint [Name Nothing "a", Name Nothing "b"]
|
||||
|
||||
|
||||
{-
|
||||
|
@ -1051,15 +1052,15 @@ todo
|
|||
DROP CONSTRAINT <constraint name> <drop behavior>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t drop constraint c"
|
||||
$ AlterTable [Name Nothing "t"] $
|
||||
DropTableConstraintDef [Name Nothing "c"] DefaultDropBehaviour)
|
||||
DropTableConstraintDef [Name Nothing "c"] DefaultDropBehaviour
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter table t drop constraint c restrict"
|
||||
$ AlterTable [Name Nothing "t"] $
|
||||
DropTableConstraintDef [Name Nothing "c"] Restrict)
|
||||
DropTableConstraintDef [Name Nothing "c"] Restrict
|
||||
|
||||
{-
|
||||
11.27 <add table period definition>
|
||||
|
@ -1111,13 +1112,13 @@ defintely skip
|
|||
DROP TABLE <table name> <drop behavior>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"drop table t"
|
||||
$ DropTable [Name Nothing "t"] DefaultDropBehaviour)
|
||||
$ DropTable [Name Nothing "t"] DefaultDropBehaviour
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"drop table t restrict"
|
||||
$ DropTable [Name Nothing "t"] Restrict)
|
||||
$ DropTable [Name Nothing "t"] Restrict
|
||||
|
||||
|
||||
{-
|
||||
|
@ -1159,51 +1160,51 @@ defintely skip
|
|||
<column name list>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create view v as select * from t"
|
||||
$ CreateView False [Name Nothing "v"] Nothing (toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star, Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
}) Nothing)
|
||||
}) Nothing
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create recursive view v as select * from t"
|
||||
$ CreateView True [Name Nothing "v"] Nothing (toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star, Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
}) Nothing)
|
||||
}) Nothing
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create view v(a,b) as select * from t"
|
||||
$ CreateView False [Name Nothing "v"] (Just [Name Nothing "a", Name Nothing "b"])
|
||||
(toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star, Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
}) Nothing)
|
||||
}) Nothing
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create view v as select * from t with check option"
|
||||
$ CreateView False [Name Nothing "v"] Nothing (toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star, Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
}) (Just DefaultCheckOption))
|
||||
}) (Just DefaultCheckOption)
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create view v as select * from t with cascaded check option"
|
||||
$ CreateView False [Name Nothing "v"] Nothing (toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star, Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
}) (Just CascadedCheckOption))
|
||||
}) (Just CascadedCheckOption)
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create view v as select * from t with local check option"
|
||||
$ CreateView False [Name Nothing "v"] Nothing
|
||||
(toQueryExpr $ makeSelect
|
||||
{msSelectList = [(Star, Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t"]]
|
||||
}) (Just LocalCheckOption))
|
||||
}) (Just LocalCheckOption)
|
||||
|
||||
|
||||
{-
|
||||
|
@ -1214,13 +1215,13 @@ defintely skip
|
|||
-}
|
||||
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"drop view v"
|
||||
$ DropView [Name Nothing "v"] DefaultDropBehaviour)
|
||||
$ DropView [Name Nothing "v"] DefaultDropBehaviour
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"drop view v cascade"
|
||||
$ DropView [Name Nothing "v"] Cascade)
|
||||
$ DropView [Name Nothing "v"] Cascade
|
||||
|
||||
|
||||
{-
|
||||
|
@ -1237,37 +1238,37 @@ defintely skip
|
|||
<constraint characteristics> ]
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create domain my_int int"
|
||||
$ CreateDomain [Name Nothing "my_int"]
|
||||
(TypeName [Name Nothing "int"])
|
||||
Nothing [])
|
||||
Nothing []
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create domain my_int as int"
|
||||
$ CreateDomain [Name Nothing "my_int"]
|
||||
(TypeName [Name Nothing "int"])
|
||||
Nothing [])
|
||||
Nothing []
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create domain my_int int default 0"
|
||||
$ CreateDomain [Name Nothing "my_int"]
|
||||
(TypeName [Name Nothing "int"])
|
||||
(Just (NumLit "0")) [])
|
||||
(Just (NumLit "0")) []
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create domain my_int int check (value > 5)"
|
||||
$ CreateDomain [Name Nothing "my_int"]
|
||||
(TypeName [Name Nothing "int"])
|
||||
Nothing [(Nothing
|
||||
,BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "5"))])
|
||||
,BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "5"))]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create domain my_int int constraint gt5 check (value > 5)"
|
||||
$ CreateDomain [Name Nothing "my_int"]
|
||||
(TypeName [Name Nothing "int"])
|
||||
Nothing [(Just [Name Nothing "gt5"]
|
||||
,BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "5"))])
|
||||
,BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "5"))]
|
||||
|
||||
|
||||
|
||||
|
@ -1289,10 +1290,10 @@ defintely skip
|
|||
SET <default clause>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter domain my_int set default 0"
|
||||
$ AlterDomain [Name Nothing "my_int"]
|
||||
$ ADSetDefault $ NumLit "0")
|
||||
$ ADSetDefault $ NumLit "0"
|
||||
|
||||
|
||||
{-
|
||||
|
@ -1302,10 +1303,10 @@ defintely skip
|
|||
DROP DEFAULT
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter domain my_int drop default"
|
||||
$ AlterDomain [Name Nothing "my_int"]
|
||||
$ ADDropDefault)
|
||||
$ ADDropDefault
|
||||
|
||||
|
||||
{-
|
||||
|
@ -1315,17 +1316,17 @@ defintely skip
|
|||
ADD <domain constraint>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter domain my_int add check (value > 6)"
|
||||
$ AlterDomain [Name Nothing "my_int"]
|
||||
$ ADAddConstraint Nothing
|
||||
$ BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "6"))
|
||||
$ BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "6")
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter domain my_int add constraint gt6 check (value > 6)"
|
||||
$ AlterDomain [Name Nothing "my_int"]
|
||||
$ ADAddConstraint (Just [Name Nothing "gt6"])
|
||||
$ BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "6"))
|
||||
$ BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "6")
|
||||
|
||||
|
||||
{-
|
||||
|
@ -1335,10 +1336,10 @@ defintely skip
|
|||
DROP CONSTRAINT <constraint name>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter domain my_int drop constraint gt6"
|
||||
$ AlterDomain [Name Nothing "my_int"]
|
||||
$ ADDropConstraint [Name Nothing "gt6"])
|
||||
$ ADDropConstraint [Name Nothing "gt6"]
|
||||
|
||||
{-
|
||||
11.40 <drop domain statement>
|
||||
|
@ -1347,13 +1348,13 @@ defintely skip
|
|||
DROP DOMAIN <domain name> <drop behavior>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"drop domain my_int"
|
||||
$ DropDomain [Name Nothing "my_int"] DefaultDropBehaviour)
|
||||
$ DropDomain [Name Nothing "my_int"] DefaultDropBehaviour
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"drop domain my_int cascade"
|
||||
$ DropDomain [Name Nothing "my_int"] Cascade)
|
||||
$ DropDomain [Name Nothing "my_int"] Cascade
|
||||
|
||||
|
||||
|
||||
|
@ -1425,7 +1426,7 @@ defintely skip
|
|||
[ <constraint characteristics> ]
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create assertion t1_not_empty CHECK ((select count(*) from t1) > 0);"
|
||||
$ CreateAssertion [Name Nothing "t1_not_empty"]
|
||||
$ BinOp (SubQueryExpr SqSq $
|
||||
|
@ -1433,7 +1434,7 @@ defintely skip
|
|||
{msSelectList = [(App [Name Nothing "count"] [Star],Nothing)]
|
||||
,msFrom = [TRSimple [Name Nothing "t1"]]
|
||||
})
|
||||
[Name Nothing ">"] (NumLit "0"))
|
||||
[Name Nothing ">"] (NumLit "0")
|
||||
|
||||
{-
|
||||
11.48 <drop assertion statement>
|
||||
|
@ -1442,13 +1443,13 @@ defintely skip
|
|||
DROP ASSERTION <constraint name> [ <drop behavior> ]
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"drop assertion t1_not_empty;"
|
||||
$ DropAssertion [Name Nothing "t1_not_empty"] DefaultDropBehaviour)
|
||||
$ DropAssertion [Name Nothing "t1_not_empty"] DefaultDropBehaviour
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"drop assertion t1_not_empty cascade;"
|
||||
$ DropAssertion [Name Nothing "t1_not_empty"] Cascade)
|
||||
$ DropAssertion [Name Nothing "t1_not_empty"] Cascade
|
||||
|
||||
|
||||
{-
|
||||
|
@ -2085,21 +2086,21 @@ defintely skip
|
|||
| NO CYCLE
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create sequence seq"
|
||||
$ CreateSequence [Name Nothing "seq"] [])
|
||||
$ CreateSequence [Name Nothing "seq"] []
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create sequence seq as bigint"
|
||||
$ CreateSequence [Name Nothing "seq"]
|
||||
[SGODataType $ TypeName [Name Nothing "bigint"]])
|
||||
[SGODataType $ TypeName [Name Nothing "bigint"]]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"create sequence seq as bigint start with 5"
|
||||
$ CreateSequence [Name Nothing "seq"]
|
||||
[SGOStartWith 5
|
||||
,SGODataType $ TypeName [Name Nothing "bigint"]
|
||||
])
|
||||
]
|
||||
|
||||
|
||||
{-
|
||||
|
@ -2122,21 +2123,21 @@ defintely skip
|
|||
<signed numeric literal>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter sequence seq restart"
|
||||
$ AlterSequence [Name Nothing "seq"]
|
||||
[SGORestart Nothing])
|
||||
[SGORestart Nothing]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter sequence seq restart with 5"
|
||||
$ AlterSequence [Name Nothing "seq"]
|
||||
[SGORestart $ Just 5])
|
||||
[SGORestart $ Just 5]
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"alter sequence seq restart with 5 increment by 5"
|
||||
$ AlterSequence [Name Nothing "seq"]
|
||||
[SGORestart $ Just 5
|
||||
,SGOIncrementBy 5])
|
||||
,SGOIncrementBy 5]
|
||||
|
||||
|
||||
{-
|
||||
|
@ -2146,13 +2147,16 @@ defintely skip
|
|||
DROP SEQUENCE <sequence generator name> <drop behavior>
|
||||
-}
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"drop sequence seq"
|
||||
$ DropSequence [Name Nothing "seq"] DefaultDropBehaviour)
|
||||
$ DropSequence [Name Nothing "seq"] DefaultDropBehaviour
|
||||
|
||||
,(TestStatement ansi2011
|
||||
,s
|
||||
"drop sequence seq restrict"
|
||||
$ DropSequence [Name Nothing "seq"] Restrict)
|
||||
$ DropSequence [Name Nothing "seq"] Restrict
|
||||
|
||||
|
||||
]
|
||||
|
||||
s :: HasCallStack => Text -> Statement -> TestItem
|
||||
s src ast = testStatement ansi2011 src ast
|
||||
|
|
|
@ -6,6 +6,9 @@ module Language.SQL.SimpleSQL.ScalarExprs (scalarExprTests) where
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
|
||||
import Data.Text (Text)
|
||||
|
||||
scalarExprTests :: TestItem
|
||||
scalarExprTests = Group "scalarExprTests"
|
||||
|
@ -25,101 +28,108 @@ scalarExprTests = Group "scalarExprTests"
|
|||
,functionsWithReservedNames
|
||||
]
|
||||
|
||||
t :: HasCallStack => Text -> ScalarExpr -> TestItem
|
||||
t src ast = testScalarExpr ansi2011 src ast
|
||||
|
||||
td :: HasCallStack => Dialect -> Text -> ScalarExpr -> TestItem
|
||||
td d src ast = testScalarExpr d src ast
|
||||
|
||||
|
||||
|
||||
literals :: TestItem
|
||||
literals = Group "literals" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("3", NumLit "3")
|
||||
,("3.", NumLit "3.")
|
||||
,("3.3", NumLit "3.3")
|
||||
,(".3", NumLit ".3")
|
||||
,("3.e3", NumLit "3.e3")
|
||||
,("3.3e3", NumLit "3.3e3")
|
||||
,(".3e3", NumLit ".3e3")
|
||||
,("3e3", NumLit "3e3")
|
||||
,("3e+3", NumLit "3e+3")
|
||||
,("3e-3", NumLit "3e-3")
|
||||
,("'string'", StringLit "'" "'" "string")
|
||||
,("'string with a '' quote'", StringLit "'" "'" "string with a '' quote")
|
||||
,("'1'", StringLit "'" "'" "1")
|
||||
,("interval '3' day"
|
||||
,IntervalLit Nothing "3" (Itf "day" Nothing) Nothing)
|
||||
,("interval '3' day (3)"
|
||||
,IntervalLit Nothing "3" (Itf "day" $ Just (3,Nothing)) Nothing)
|
||||
,("interval '3 weeks'", TypedLit (TypeName [Name Nothing "interval"]) "3 weeks")
|
||||
literals = Group "literals"
|
||||
[t "3" $ NumLit "3"
|
||||
,t "3." $ NumLit "3."
|
||||
,t "3.3" $ NumLit "3.3"
|
||||
,t ".3" $ NumLit ".3"
|
||||
,t "3.e3" $ NumLit "3.e3"
|
||||
,t "3.3e3" $ NumLit "3.3e3"
|
||||
,t ".3e3" $ NumLit ".3e3"
|
||||
,t "3e3" $ NumLit "3e3"
|
||||
,t "3e+3" $ NumLit "3e+3"
|
||||
,t "3e-3" $ NumLit "3e-3"
|
||||
,t "'string'" $ StringLit "'" "'" "string"
|
||||
,t "'string with a '' quote'" $ StringLit "'" "'" "string with a '' quote"
|
||||
,t "'1'" $ StringLit "'" "'" "1"
|
||||
,t "interval '3' day"
|
||||
$ IntervalLit Nothing "3" (Itf "day" Nothing) Nothing
|
||||
,t "interval '3' day (3)"
|
||||
$ IntervalLit Nothing "3" (Itf "day" $ Just (3,Nothing)) Nothing
|
||||
,t "interval '3 weeks'" $ TypedLit (TypeName [Name Nothing "interval"]) "3 weeks"
|
||||
]
|
||||
|
||||
identifiers :: TestItem
|
||||
identifiers = Group "identifiers" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("iden1", Iden [Name Nothing "iden1"])
|
||||
identifiers = Group "identifiers"
|
||||
[t "iden1" $ Iden [Name Nothing "iden1"]
|
||||
--,("t.a", Iden2 "t" "a")
|
||||
,("\"quoted identifier\"", Iden [Name (Just ("\"","\"")) "quoted identifier"])
|
||||
,("\"from\"", Iden [Name (Just ("\"","\"")) "from"])
|
||||
,t "\"quoted identifier\"" $ Iden [Name (Just ("\"","\"")) "quoted identifier"]
|
||||
,t "\"from\"" $ Iden [Name (Just ("\"","\"")) "from"]
|
||||
]
|
||||
|
||||
star :: TestItem
|
||||
star = Group "star" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("*", Star)
|
||||
star = Group "star"
|
||||
[t "*" Star
|
||||
--,("t.*", Star2 "t")
|
||||
--,("ROW(t.*,42)", App "ROW" [Star2 "t", NumLit "42"])
|
||||
]
|
||||
|
||||
parameter :: TestItem
|
||||
parameter = Group "parameter"
|
||||
[TestScalarExpr ansi2011 "?" Parameter
|
||||
,TestScalarExpr postgres "$13" $ PositionalArg 13]
|
||||
|
||||
[td ansi2011 "?" Parameter
|
||||
,td postgres "$13" $ PositionalArg 13]
|
||||
|
||||
dots :: TestItem
|
||||
dots = Group "dot" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("t.a", Iden [Name Nothing "t",Name Nothing "a"])
|
||||
,("t.*", BinOp (Iden [Name Nothing "t"]) [Name Nothing "."] Star)
|
||||
,("a.b.c", Iden [Name Nothing "a",Name Nothing "b",Name Nothing "c"])
|
||||
,("ROW(t.*,42)", App [Name Nothing "ROW"] [BinOp (Iden [Name Nothing "t"]) [Name Nothing "."] Star, NumLit "42"])
|
||||
dots = Group "dot"
|
||||
[t "t.a" $ Iden [Name Nothing "t",Name Nothing "a"]
|
||||
,t "t.*" $ BinOp (Iden [Name Nothing "t"]) [Name Nothing "."] Star
|
||||
,t "a.b.c" $ Iden [Name Nothing "a",Name Nothing "b",Name Nothing "c"]
|
||||
,t "ROW(t.*,42)"
|
||||
$ App [Name Nothing "ROW"] [BinOp (Iden [Name Nothing "t"]) [Name Nothing "."] Star, NumLit "42"]
|
||||
]
|
||||
|
||||
app :: TestItem
|
||||
app = Group "app" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("f()", App [Name Nothing "f"] [])
|
||||
,("f(a)", App [Name Nothing "f"] [Iden [Name Nothing "a"]])
|
||||
,("f(a,b)", App [Name Nothing "f"] [Iden [Name Nothing "a"], Iden [Name Nothing "b"]])
|
||||
app = Group "app"
|
||||
[t "f()" $ App [Name Nothing "f"] []
|
||||
,t "f(a)" $ App [Name Nothing "f"] [Iden [Name Nothing "a"]]
|
||||
,t "f(a,b)" $ App [Name Nothing "f"] [Iden [Name Nothing "a"], Iden [Name Nothing "b"]]
|
||||
]
|
||||
|
||||
caseexp :: TestItem
|
||||
caseexp = Group "caseexp" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("case a when 1 then 2 end"
|
||||
,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"]
|
||||
,NumLit "2")] Nothing)
|
||||
caseexp = Group "caseexp"
|
||||
[t "case a when 1 then 2 end"
|
||||
$ Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"]
|
||||
,NumLit "2")] Nothing
|
||||
|
||||
,("case a when 1 then 2 when 3 then 4 end"
|
||||
,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"], NumLit "2")
|
||||
,([NumLit "3"], NumLit "4")] Nothing)
|
||||
,t "case a when 1 then 2 when 3 then 4 end"
|
||||
$ Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"], NumLit "2")
|
||||
,([NumLit "3"], NumLit "4")] Nothing
|
||||
|
||||
,("case a when 1 then 2 when 3 then 4 else 5 end"
|
||||
,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"], NumLit "2")
|
||||
,t "case a when 1 then 2 when 3 then 4 else 5 end"
|
||||
$ Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"], NumLit "2")
|
||||
,([NumLit "3"], NumLit "4")]
|
||||
(Just $ NumLit "5"))
|
||||
(Just $ NumLit "5")
|
||||
|
||||
,("case when a=1 then 2 when a=3 then 4 else 5 end"
|
||||
,Case Nothing [([BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "1")], NumLit "2")
|
||||
,t "case when a=1 then 2 when a=3 then 4 else 5 end"
|
||||
$ Case Nothing [([BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "1")], NumLit "2")
|
||||
,([BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "3")], NumLit "4")]
|
||||
(Just $ NumLit "5"))
|
||||
(Just $ NumLit "5")
|
||||
|
||||
,("case a when 1,2 then 10 when 3,4 then 20 end"
|
||||
,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1",NumLit "2"]
|
||||
,t "case a when 1,2 then 10 when 3,4 then 20 end"
|
||||
$ Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1",NumLit "2"]
|
||||
,NumLit "10")
|
||||
,([NumLit "3",NumLit "4"]
|
||||
,NumLit "20")]
|
||||
Nothing)
|
||||
|
||||
Nothing
|
||||
]
|
||||
|
||||
convertfun :: TestItem
|
||||
convertfun = Group "convert" $ map (uncurry (TestScalarExpr sqlserver))
|
||||
[("CONVERT(varchar, 25.65)"
|
||||
,Convert (TypeName [Name Nothing "varchar"]) (NumLit "25.65") Nothing)
|
||||
,("CONVERT(datetime, '2017-08-25')"
|
||||
,Convert (TypeName [Name Nothing "datetime"]) (StringLit "'" "'" "2017-08-25") Nothing)
|
||||
,("CONVERT(varchar, '2017-08-25', 101)"
|
||||
,Convert (TypeName [Name Nothing "varchar"]) (StringLit "'" "'" "2017-08-25") (Just 101))
|
||||
convertfun = Group "convert"
|
||||
[td sqlserver "CONVERT(varchar, 25.65)"
|
||||
$ Convert (TypeName [Name Nothing "varchar"]) (NumLit "25.65") Nothing
|
||||
,td sqlserver "CONVERT(datetime, '2017-08-25')"
|
||||
$ Convert (TypeName [Name Nothing "datetime"]) (StringLit "'" "'" "2017-08-25") Nothing
|
||||
,td sqlserver "CONVERT(varchar, '2017-08-25', 101)"
|
||||
$ Convert (TypeName [Name Nothing "varchar"]) (StringLit "'" "'" "2017-08-25") (Just 101)
|
||||
]
|
||||
|
||||
operators :: TestItem
|
||||
|
@ -130,70 +140,69 @@ operators = Group "operators"
|
|||
,miscOps]
|
||||
|
||||
binaryOperators :: TestItem
|
||||
binaryOperators = Group "binaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("a + b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"] (Iden [Name Nothing "b"]))
|
||||
binaryOperators = Group "binaryOperators"
|
||||
[t "a + b" $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"] (Iden [Name Nothing "b"])
|
||||
-- sanity check fixities
|
||||
-- todo: add more fixity checking
|
||||
|
||||
,("a + b * c"
|
||||
,BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"]
|
||||
(BinOp (Iden [Name Nothing "b"]) [Name Nothing "*"] (Iden [Name Nothing "c"])))
|
||||
,t "a + b * c"
|
||||
$ BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"]
|
||||
(BinOp (Iden [Name Nothing "b"]) [Name Nothing "*"] (Iden [Name Nothing "c"]))
|
||||
|
||||
,("a * b + c"
|
||||
,BinOp (BinOp (Iden [Name Nothing "a"]) [Name Nothing "*"] (Iden [Name Nothing "b"]))
|
||||
[Name Nothing "+"] (Iden [Name Nothing "c"]))
|
||||
,t "a * b + c"
|
||||
$ BinOp (BinOp (Iden [Name Nothing "a"]) [Name Nothing "*"] (Iden [Name Nothing "b"]))
|
||||
[Name Nothing "+"] (Iden [Name Nothing "c"])
|
||||
]
|
||||
|
||||
unaryOperators :: TestItem
|
||||
unaryOperators = Group "unaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("not a", PrefixOp [Name Nothing "not"] $ Iden [Name Nothing "a"])
|
||||
,("not not a", PrefixOp [Name Nothing "not"] $ PrefixOp [Name Nothing "not"] $ Iden [Name Nothing "a"])
|
||||
,("+a", PrefixOp [Name Nothing "+"] $ Iden [Name Nothing "a"])
|
||||
,("-a", PrefixOp [Name Nothing "-"] $ Iden [Name Nothing "a"])
|
||||
unaryOperators = Group "unaryOperators"
|
||||
[t "not a" $ PrefixOp [Name Nothing "not"] $ Iden [Name Nothing "a"]
|
||||
,t "not not a" $ PrefixOp [Name Nothing "not"] $ PrefixOp [Name Nothing "not"] $ Iden [Name Nothing "a"]
|
||||
,t "+a" $ PrefixOp [Name Nothing "+"] $ Iden [Name Nothing "a"]
|
||||
,t "-a" $ PrefixOp [Name Nothing "-"] $ Iden [Name Nothing "a"]
|
||||
]
|
||||
|
||||
|
||||
casts :: TestItem
|
||||
casts = Group "operators" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("cast('1' as int)"
|
||||
,Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "int"])
|
||||
casts = Group "operators"
|
||||
[t "cast('1' as int)"
|
||||
$ Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "int"]
|
||||
|
||||
,("int '3'"
|
||||
,TypedLit (TypeName [Name Nothing "int"]) "3")
|
||||
,t "int '3'"
|
||||
$ TypedLit (TypeName [Name Nothing "int"]) "3"
|
||||
|
||||
,("cast('1' as double precision)"
|
||||
,Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "double precision"])
|
||||
,t "cast('1' as double precision)"
|
||||
$ Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "double precision"]
|
||||
|
||||
,("cast('1' as float(8))"
|
||||
,Cast (StringLit "'" "'" "1") $ PrecTypeName [Name Nothing "float"] 8)
|
||||
,t "cast('1' as float(8))"
|
||||
$ Cast (StringLit "'" "'" "1") $ PrecTypeName [Name Nothing "float"] 8
|
||||
|
||||
,("cast('1' as decimal(15,2))"
|
||||
,Cast (StringLit "'" "'" "1") $ PrecScaleTypeName [Name Nothing "decimal"] 15 2)
|
||||
,t "cast('1' as decimal(15,2))"
|
||||
$ Cast (StringLit "'" "'" "1") $ PrecScaleTypeName [Name Nothing "decimal"] 15 2
|
||||
|
||||
|
||||
,("double precision '3'"
|
||||
,TypedLit (TypeName [Name Nothing "double precision"]) "3")
|
||||
,t "double precision '3'"
|
||||
$ TypedLit (TypeName [Name Nothing "double precision"]) "3"
|
||||
]
|
||||
|
||||
subqueries :: TestItem
|
||||
subqueries = Group "unaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("exists (select a from t)", SubQueryExpr SqExists ms)
|
||||
,("(select a from t)", SubQueryExpr SqSq ms)
|
||||
subqueries = Group "unaryOperators"
|
||||
[t "exists (select a from t)" $ SubQueryExpr SqExists ms
|
||||
,t "(select a from t)" $ SubQueryExpr SqSq ms
|
||||
|
||||
,("a in (select a from t)"
|
||||
,In True (Iden [Name Nothing "a"]) (InQueryExpr ms))
|
||||
,t "a in (select a from t)"
|
||||
$ In True (Iden [Name Nothing "a"]) (InQueryExpr ms)
|
||||
|
||||
,("a not in (select a from t)"
|
||||
,In False (Iden [Name Nothing "a"]) (InQueryExpr ms))
|
||||
,t "a not in (select a from t)"
|
||||
$ In False (Iden [Name Nothing "a"]) (InQueryExpr ms)
|
||||
|
||||
,("a > all (select a from t)"
|
||||
,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing ">"] CPAll ms)
|
||||
,t "a > all (select a from t)"
|
||||
$ QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing ">"] CPAll ms
|
||||
|
||||
,("a = some (select a from t)"
|
||||
,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "="] CPSome ms)
|
||||
,t "a = some (select a from t)"
|
||||
$ QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "="] CPSome ms
|
||||
|
||||
,("a <= any (select a from t)"
|
||||
,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "<="] CPAny ms)
|
||||
,t "a <= any (select a from t)"
|
||||
$ QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "<="] CPAny ms
|
||||
]
|
||||
where
|
||||
ms = toQueryExpr $ makeSelect
|
||||
|
@ -202,94 +211,93 @@ subqueries = Group "unaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
|
|||
}
|
||||
|
||||
miscOps :: TestItem
|
||||
miscOps = Group "unaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("a in (1,2,3)"
|
||||
,In True (Iden [Name Nothing "a"]) $ InList $ map NumLit ["1","2","3"])
|
||||
miscOps = Group "unaryOperators"
|
||||
[t "a in (1,2,3)"
|
||||
$ In True (Iden [Name Nothing "a"]) $ InList $ map NumLit ["1","2","3"]
|
||||
|
||||
,("a is null", PostfixOp [Name Nothing "is null"] (Iden [Name Nothing "a"]))
|
||||
,("a is not null", PostfixOp [Name Nothing "is not null"] (Iden [Name Nothing "a"]))
|
||||
,("a is true", PostfixOp [Name Nothing "is true"] (Iden [Name Nothing "a"]))
|
||||
,("a is not true", PostfixOp [Name Nothing "is not true"] (Iden [Name Nothing "a"]))
|
||||
,("a is false", PostfixOp [Name Nothing "is false"] (Iden [Name Nothing "a"]))
|
||||
,("a is not false", PostfixOp [Name Nothing "is not false"] (Iden [Name Nothing "a"]))
|
||||
,("a is unknown", PostfixOp [Name Nothing "is unknown"] (Iden [Name Nothing "a"]))
|
||||
,("a is not unknown", PostfixOp [Name Nothing "is not unknown"] (Iden [Name Nothing "a"]))
|
||||
,("a is distinct from b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "is distinct from"] (Iden [Name Nothing "b"]))
|
||||
,t "a is null" $ PostfixOp [Name Nothing "is null"] (Iden [Name Nothing "a"])
|
||||
,t "a is not null" $ PostfixOp [Name Nothing "is not null"] (Iden [Name Nothing "a"])
|
||||
,t "a is true" $ PostfixOp [Name Nothing "is true"] (Iden [Name Nothing "a"])
|
||||
,t "a is not true" $ PostfixOp [Name Nothing "is not true"] (Iden [Name Nothing "a"])
|
||||
,t "a is false" $ PostfixOp [Name Nothing "is false"] (Iden [Name Nothing "a"])
|
||||
,t "a is not false" $ PostfixOp [Name Nothing "is not false"] (Iden [Name Nothing "a"])
|
||||
,t "a is unknown" $ PostfixOp [Name Nothing "is unknown"] (Iden [Name Nothing "a"])
|
||||
,t "a is not unknown" $ PostfixOp [Name Nothing "is not unknown"] (Iden [Name Nothing "a"])
|
||||
,t "a is distinct from b" $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "is distinct from"] (Iden [Name Nothing "b"])
|
||||
|
||||
,("a is not distinct from b"
|
||||
,BinOp (Iden [Name Nothing "a"]) [Name Nothing "is not distinct from"] (Iden [Name Nothing "b"]))
|
||||
,t "a is not distinct from b"
|
||||
$ BinOp (Iden [Name Nothing "a"]) [Name Nothing "is not distinct from"] (Iden [Name Nothing "b"])
|
||||
|
||||
,("a like b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "like"] (Iden [Name Nothing "b"]))
|
||||
,("a not like b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "not like"] (Iden [Name Nothing "b"]))
|
||||
,("a is similar to b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "is similar to"] (Iden [Name Nothing "b"]))
|
||||
,t "a like b" $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "like"] (Iden [Name Nothing "b"])
|
||||
,t "a not like b" $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "not like"] (Iden [Name Nothing "b"])
|
||||
,t "a is similar to b"$ BinOp (Iden [Name Nothing "a"]) [Name Nothing "is similar to"] (Iden [Name Nothing "b"])
|
||||
|
||||
,("a is not similar to b"
|
||||
,BinOp (Iden [Name Nothing "a"]) [Name Nothing "is not similar to"] (Iden [Name Nothing "b"]))
|
||||
|
||||
,("a overlaps b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "overlaps"] (Iden [Name Nothing "b"]))
|
||||
,t "a is not similar to b"
|
||||
$ BinOp (Iden [Name Nothing "a"]) [Name Nothing "is not similar to"] (Iden [Name Nothing "b"])
|
||||
|
||||
,t "a overlaps b" $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "overlaps"] (Iden [Name Nothing "b"])
|
||||
|
||||
-- special operators
|
||||
|
||||
,("a between b and c", SpecialOp [Name Nothing "between"] [Iden [Name Nothing "a"]
|
||||
,t "a between b and c" $ SpecialOp [Name Nothing "between"] [Iden [Name Nothing "a"]
|
||||
,Iden [Name Nothing "b"]
|
||||
,Iden [Name Nothing "c"]])
|
||||
,Iden [Name Nothing "c"]]
|
||||
|
||||
,("a not between b and c", SpecialOp [Name Nothing "not between"] [Iden [Name Nothing "a"]
|
||||
,t "a not between b and c" $ SpecialOp [Name Nothing "not between"] [Iden [Name Nothing "a"]
|
||||
,Iden [Name Nothing "b"]
|
||||
,Iden [Name Nothing "c"]])
|
||||
,("(1,2)"
|
||||
,SpecialOp [Name Nothing "rowctor"] [NumLit "1", NumLit "2"])
|
||||
,Iden [Name Nothing "c"]]
|
||||
,t "(1,2)"
|
||||
$ SpecialOp [Name Nothing "rowctor"] [NumLit "1", NumLit "2"]
|
||||
|
||||
|
||||
-- keyword special operators
|
||||
|
||||
,("extract(day from t)"
|
||||
, SpecialOpK [Name Nothing "extract"] (Just $ Iden [Name Nothing "day"]) [("from", Iden [Name Nothing "t"])])
|
||||
,t "extract(day from t)"
|
||||
$ SpecialOpK [Name Nothing "extract"] (Just $ Iden [Name Nothing "day"]) [("from", Iden [Name Nothing "t"])]
|
||||
|
||||
,("substring(x from 1 for 2)"
|
||||
,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("from", NumLit "1")
|
||||
,("for", NumLit "2")])
|
||||
,t "substring(x from 1 for 2)"
|
||||
$ SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("from", NumLit "1")
|
||||
,("for", NumLit "2")]
|
||||
|
||||
,("substring(x from 1)"
|
||||
,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("from", NumLit "1")])
|
||||
,t "substring(x from 1)"
|
||||
$ SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("from", NumLit "1")]
|
||||
|
||||
,("substring(x for 2)"
|
||||
,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("for", NumLit "2")])
|
||||
,t "substring(x for 2)"
|
||||
$ SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("for", NumLit "2")]
|
||||
|
||||
,("substring(x from 1 for 2 collate C)"
|
||||
,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"])
|
||||
,t "substring(x from 1 for 2 collate C)"
|
||||
$ SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"])
|
||||
[("from", NumLit "1")
|
||||
,("for", Collate (NumLit "2") [Name Nothing "C"])])
|
||||
,("for", Collate (NumLit "2") [Name Nothing "C"])]
|
||||
|
||||
-- this doesn't work because of a overlap in the 'in' parser
|
||||
|
||||
,("POSITION( string1 IN string2 )"
|
||||
,SpecialOpK [Name Nothing "position"] (Just $ Iden [Name Nothing "string1"]) [("in", Iden [Name Nothing "string2"])])
|
||||
,t "POSITION( string1 IN string2 )"
|
||||
$ SpecialOpK [Name Nothing "position"] (Just $ Iden [Name Nothing "string1"]) [("in", Iden [Name Nothing "string2"])]
|
||||
|
||||
,("CONVERT(char_value USING conversion_char_name)"
|
||||
,SpecialOpK [Name Nothing "convert"] (Just $ Iden [Name Nothing "char_value"])
|
||||
[("using", Iden [Name Nothing "conversion_char_name"])])
|
||||
,t "CONVERT(char_value USING conversion_char_name)"
|
||||
$ SpecialOpK [Name Nothing "convert"] (Just $ Iden [Name Nothing "char_value"])
|
||||
[("using", Iden [Name Nothing "conversion_char_name"])]
|
||||
|
||||
,("TRANSLATE(char_value USING translation_name)"
|
||||
,SpecialOpK [Name Nothing "translate"] (Just $ Iden [Name Nothing "char_value"])
|
||||
[("using", Iden [Name Nothing "translation_name"])])
|
||||
,t "TRANSLATE(char_value USING translation_name)"
|
||||
$ SpecialOpK [Name Nothing "translate"] (Just $ Iden [Name Nothing "char_value"])
|
||||
[("using", Iden [Name Nothing "translation_name"])]
|
||||
|
||||
{-
|
||||
OVERLAY(string PLACING embedded_string FROM start
|
||||
[FOR length])
|
||||
-}
|
||||
|
||||
,("OVERLAY(string PLACING embedded_string FROM start)"
|
||||
,SpecialOpK [Name Nothing "overlay"] (Just $ Iden [Name Nothing "string"])
|
||||
,t "OVERLAY(string PLACING embedded_string FROM start)"
|
||||
$ SpecialOpK [Name Nothing "overlay"] (Just $ Iden [Name Nothing "string"])
|
||||
[("placing", Iden [Name Nothing "embedded_string"])
|
||||
,("from", Iden [Name Nothing "start"])])
|
||||
,("from", Iden [Name Nothing "start"])]
|
||||
|
||||
,("OVERLAY(string PLACING embedded_string FROM start FOR length)"
|
||||
,SpecialOpK [Name Nothing "overlay"] (Just $ Iden [Name Nothing "string"])
|
||||
,t "OVERLAY(string PLACING embedded_string FROM start FOR length)"
|
||||
$ SpecialOpK [Name Nothing "overlay"] (Just $ Iden [Name Nothing "string"])
|
||||
[("placing", Iden [Name Nothing "embedded_string"])
|
||||
,("from", Iden [Name Nothing "start"])
|
||||
,("for", Iden [Name Nothing "length"])])
|
||||
,("for", Iden [Name Nothing "length"])]
|
||||
|
||||
{-
|
||||
TRIM( [ [{LEADING | TRAILING | BOTH}] [removal_char] FROM ]
|
||||
|
@ -299,135 +307,133 @@ target_string
|
|||
|
||||
|
||||
|
||||
,("trim(from target_string)"
|
||||
,SpecialOpK [Name Nothing "trim"] Nothing
|
||||
,t "trim(from target_string)"
|
||||
$ SpecialOpK [Name Nothing "trim"] Nothing
|
||||
[("both", StringLit "'" "'" " ")
|
||||
,("from", Iden [Name Nothing "target_string"])])
|
||||
,("from", Iden [Name Nothing "target_string"])]
|
||||
|
||||
,("trim(leading from target_string)"
|
||||
,SpecialOpK [Name Nothing "trim"] Nothing
|
||||
,t "trim(leading from target_string)"
|
||||
$ SpecialOpK [Name Nothing "trim"] Nothing
|
||||
[("leading", StringLit "'" "'" " ")
|
||||
,("from", Iden [Name Nothing "target_string"])])
|
||||
,("from", Iden [Name Nothing "target_string"])]
|
||||
|
||||
,("trim(trailing from target_string)"
|
||||
,SpecialOpK [Name Nothing "trim"] Nothing
|
||||
,t "trim(trailing from target_string)"
|
||||
$ SpecialOpK [Name Nothing "trim"] Nothing
|
||||
[("trailing", StringLit "'" "'" " ")
|
||||
,("from", Iden [Name Nothing "target_string"])])
|
||||
,("from", Iden [Name Nothing "target_string"])]
|
||||
|
||||
,("trim(both from target_string)"
|
||||
,SpecialOpK [Name Nothing "trim"] Nothing
|
||||
,t "trim(both from target_string)"
|
||||
$ SpecialOpK [Name Nothing "trim"] Nothing
|
||||
[("both", StringLit "'" "'" " ")
|
||||
,("from", Iden [Name Nothing "target_string"])])
|
||||
,("from", Iden [Name Nothing "target_string"])]
|
||||
|
||||
|
||||
,("trim(leading 'x' from target_string)"
|
||||
,SpecialOpK [Name Nothing "trim"] Nothing
|
||||
,t "trim(leading 'x' from target_string)"
|
||||
$ SpecialOpK [Name Nothing "trim"] Nothing
|
||||
[("leading", StringLit "'" "'" "x")
|
||||
,("from", Iden [Name Nothing "target_string"])])
|
||||
,("from", Iden [Name Nothing "target_string"])]
|
||||
|
||||
,("trim(trailing 'y' from target_string)"
|
||||
,SpecialOpK [Name Nothing "trim"] Nothing
|
||||
,t "trim(trailing 'y' from target_string)"
|
||||
$ SpecialOpK [Name Nothing "trim"] Nothing
|
||||
[("trailing", StringLit "'" "'" "y")
|
||||
,("from", Iden [Name Nothing "target_string"])])
|
||||
,("from", Iden [Name Nothing "target_string"])]
|
||||
|
||||
,("trim(both 'z' from target_string collate C)"
|
||||
,SpecialOpK [Name Nothing "trim"] Nothing
|
||||
,t "trim(both 'z' from target_string collate C)"
|
||||
$ SpecialOpK [Name Nothing "trim"] Nothing
|
||||
[("both", StringLit "'" "'" "z")
|
||||
,("from", Collate (Iden [Name Nothing "target_string"]) [Name Nothing "C"])])
|
||||
,("from", Collate (Iden [Name Nothing "target_string"]) [Name Nothing "C"])]
|
||||
|
||||
,("trim(leading from target_string)"
|
||||
,SpecialOpK [Name Nothing "trim"] Nothing
|
||||
,t "trim(leading from target_string)"
|
||||
$ SpecialOpK [Name Nothing "trim"] Nothing
|
||||
[("leading", StringLit "'" "'" " ")
|
||||
,("from", Iden [Name Nothing "target_string"])])
|
||||
|
||||
,("from", Iden [Name Nothing "target_string"])]
|
||||
|
||||
]
|
||||
|
||||
aggregates :: TestItem
|
||||
aggregates = Group "aggregates" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("count(*)",App [Name Nothing "count"] [Star])
|
||||
aggregates = Group "aggregates"
|
||||
[t "count(*)" $ App [Name Nothing "count"] [Star]
|
||||
|
||||
,("sum(a order by a)"
|
||||
,AggregateApp [Name Nothing "sum"] SQDefault [Iden [Name Nothing "a"]]
|
||||
[SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault] Nothing)
|
||||
,t "sum(a order by a)"
|
||||
$ AggregateApp [Name Nothing "sum"] SQDefault [Iden [Name Nothing "a"]]
|
||||
[SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault] Nothing
|
||||
|
||||
,("sum(all a)"
|
||||
,AggregateApp [Name Nothing "sum"] All [Iden [Name Nothing "a"]] [] Nothing)
|
||||
,t "sum(all a)"
|
||||
$ AggregateApp [Name Nothing "sum"] All [Iden [Name Nothing "a"]] [] Nothing
|
||||
|
||||
,("count(distinct a)"
|
||||
,AggregateApp [Name Nothing "count"] Distinct [Iden [Name Nothing "a"]] [] Nothing)
|
||||
,t "count(distinct a)"
|
||||
$ AggregateApp [Name Nothing "count"] Distinct [Iden [Name Nothing "a"]] [] Nothing
|
||||
]
|
||||
|
||||
windowFunctions :: TestItem
|
||||
windowFunctions = Group "windowFunctions" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("max(a) over ()", WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [] [] Nothing)
|
||||
,("count(*) over ()", WindowApp [Name Nothing "count"] [Star] [] [] Nothing)
|
||||
windowFunctions = Group "windowFunctions"
|
||||
[t "max(a) over ()" $ WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [] [] Nothing
|
||||
,t "count(*) over ()" $ WindowApp [Name Nothing "count"] [Star] [] [] Nothing
|
||||
|
||||
,("max(a) over (partition by b)"
|
||||
,WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]] [] Nothing)
|
||||
,t "max(a) over (partition by b)"
|
||||
$ WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]] [] Nothing
|
||||
|
||||
,("max(a) over (partition by b,c)"
|
||||
,WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"],Iden [Name Nothing "c"]] [] Nothing)
|
||||
,t "max(a) over (partition by b,c)"
|
||||
$ WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"],Iden [Name Nothing "c"]] [] Nothing
|
||||
|
||||
,("sum(a) over (order by b)"
|
||||
,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] []
|
||||
[SortSpec (Iden [Name Nothing "b"]) DirDefault NullsOrderDefault] Nothing)
|
||||
,t "sum(a) over (order by b)"
|
||||
$ WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] []
|
||||
[SortSpec (Iden [Name Nothing "b"]) DirDefault NullsOrderDefault] Nothing
|
||||
|
||||
,("sum(a) over (order by b desc,c)"
|
||||
,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] []
|
||||
,t "sum(a) over (order by b desc,c)"
|
||||
$ WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] []
|
||||
[SortSpec (Iden [Name Nothing "b"]) Desc NullsOrderDefault
|
||||
,SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault] Nothing)
|
||||
,SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault] Nothing
|
||||
|
||||
,("sum(a) over (partition by b order by c)"
|
||||
,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
[SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault] Nothing)
|
||||
,t "sum(a) over (partition by b order by c)"
|
||||
$ WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
[SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault] Nothing
|
||||
|
||||
,("sum(a) over (partition by b order by c range unbounded preceding)"
|
||||
,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
,t "sum(a) over (partition by b order by c range unbounded preceding)"
|
||||
$ WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
[SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
|
||||
$ Just $ FrameFrom FrameRange UnboundedPreceding)
|
||||
$ Just $ FrameFrom FrameRange UnboundedPreceding
|
||||
|
||||
,("sum(a) over (partition by b order by c range 5 preceding)"
|
||||
,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
,t "sum(a) over (partition by b order by c range 5 preceding)"
|
||||
$ WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
[SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
|
||||
$ Just $ FrameFrom FrameRange $ Preceding (NumLit "5"))
|
||||
$ Just $ FrameFrom FrameRange $ Preceding (NumLit "5")
|
||||
|
||||
,("sum(a) over (partition by b order by c range current row)"
|
||||
,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
,t "sum(a) over (partition by b order by c range current row)"
|
||||
$ WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
[SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
|
||||
$ Just $ FrameFrom FrameRange Current)
|
||||
$ Just $ FrameFrom FrameRange Current
|
||||
|
||||
,("sum(a) over (partition by b order by c rows 5 following)"
|
||||
,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
,t "sum(a) over (partition by b order by c rows 5 following)"
|
||||
$ WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
[SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
|
||||
$ Just $ FrameFrom FrameRows $ Following (NumLit "5"))
|
||||
$ Just $ FrameFrom FrameRows $ Following (NumLit "5")
|
||||
|
||||
,("sum(a) over (partition by b order by c range unbounded following)"
|
||||
,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
,t "sum(a) over (partition by b order by c range unbounded following)"
|
||||
$ WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
[SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
|
||||
$ Just $ FrameFrom FrameRange UnboundedFollowing)
|
||||
$ Just $ FrameFrom FrameRange UnboundedFollowing
|
||||
|
||||
,("sum(a) over (partition by b order by c \n\
|
||||
,t "sum(a) over (partition by b order by c \n\
|
||||
\range between 5 preceding and 5 following)"
|
||||
,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
$ WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
|
||||
[SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
|
||||
$ Just $ FrameBetween FrameRange
|
||||
(Preceding (NumLit "5"))
|
||||
(Following (NumLit "5")))
|
||||
(Following (NumLit "5"))
|
||||
|
||||
]
|
||||
|
||||
parens :: TestItem
|
||||
parens = Group "parens" $ map (uncurry (TestScalarExpr ansi2011))
|
||||
[("(a)", Parens (Iden [Name Nothing "a"]))
|
||||
,("(a + b)", Parens (BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"] (Iden [Name Nothing "b"])))
|
||||
parens = Group "parens"
|
||||
[t "(a)" $ Parens (Iden [Name Nothing "a"])
|
||||
,t "(a + b)" $ Parens (BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"] (Iden [Name Nothing "b"]))
|
||||
]
|
||||
|
||||
functionsWithReservedNames :: TestItem
|
||||
functionsWithReservedNames = Group "functionsWithReservedNames" $ map t
|
||||
functionsWithReservedNames = Group "functionsWithReservedNames" $ map f
|
||||
["abs"
|
||||
,"char_length"
|
||||
]
|
||||
where
|
||||
t fn = TestScalarExpr ansi2011 (fn <> "(a)") $ App [Name Nothing fn] [Iden [Name Nothing "a"]]
|
||||
|
||||
f fn = t (fn <> "(a)") $ App [Name Nothing fn] [Iden [Name Nothing "a"]]
|
||||
|
|
|
@ -9,100 +9,103 @@ module Language.SQL.SimpleSQL.TableRefs (tableRefTests) where
|
|||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
import Data.Text (Text)
|
||||
|
||||
tableRefTests :: TestItem
|
||||
tableRefTests = Group "tableRefTests" $ map (uncurry (TestQueryExpr ansi2011))
|
||||
[("select a from t"
|
||||
,ms [TRSimple [Name Nothing "t"]])
|
||||
tableRefTests = Group "tableRefTests"
|
||||
[q "select a from t"
|
||||
$ ms [TRSimple [Name Nothing "t"]]
|
||||
|
||||
,("select a from f(a)"
|
||||
,ms [TRFunction [Name Nothing "f"] [Iden [Name Nothing "a"]]])
|
||||
,q "select a from f(a)"
|
||||
$ ms [TRFunction [Name Nothing "f"] [Iden [Name Nothing "a"]]]
|
||||
|
||||
,("select a from t,u"
|
||||
,ms [TRSimple [Name Nothing "t"], TRSimple [Name Nothing "u"]])
|
||||
,q "select a from t,u"
|
||||
$ ms [TRSimple [Name Nothing "t"], TRSimple [Name Nothing "u"]]
|
||||
|
||||
,("select a from s.t"
|
||||
,ms [TRSimple [Name Nothing "s", Name Nothing "t"]])
|
||||
,q "select a from s.t"
|
||||
$ ms [TRSimple [Name Nothing "s", Name Nothing "t"]]
|
||||
|
||||
-- these lateral queries make no sense but the syntax is valid
|
||||
|
||||
,("select a from lateral a"
|
||||
,ms [TRLateral $ TRSimple [Name Nothing "a"]])
|
||||
,q "select a from lateral a"
|
||||
$ ms [TRLateral $ TRSimple [Name Nothing "a"]]
|
||||
|
||||
,("select a from lateral a,b"
|
||||
,ms [TRLateral $ TRSimple [Name Nothing "a"], TRSimple [Name Nothing "b"]])
|
||||
,q "select a from lateral a,b"
|
||||
$ ms [TRLateral $ TRSimple [Name Nothing "a"], TRSimple [Name Nothing "b"]]
|
||||
|
||||
,("select a from a, lateral b"
|
||||
,ms [TRSimple [Name Nothing "a"], TRLateral $ TRSimple [Name Nothing "b"]])
|
||||
,q "select a from a, lateral b"
|
||||
$ ms [TRSimple [Name Nothing "a"], TRLateral $ TRSimple [Name Nothing "b"]]
|
||||
|
||||
,("select a from a natural join lateral b"
|
||||
,ms [TRJoin (TRSimple [Name Nothing "a"]) True JInner
|
||||
,q "select a from a natural join lateral b"
|
||||
$ ms [TRJoin (TRSimple [Name Nothing "a"]) True JInner
|
||||
(TRLateral $ TRSimple [Name Nothing "b"])
|
||||
Nothing])
|
||||
Nothing]
|
||||
|
||||
,("select a from lateral a natural join lateral b"
|
||||
,ms [TRJoin (TRLateral $ TRSimple [Name Nothing "a"]) True JInner
|
||||
,q "select a from lateral a natural join lateral b"
|
||||
$ ms [TRJoin (TRLateral $ TRSimple [Name Nothing "a"]) True JInner
|
||||
(TRLateral $ TRSimple [Name Nothing "b"])
|
||||
Nothing])
|
||||
Nothing]
|
||||
|
||||
|
||||
,("select a from t inner join u on expr"
|
||||
,ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])])
|
||||
,q "select a from t inner join u on expr"
|
||||
$ ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])]
|
||||
|
||||
,("select a from t join u on expr"
|
||||
,ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])])
|
||||
,q "select a from t join u on expr"
|
||||
$ ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])]
|
||||
|
||||
,("select a from t left join u on expr"
|
||||
,ms [TRJoin (TRSimple [Name Nothing "t"]) False JLeft (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])])
|
||||
,q "select a from t left join u on expr"
|
||||
$ ms [TRJoin (TRSimple [Name Nothing "t"]) False JLeft (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])]
|
||||
|
||||
,("select a from t right join u on expr"
|
||||
,ms [TRJoin (TRSimple [Name Nothing "t"]) False JRight (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])])
|
||||
,q "select a from t right join u on expr"
|
||||
$ ms [TRJoin (TRSimple [Name Nothing "t"]) False JRight (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])]
|
||||
|
||||
,("select a from t full join u on expr"
|
||||
,ms [TRJoin (TRSimple [Name Nothing "t"]) False JFull (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])])
|
||||
,q "select a from t full join u on expr"
|
||||
$ ms [TRJoin (TRSimple [Name Nothing "t"]) False JFull (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinOn $ Iden [Name Nothing "expr"])]
|
||||
|
||||
,("select a from t cross join u"
|
||||
,ms [TRJoin (TRSimple [Name Nothing "t"]) False
|
||||
JCross (TRSimple [Name Nothing "u"]) Nothing])
|
||||
,q "select a from t cross join u"
|
||||
$ ms [TRJoin (TRSimple [Name Nothing "t"]) False
|
||||
JCross (TRSimple [Name Nothing "u"]) Nothing]
|
||||
|
||||
,("select a from t natural inner join u"
|
||||
,ms [TRJoin (TRSimple [Name Nothing "t"]) True JInner (TRSimple [Name Nothing "u"])
|
||||
Nothing])
|
||||
,q "select a from t natural inner join u"
|
||||
$ ms [TRJoin (TRSimple [Name Nothing "t"]) True JInner (TRSimple [Name Nothing "u"])
|
||||
Nothing]
|
||||
|
||||
,("select a from t inner join u using(a,b)"
|
||||
,ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinUsing [Name Nothing "a", Name Nothing "b"])])
|
||||
,q "select a from t inner join u using(a,b)"
|
||||
$ ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
|
||||
(Just $ JoinUsing [Name Nothing "a", Name Nothing "b"])]
|
||||
|
||||
,("select a from (select a from t)"
|
||||
,ms [TRQueryExpr $ ms [TRSimple [Name Nothing "t"]]])
|
||||
,q "select a from (select a from t)"
|
||||
$ ms [TRQueryExpr $ ms [TRSimple [Name Nothing "t"]]]
|
||||
|
||||
,("select a from t as u"
|
||||
,ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") Nothing)])
|
||||
,q "select a from t as u"
|
||||
$ ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") Nothing)]
|
||||
|
||||
,("select a from t u"
|
||||
,ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") Nothing)])
|
||||
,q "select a from t u"
|
||||
$ ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") Nothing)]
|
||||
|
||||
,("select a from t u(b)"
|
||||
,ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") $ Just [Name Nothing "b"])])
|
||||
,q "select a from t u(b)"
|
||||
$ ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") $ Just [Name Nothing "b"])]
|
||||
|
||||
,("select a from (t cross join u) as u"
|
||||
,ms [TRAlias (TRParens $
|
||||
,q "select a from (t cross join u) as u"
|
||||
$ ms [TRAlias (TRParens $
|
||||
TRJoin (TRSimple [Name Nothing "t"]) False JCross (TRSimple [Name Nothing "u"]) Nothing)
|
||||
(Alias (Name Nothing "u") Nothing)])
|
||||
(Alias (Name Nothing "u") Nothing)]
|
||||
-- todo: not sure if the associativity is correct
|
||||
|
||||
,("select a from t cross join u cross join v",
|
||||
ms [TRJoin
|
||||
,q "select a from t cross join u cross join v"
|
||||
$ ms [TRJoin
|
||||
(TRJoin (TRSimple [Name Nothing "t"]) False
|
||||
JCross (TRSimple [Name Nothing "u"]) Nothing)
|
||||
False JCross (TRSimple [Name Nothing "v"]) Nothing])
|
||||
False JCross (TRSimple [Name Nothing "v"]) Nothing]
|
||||
]
|
||||
where
|
||||
ms f = toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)]
|
||||
,msFrom = f}
|
||||
q :: HasCallStack => Text -> QueryExpr -> TestItem
|
||||
q src ast = testQueryExpr ansi2011 src ast
|
||||
|
|
92
tests/Language/SQL/SimpleSQL/TestRunners.hs
Normal file
92
tests/Language/SQL/SimpleSQL/TestRunners.hs
Normal file
|
@ -0,0 +1,92 @@
|
|||
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module Language.SQL.SimpleSQL.TestRunners
|
||||
(testLex
|
||||
,lexFails
|
||||
,testScalarExpr
|
||||
,testQueryExpr
|
||||
,testStatement
|
||||
,testStatements
|
||||
,testParseQueryExpr
|
||||
,testParseQueryExprFails
|
||||
,testParseScalarExprFails
|
||||
,HasCallStack
|
||||
) where
|
||||
|
||||
import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
import Language.SQL.SimpleSQL.Pretty
|
||||
import Language.SQL.SimpleSQL.Parse
|
||||
import qualified Language.SQL.SimpleSQL.Lex as Lex
|
||||
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
|
||||
import Language.SQL.SimpleSQL.Expectations
|
||||
(shouldParseL
|
||||
,shouldFail
|
||||
,shouldParseA
|
||||
,shouldSucceed
|
||||
)
|
||||
|
||||
import Test.Hspec
|
||||
(it
|
||||
,HasCallStack
|
||||
)
|
||||
|
||||
testLex :: HasCallStack => Dialect -> Text -> [Lex.Token] -> TestItem
|
||||
testLex d input a =
|
||||
LexTest d input a $ do
|
||||
it (T.unpack input) $ Lex.lexSQL d False "" Nothing input `shouldParseL` a
|
||||
it (T.unpack $ "pp: " <> input) $ Lex.lexSQL d False "" Nothing (Lex.prettyTokens d a) `shouldParseL` a
|
||||
|
||||
lexFails :: HasCallStack => Dialect -> Text -> TestItem
|
||||
lexFails d input =
|
||||
LexFails d input $
|
||||
it (T.unpack input) $ shouldFail $ Lex.lexSQL d False "" Nothing input
|
||||
|
||||
testScalarExpr :: HasCallStack => Dialect -> Text -> ScalarExpr -> TestItem
|
||||
testScalarExpr d input a =
|
||||
TestScalarExpr d input a $ do
|
||||
it (T.unpack input) $ parseScalarExpr d "" Nothing input `shouldParseA` a
|
||||
it (T.unpack $ "pp: " <> input) $ parseScalarExpr d "" Nothing (prettyScalarExpr d a) `shouldParseA` a
|
||||
|
||||
testQueryExpr :: HasCallStack => Dialect -> Text -> QueryExpr -> TestItem
|
||||
testQueryExpr d input a =
|
||||
TestQueryExpr d input a $ do
|
||||
it (T.unpack input) $ parseQueryExpr d "" Nothing input `shouldParseA` a
|
||||
it (T.unpack $ "pp: " <> input) $ parseQueryExpr d "" Nothing (prettyQueryExpr d a) `shouldParseA` a
|
||||
|
||||
testParseQueryExpr :: HasCallStack => Dialect -> Text -> TestItem
|
||||
testParseQueryExpr d input =
|
||||
let a = parseQueryExpr d "" Nothing input
|
||||
in ParseQueryExpr d input $ do
|
||||
it (T.unpack input) $ shouldSucceed (T.unpack . prettyError) a
|
||||
case a of
|
||||
Left _ -> pure ()
|
||||
Right a' ->
|
||||
it (T.unpack $ "pp: " <> input) $
|
||||
parseQueryExpr d "" Nothing (prettyQueryExpr d a') `shouldParseA` a'
|
||||
|
||||
testParseQueryExprFails :: HasCallStack => Dialect -> Text -> TestItem
|
||||
testParseQueryExprFails d input =
|
||||
ParseQueryExprFails d input $
|
||||
it (T.unpack input) $ shouldFail $ parseQueryExpr d "" Nothing input
|
||||
|
||||
testParseScalarExprFails :: HasCallStack => Dialect -> Text -> TestItem
|
||||
testParseScalarExprFails d input =
|
||||
ParseScalarExprFails d input $
|
||||
it (T.unpack input) $ shouldFail $ parseScalarExpr d "" Nothing input
|
||||
|
||||
testStatement :: HasCallStack => Dialect -> Text -> Statement -> TestItem
|
||||
testStatement d input a =
|
||||
TestStatement d input a $ do
|
||||
it (T.unpack input) $ parseStatement d "" Nothing input `shouldParseA` a
|
||||
it (T.unpack $ "pp: " <> input) $ parseStatement d "" Nothing (prettyStatement d a) `shouldParseA` a
|
||||
|
||||
testStatements :: HasCallStack => Dialect -> Text -> [Statement] -> TestItem
|
||||
testStatements d input a =
|
||||
TestStatements d input a $ do
|
||||
it (T.unpack input) $ parseStatements d "" Nothing input `shouldParseA` a
|
||||
it (T.unpack $ "pp: " <> input) $ parseStatements d "" Nothing (prettyStatements d a) `shouldParseA` a
|
||||
|
|
@ -13,6 +13,9 @@ import Language.SQL.SimpleSQL.Syntax
|
|||
import Language.SQL.SimpleSQL.Lex (Token)
|
||||
import Language.SQL.SimpleSQL.Dialect
|
||||
|
||||
import Test.Hspec (SpecWith)
|
||||
|
||||
|
||||
import Data.Text (Text)
|
||||
|
||||
{-
|
||||
|
@ -20,13 +23,19 @@ TODO: maybe make the dialect args into [dialect], then each test
|
|||
checks all the dialects mentioned work, and all the dialects not
|
||||
mentioned give a parse error. Not sure if this will be too awkward due
|
||||
to lots of tricky exceptions/variationsx.
|
||||
|
||||
The test items are designed to allow code to grab all the examples
|
||||
in easily usable data types, but since hspec has this neat feature
|
||||
where it will give a source location for a test failure, each testitem
|
||||
apart from group already has the SpecWith attached to run that test,
|
||||
that way we can attach the source location to each test item
|
||||
-}
|
||||
|
||||
data TestItem = Group Text [TestItem]
|
||||
| TestScalarExpr Dialect Text ScalarExpr
|
||||
| TestQueryExpr Dialect Text QueryExpr
|
||||
| TestStatement Dialect Text Statement
|
||||
| TestStatements Dialect Text [Statement]
|
||||
| TestScalarExpr Dialect Text ScalarExpr (SpecWith ())
|
||||
| TestQueryExpr Dialect Text QueryExpr (SpecWith ())
|
||||
| TestStatement Dialect Text Statement (SpecWith ())
|
||||
| TestStatements Dialect Text [Statement] (SpecWith ())
|
||||
|
||||
{-
|
||||
this just checks the sql parses without error, mostly just a
|
||||
|
@ -34,12 +43,13 @@ intermediate when I'm too lazy to write out the parsed AST. These
|
|||
should all be TODO to convert to a testqueryexpr test.
|
||||
-}
|
||||
|
||||
| ParseQueryExpr Dialect Text
|
||||
| ParseQueryExpr Dialect Text (SpecWith ())
|
||||
|
||||
-- check that the string given fails to parse
|
||||
|
||||
| ParseQueryExprFails Dialect Text
|
||||
| ParseScalarExprFails Dialect Text
|
||||
| LexTest Dialect Text [Token]
|
||||
| LexFails Dialect Text
|
||||
deriving (Eq,Show)
|
||||
| ParseQueryExprFails Dialect Text (SpecWith ())
|
||||
| ParseScalarExprFails Dialect Text (SpecWith ())
|
||||
| LexTest Dialect Text [Token] (SpecWith ())
|
||||
| LexFails Dialect Text (SpecWith ())
|
||||
| GeneralParseFailTest Text Text (SpecWith ())
|
||||
|
||||
|
|
|
@ -12,13 +12,11 @@ module Language.SQL.SimpleSQL.Tests
|
|||
,TestItem(..)
|
||||
) where
|
||||
|
||||
import qualified Test.Tasty as T
|
||||
import qualified Test.Tasty.HUnit as H
|
||||
|
||||
--import Language.SQL.SimpleSQL.Syntax
|
||||
import Language.SQL.SimpleSQL.Pretty
|
||||
import Language.SQL.SimpleSQL.Parse
|
||||
import qualified Language.SQL.SimpleSQL.Lex as Lex
|
||||
import Test.Hspec
|
||||
(SpecWith
|
||||
,describe
|
||||
,parallel
|
||||
)
|
||||
|
||||
import Language.SQL.SimpleSQL.TestTypes
|
||||
|
||||
|
@ -44,11 +42,10 @@ import Language.SQL.SimpleSQL.SQL2011Schema
|
|||
import Language.SQL.SimpleSQL.MySQL
|
||||
import Language.SQL.SimpleSQL.Oracle
|
||||
import Language.SQL.SimpleSQL.CustomDialect
|
||||
import Language.SQL.SimpleSQL.ErrorMessages
|
||||
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
|
||||
|
||||
{-
|
||||
Order the tests to start from the simplest first. This is also the
|
||||
order on the generated documentation.
|
||||
|
@ -77,104 +74,22 @@ testData =
|
|||
,customDialectTests
|
||||
,emptyStatementTests
|
||||
,createIndexTests
|
||||
,errorMessageTests
|
||||
]
|
||||
|
||||
tests :: T.TestTree
|
||||
tests = itemToTest testData
|
||||
tests :: SpecWith ()
|
||||
tests = parallel $ itemToTest testData
|
||||
|
||||
--runTests :: IO ()
|
||||
--runTests = void $ H.runTestTT $ itemToTest testData
|
||||
|
||||
itemToTest :: TestItem -> T.TestTree
|
||||
itemToTest :: TestItem -> SpecWith ()
|
||||
itemToTest (Group nm ts) =
|
||||
T.testGroup (T.unpack nm) $ map itemToTest ts
|
||||
itemToTest (TestScalarExpr d str expected) =
|
||||
toTest parseScalarExpr prettyScalarExpr d str expected
|
||||
itemToTest (TestQueryExpr d str expected) =
|
||||
toTest parseQueryExpr prettyQueryExpr d str expected
|
||||
itemToTest (TestStatement d str expected) =
|
||||
toTest parseStatement prettyStatement d str expected
|
||||
itemToTest (TestStatements d str expected) =
|
||||
toTest parseStatements prettyStatements d str expected
|
||||
itemToTest (ParseQueryExpr d str) =
|
||||
toPTest parseQueryExpr prettyQueryExpr d str
|
||||
|
||||
itemToTest (ParseQueryExprFails d str) =
|
||||
toFTest parseQueryExpr prettyQueryExpr d str
|
||||
|
||||
itemToTest (ParseScalarExprFails d str) =
|
||||
toFTest parseScalarExpr prettyScalarExpr d str
|
||||
|
||||
itemToTest (LexTest d s ts) = makeLexerTest d s ts
|
||||
itemToTest (LexFails d s) = makeLexingFailsTest d s
|
||||
|
||||
makeLexerTest :: Dialect -> Text -> [Lex.Token] -> T.TestTree
|
||||
makeLexerTest d s ts = H.testCase (T.unpack s) $ do
|
||||
let ts1 = either (error . T.unpack . Lex.prettyError) id $ Lex.lexSQL d "" Nothing s
|
||||
H.assertEqual "" ts ts1
|
||||
let s' = Lex.prettyTokens d $ ts1
|
||||
H.assertEqual "pretty print" s s'
|
||||
|
||||
makeLexingFailsTest :: Dialect -> Text -> T.TestTree
|
||||
makeLexingFailsTest d s = H.testCase (T.unpack s) $ do
|
||||
case Lex.lexSQL d "" Nothing s of
|
||||
Right x -> H.assertFailure $ "lexing should have failed: " ++ T.unpack s ++ "\ngot: " ++ show x
|
||||
Left _ -> pure ()
|
||||
|
||||
|
||||
toTest :: (Eq a, Show a) =>
|
||||
(Dialect -> Text -> Maybe (Int,Int) -> Text -> Either ParseError a)
|
||||
-> (Dialect -> a -> Text)
|
||||
-> Dialect
|
||||
-> Text
|
||||
-> a
|
||||
-> T.TestTree
|
||||
toTest parser pp d str expected = H.testCase (T.unpack str) $ do
|
||||
let egot = parser d "" Nothing str
|
||||
case egot of
|
||||
Left e -> H.assertFailure $ T.unpack $ prettyError e
|
||||
Right got -> H.assertEqual "" expected got
|
||||
|
||||
let str' = pp d expected
|
||||
egot' = parser d "" Nothing str'
|
||||
case egot' of
|
||||
Left e' ->
|
||||
H.assertFailure $ "pp roundtrip"
|
||||
++ "\n" ++ (T.unpack str')
|
||||
++ (T.unpack $ prettyError e')
|
||||
Right got' ->
|
||||
H.assertEqual
|
||||
("pp roundtrip" ++ "\n" ++ T.unpack str')
|
||||
expected got'
|
||||
|
||||
toPTest :: (Eq a, Show a) =>
|
||||
(Dialect -> Text -> Maybe (Int,Int) -> Text -> Either ParseError a)
|
||||
-> (Dialect -> a -> Text)
|
||||
-> Dialect
|
||||
-> Text
|
||||
-> T.TestTree
|
||||
toPTest parser pp d str = H.testCase (T.unpack str) $ do
|
||||
let egot = parser d "" Nothing str
|
||||
case egot of
|
||||
Left e -> H.assertFailure $ T.unpack $ prettyError e
|
||||
Right got -> do
|
||||
let str' = pp d got
|
||||
let egot' = parser d "" Nothing str'
|
||||
case egot' of
|
||||
Left e' -> H.assertFailure $ "pp roundtrip "
|
||||
++ "\n" ++ T.unpack str' ++ "\n"
|
||||
++ T.unpack (prettyError e')
|
||||
Right _got' -> return ()
|
||||
|
||||
toFTest :: (Eq a, Show a) =>
|
||||
(Dialect -> Text -> Maybe (Int,Int) -> Text -> Either ParseError a)
|
||||
-> (Dialect -> a -> Text)
|
||||
-> Dialect
|
||||
-> Text
|
||||
-> T.TestTree
|
||||
toFTest parser _pp d str = H.testCase (T.unpack str) $ do
|
||||
let egot = parser d "" Nothing str
|
||||
case egot of
|
||||
Left _e -> return ()
|
||||
Right _got ->
|
||||
H.assertFailure $ "parse didn't fail: " ++ show d ++ "\n" ++ T.unpack str
|
||||
describe (T.unpack nm) $ mapM_ itemToTest ts
|
||||
itemToTest (TestScalarExpr _ _ _ t) = t
|
||||
itemToTest (TestQueryExpr _ _ _ t) = t
|
||||
itemToTest (TestStatement _ _ _ t) = t
|
||||
itemToTest (TestStatements _ _ _ t) = t
|
||||
itemToTest (ParseQueryExpr _ _ t) = t
|
||||
itemToTest (ParseQueryExprFails _ _ t) = t
|
||||
itemToTest (ParseScalarExprFails _ _ t) = t
|
||||
itemToTest (LexTest _ _ _ t) = t
|
||||
itemToTest (LexFails _ _ t) = t
|
||||
itemToTest (GeneralParseFailTest _ _ t) = t
|
||||
|
|
|
@ -14,15 +14,14 @@ module Language.SQL.SimpleSQL.Tpch (tpchTests,tpchQueries) where
|
|||
import Language.SQL.SimpleSQL.TestTypes
|
||||
|
||||
import Data.Text (Text)
|
||||
import Language.SQL.SimpleSQL.TestRunners
|
||||
|
||||
tpchTests :: TestItem
|
||||
tpchTests =
|
||||
Group "parse tpch"
|
||||
$ map (ParseQueryExpr ansi2011 . snd) tpchQueries
|
||||
tpchTests = Group "parse tpch" tpchQueries
|
||||
|
||||
tpchQueries :: [(String,Text)]
|
||||
tpchQueries :: [TestItem]
|
||||
tpchQueries =
|
||||
[("Q1","\n\
|
||||
[q "Q1" "\n\
|
||||
\select\n\
|
||||
\ l_returnflag,\n\
|
||||
\ l_linestatus,\n\
|
||||
|
@ -43,8 +42,8 @@ tpchQueries =
|
|||
\ l_linestatus\n\
|
||||
\order by\n\
|
||||
\ l_returnflag,\n\
|
||||
\ l_linestatus")
|
||||
,("Q2","\n\
|
||||
\ l_linestatus"
|
||||
,q "Q2" "\n\
|
||||
\select\n\
|
||||
\ s_acctbal,\n\
|
||||
\ s_name,\n\
|
||||
|
@ -88,8 +87,8 @@ tpchQueries =
|
|||
\ n_name,\n\
|
||||
\ s_name,\n\
|
||||
\ p_partkey\n\
|
||||
\fetch first 100 rows only")
|
||||
,("Q3","\n\
|
||||
\fetch first 100 rows only"
|
||||
,q "Q3" "\n\
|
||||
\ select\n\
|
||||
\ l_orderkey,\n\
|
||||
\ sum(l_extendedprice * (1 - l_discount)) as revenue,\n\
|
||||
|
@ -112,8 +111,8 @@ tpchQueries =
|
|||
\ order by\n\
|
||||
\ revenue desc,\n\
|
||||
\ o_orderdate\n\
|
||||
\ fetch first 10 rows only")
|
||||
,("Q4","\n\
|
||||
\ fetch first 10 rows only"
|
||||
,q "Q4" "\n\
|
||||
\ select\n\
|
||||
\ o_orderpriority,\n\
|
||||
\ count(*) as order_count\n\
|
||||
|
@ -134,8 +133,8 @@ tpchQueries =
|
|||
\ group by\n\
|
||||
\ o_orderpriority\n\
|
||||
\ order by\n\
|
||||
\ o_orderpriority")
|
||||
,("Q5","\n\
|
||||
\ o_orderpriority"
|
||||
,q "Q5" "\n\
|
||||
\ select\n\
|
||||
\ n_name,\n\
|
||||
\ sum(l_extendedprice * (1 - l_discount)) as revenue\n\
|
||||
|
@ -159,8 +158,8 @@ tpchQueries =
|
|||
\ group by\n\
|
||||
\ n_name\n\
|
||||
\ order by\n\
|
||||
\ revenue desc")
|
||||
,("Q6","\n\
|
||||
\ revenue desc"
|
||||
,q "Q6" "\n\
|
||||
\ select\n\
|
||||
\ sum(l_extendedprice * l_discount) as revenue\n\
|
||||
\ from\n\
|
||||
|
@ -169,8 +168,8 @@ tpchQueries =
|
|||
\ l_shipdate >= date '1997-01-01'\n\
|
||||
\ and l_shipdate < date '1997-01-01' + interval '1' year\n\
|
||||
\ and l_discount between 0.07 - 0.01 and 0.07 + 0.01\n\
|
||||
\ and l_quantity < 24")
|
||||
,("Q7","\n\
|
||||
\ and l_quantity < 24"
|
||||
,q "Q7" "\n\
|
||||
\ select\n\
|
||||
\ supp_nation,\n\
|
||||
\ cust_nation,\n\
|
||||
|
@ -209,8 +208,8 @@ tpchQueries =
|
|||
\ order by\n\
|
||||
\ supp_nation,\n\
|
||||
\ cust_nation,\n\
|
||||
\ l_year")
|
||||
,("Q8","\n\
|
||||
\ l_year"
|
||||
,q "Q8" "\n\
|
||||
\ select\n\
|
||||
\ o_year,\n\
|
||||
\ sum(case\n\
|
||||
|
@ -247,8 +246,8 @@ tpchQueries =
|
|||
\ group by\n\
|
||||
\ o_year\n\
|
||||
\ order by\n\
|
||||
\ o_year")
|
||||
,("Q9","\n\
|
||||
\ o_year"
|
||||
,q "Q9" "\n\
|
||||
\ select\n\
|
||||
\ nation,\n\
|
||||
\ o_year,\n\
|
||||
|
@ -280,8 +279,8 @@ tpchQueries =
|
|||
\ o_year\n\
|
||||
\ order by\n\
|
||||
\ nation,\n\
|
||||
\ o_year desc")
|
||||
,("Q10","\n\
|
||||
\ o_year desc"
|
||||
,q "Q10" "\n\
|
||||
\ select\n\
|
||||
\ c_custkey,\n\
|
||||
\ c_name,\n\
|
||||
|
@ -313,8 +312,8 @@ tpchQueries =
|
|||
\ c_comment\n\
|
||||
\ order by\n\
|
||||
\ revenue desc\n\
|
||||
\ fetch first 20 rows only")
|
||||
,("Q11","\n\
|
||||
\ fetch first 20 rows only"
|
||||
,q "Q11" "\n\
|
||||
\ select\n\
|
||||
\ ps_partkey,\n\
|
||||
\ sum(ps_supplycost * ps_availqty) as value\n\
|
||||
|
@ -341,8 +340,8 @@ tpchQueries =
|
|||
\ and n_name = 'CHINA'\n\
|
||||
\ )\n\
|
||||
\ order by\n\
|
||||
\ value desc")
|
||||
,("Q12","\n\
|
||||
\ value desc"
|
||||
,q "Q12" "\n\
|
||||
\ select\n\
|
||||
\ l_shipmode,\n\
|
||||
\ sum(case\n\
|
||||
|
@ -370,8 +369,8 @@ tpchQueries =
|
|||
\ group by\n\
|
||||
\ l_shipmode\n\
|
||||
\ order by\n\
|
||||
\ l_shipmode")
|
||||
,("Q13","\n\
|
||||
\ l_shipmode"
|
||||
,q "Q13" "\n\
|
||||
\ select\n\
|
||||
\ c_count,\n\
|
||||
\ count(*) as custdist\n\
|
||||
|
@ -391,8 +390,8 @@ tpchQueries =
|
|||
\ c_count\n\
|
||||
\ order by\n\
|
||||
\ custdist desc,\n\
|
||||
\ c_count desc")
|
||||
,("Q14","\n\
|
||||
\ c_count desc"
|
||||
,q "Q14" "\n\
|
||||
\ select\n\
|
||||
\ 100.00 * sum(case\n\
|
||||
\ when p_type like 'PROMO%'\n\
|
||||
|
@ -405,8 +404,8 @@ tpchQueries =
|
|||
\ where\n\
|
||||
\ l_partkey = p_partkey\n\
|
||||
\ and l_shipdate >= date '1994-12-01'\n\
|
||||
\ and l_shipdate < date '1994-12-01' + interval '1' month")
|
||||
,("Q15","\n\
|
||||
\ and l_shipdate < date '1994-12-01' + interval '1' month"
|
||||
,q "Q15" "\n\
|
||||
\ /*create view revenue0 (supplier_no, total_revenue) as\n\
|
||||
\ select\n\
|
||||
\ l_suppkey,\n\
|
||||
|
@ -448,8 +447,8 @@ tpchQueries =
|
|||
\ revenue0\n\
|
||||
\ )\n\
|
||||
\ order by\n\
|
||||
\ s_suppkey")
|
||||
,("Q16","\n\
|
||||
\ s_suppkey"
|
||||
,q "Q16" "\n\
|
||||
\ select\n\
|
||||
\ p_brand,\n\
|
||||
\ p_type,\n\
|
||||
|
@ -479,8 +478,8 @@ tpchQueries =
|
|||
\ supplier_cnt desc,\n\
|
||||
\ p_brand,\n\
|
||||
\ p_type,\n\
|
||||
\ p_size")
|
||||
,("Q17","\n\
|
||||
\ p_size"
|
||||
,q "Q17" "\n\
|
||||
\ select\n\
|
||||
\ sum(l_extendedprice) / 7.0 as avg_yearly\n\
|
||||
\ from\n\
|
||||
|
@ -497,8 +496,8 @@ tpchQueries =
|
|||
\ lineitem\n\
|
||||
\ where\n\
|
||||
\ l_partkey = p_partkey\n\
|
||||
\ )")
|
||||
,("Q18","\n\
|
||||
\ )"
|
||||
,q "Q18" "\n\
|
||||
\ select\n\
|
||||
\ c_name,\n\
|
||||
\ c_custkey,\n\
|
||||
|
@ -531,8 +530,8 @@ tpchQueries =
|
|||
\ order by\n\
|
||||
\ o_totalprice desc,\n\
|
||||
\ o_orderdate\n\
|
||||
\ fetch first 100 rows only")
|
||||
,("Q19","\n\
|
||||
\ fetch first 100 rows only"
|
||||
,q "Q19" "\n\
|
||||
\ select\n\
|
||||
\ sum(l_extendedprice* (1 - l_discount)) as revenue\n\
|
||||
\ from\n\
|
||||
|
@ -567,8 +566,8 @@ tpchQueries =
|
|||
\ and p_size between 1 and 15\n\
|
||||
\ and l_shipmode in ('AIR', 'AIR REG')\n\
|
||||
\ and l_shipinstruct = 'DELIVER IN PERSON'\n\
|
||||
\ )")
|
||||
,("Q20","\n\
|
||||
\ )"
|
||||
,q "Q20" "\n\
|
||||
\ select\n\
|
||||
\ s_name,\n\
|
||||
\ s_address\n\
|
||||
|
@ -605,8 +604,8 @@ tpchQueries =
|
|||
\ and s_nationkey = n_nationkey\n\
|
||||
\ and n_name = 'VIETNAM'\n\
|
||||
\ order by\n\
|
||||
\ s_name")
|
||||
,("Q21","\n\
|
||||
\ s_name"
|
||||
,q "Q21" "\n\
|
||||
\ select\n\
|
||||
\ s_name,\n\
|
||||
\ count(*) as numwait\n\
|
||||
|
@ -646,8 +645,8 @@ tpchQueries =
|
|||
\ order by\n\
|
||||
\ numwait desc,\n\
|
||||
\ s_name\n\
|
||||
\ fetch first 100 rows only")
|
||||
,("Q22","\n\
|
||||
\ fetch first 100 rows only"
|
||||
,q "Q22" "\n\
|
||||
\ select\n\
|
||||
\ cntrycode,\n\
|
||||
\ count(*) as numcust,\n\
|
||||
|
@ -684,5 +683,8 @@ tpchQueries =
|
|||
\ group by\n\
|
||||
\ cntrycode\n\
|
||||
\ order by\n\
|
||||
\ cntrycode")
|
||||
\ cntrycode"
|
||||
]
|
||||
where
|
||||
q :: HasCallStack => Text -> Text -> TestItem
|
||||
q _ src = testParseQueryExpr ansi2011 src
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
|
||||
|
||||
import Test.Tasty
|
||||
import Test.Hspec (hspec)
|
||||
|
||||
|
||||
import Language.SQL.SimpleSQL.Tests
|
||||
|
||||
main :: IO ()
|
||||
main = defaultMain tests
|
||||
main = hspec tests
|
||||
|
|
|
@ -20,26 +20,28 @@ doc _ (Group nm _) | "generated" `T.isInfixOf` nm = []
|
|||
doc n (Group nm is) =
|
||||
Heading n (L.fromStrict nm)
|
||||
: concatMap (doc (n + 1)) is
|
||||
doc _ (TestScalarExpr _ str e) =
|
||||
doc _ (TestScalarExpr _ str e _) =
|
||||
[Row (L.fromStrict str) (L.pack $ ppShow e)]
|
||||
doc _ (TestQueryExpr _ str e) =
|
||||
doc _ (TestQueryExpr _ str e _) =
|
||||
[Row (L.fromStrict str) (L.pack $ ppShow e)]
|
||||
doc _ (TestStatement _ str e) =
|
||||
doc _ (TestStatement _ str e _) =
|
||||
[Row (L.fromStrict str) (L.pack $ ppShow e)]
|
||||
doc _ (TestStatements _ str e) =
|
||||
doc _ (TestStatements _ str e _) =
|
||||
[Row (L.fromStrict str) (L.pack $ ppShow e)]
|
||||
doc _ (ParseQueryExpr d str) =
|
||||
doc _ (ParseQueryExpr d str _) =
|
||||
[Row (L.fromStrict str) (showResult $ P.parseQueryExpr d "" Nothing str)]
|
||||
doc _ (ParseQueryExprFails d str) =
|
||||
doc _ (ParseQueryExprFails d str _) =
|
||||
[Row (L.fromStrict str) (showResult $ P.parseQueryExpr d "" Nothing str)]
|
||||
doc _ (ParseScalarExprFails d str) =
|
||||
doc _ (ParseScalarExprFails d str _) =
|
||||
[Row (L.fromStrict str) (showResult $ P.parseScalarExpr d "" Nothing str)]
|
||||
|
||||
doc _ (LexTest d str _) =
|
||||
[Row (L.fromStrict str) (showResultL $ L.lexSQL d "" Nothing str)]
|
||||
doc _ (LexTest d str _ _) =
|
||||
[Row (L.fromStrict str) (showResultL $ L.lexSQL d False "" Nothing str)]
|
||||
|
||||
doc _ (LexFails d str _) =
|
||||
[Row (L.fromStrict str) (showResultL $ L.lexSQL d False "" Nothing str)]
|
||||
doc _ (GeneralParseFailTest {}) = []
|
||||
|
||||
doc _ (LexFails d str) =
|
||||
[Row (L.fromStrict str) (showResultL $ L.lexSQL d "" Nothing str)]
|
||||
|
||||
showResult :: Show a => Either P.ParseError a -> L.Text
|
||||
showResult = either (("Left\n" <>) . L.fromStrict . P.prettyError) (L.pack . ppShow)
|
||||
|
|
|
@ -184,6 +184,8 @@ generally available to work on these, so you should either make a pull
|
|||
request, or find someone willing to implement the features and make a
|
||||
pull request.
|
||||
|
||||
Bug reports of confusing or poor parse errors are also encouraged.
|
||||
|
||||
There is a related tutorial on implementing a SQL parser here:
|
||||
<http://jakewheat.github.io/intro_to_parsing/> (TODO: this is out of
|
||||
date, in the process of being updated)
|
||||
|
@ -210,6 +212,13 @@ Or use the makefile target
|
|||
make test
|
||||
~~~~
|
||||
|
||||
To skip some of the slow lexer tests, which you usually only need to
|
||||
run before each commit, use:
|
||||
|
||||
~~~~
|
||||
make fast-test
|
||||
~~~~
|
||||
|
||||
When you add support for new syntax: add some tests. If you modify or
|
||||
fix something, and it doesn't have tests, add some. If the syntax
|
||||
isn't in ANSI SQL, guard it behind a dialect flag. If you add
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
cabal-version: 2.2
|
||||
|
||||
name: simple-sql-parser
|
||||
version: 0.7.1
|
||||
version: 0.8.0
|
||||
|
||||
executable RenderTestCases
|
||||
main-is: RenderTestCases.hs
|
||||
|
@ -13,9 +13,11 @@ executable RenderTestCases
|
|||
parser-combinators,
|
||||
mtl,
|
||||
containers,
|
||||
tasty,
|
||||
tasty-hunit,
|
||||
hspec,
|
||||
hspec-megaparsec,
|
||||
pretty-show,
|
||||
hspec-expectations,
|
||||
raw-strings-qq,
|
||||
default-language: Haskell2010
|
||||
ghc-options: -Wall -O0
|
||||
|
||||
|
@ -47,3 +49,6 @@ executable RenderTestCases
|
|||
Language.SQL.SimpleSQL.TestTypes
|
||||
Language.SQL.SimpleSQL.Tests
|
||||
Language.SQL.SimpleSQL.Tpch
|
||||
Language.SQL.SimpleSQL.Expectations
|
||||
Language.SQL.SimpleSQL.TestRunners
|
||||
Language.SQL.SimpleSQL.ErrorMessages
|
||||
|
|
Loading…
Reference in a new issue