diff --git a/Language/SQL/SimpleSQL/Dialect.hs b/Language/SQL/SimpleSQL/Dialect.hs
--- a/Language/SQL/SimpleSQL/Dialect.hs
+++ b/Language/SQL/SimpleSQL/Dialect.hs
@@ -80,7 +80,7 @@
     ,diAtIdentifier :: Bool
      -- | allow identifiers with a leading \# \#example
     ,diHashIdentifier :: Bool
-     -- | allow positional identifiers like this: $1 
+     -- | allow positional identifiers like this: $1
     ,diPositionalArg :: Bool
      -- | allow postgres style dollar strings
     ,diDollarString :: Bool
@@ -96,6 +96,12 @@
     ,diAutoincrement :: Bool
      -- | allow omitting the comma between constraint clauses
     ,diNonCommaSeparatedConstraints :: Bool
+     -- | allow marking tables as "without rowid"
+    ,diWithoutRowidTables :: Bool
+     -- | allow omitting types for columns
+    ,diOptionalColumnTypes :: Bool
+     -- | allow mixing in DEFAULT clauses with other constraints
+    ,diDefaultClausesAsConstraints :: Bool
     }
                deriving (Eq,Show,Read,Data,Typeable)
 
@@ -117,9 +123,12 @@
                    ,diEString = False
                    ,diPostgresSymbols = False
                    ,diSqlServerSymbols = False
-                   ,diConvertFunction = False                     
+                   ,diConvertFunction = False
                    ,diAutoincrement = False
                    ,diNonCommaSeparatedConstraints = False
+                   ,diWithoutRowidTables = False
+                   ,diOptionalColumnTypes = False
+                   ,diDefaultClausesAsConstraints = False
                    }
 
 -- | mysql dialect
diff --git a/Language/SQL/SimpleSQL/Lex.hs b/Language/SQL/SimpleSQL/Lex.hs
--- a/Language/SQL/SimpleSQL/Lex.hs
+++ b/Language/SQL/SimpleSQL/Lex.hs
@@ -74,7 +74,6 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RecordWildCards   #-}
 {-# LANGUAGE TypeFamilies      #-}
-
 module Language.SQL.SimpleSQL.Lex
     (Token(..)
     ,WithPos(..)
@@ -111,21 +110,26 @@
     ,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 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 @@
     | 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 @@
     -> 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 @@
     -> 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 _ (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,25 +297,55 @@
 
 -- | 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 @@
 -}
 
 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 @@
     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 @@
 
 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 @@
 --------------------------------------
 
 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 @@
 -- 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 @@
 
 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 @@
     else basicAnsiOps
    ])
  where
-   dots = [takeWhile1P (Just "dot") (=='.')]
+   dots = [takeWhile1P Nothing (=='.')]
    odbcSymbol = [string "{", string "}"]
    postgresExtraSymbols =
        [try (string ":=")
@@ -670,7 +723,7 @@
 --------------------------------------
 
 sqlWhitespace :: Dialect -> Parser Token
-sqlWhitespace _ = Whitespace <$> takeWhile1P (Just "whitespace") isSpace <?> ""
+sqlWhitespace _ = Whitespace <$> takeWhile1P Nothing isSpace
 
 ----------------------------------------------------------------------------
 
@@ -679,6 +732,9 @@
 char_ :: Char -> Parser ()
 char_ = void . char
 
+hchar_ :: Char -> Parser ()
+hchar_ = void . hidden . char
+
 string_ :: Text -> Parser ()
 string_ = void . string
 
@@ -687,6 +743,39 @@
 
 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
 
 ----------------------------------------------------------------------------
 
diff --git a/Language/SQL/SimpleSQL/Parse.hs b/Language/SQL/SimpleSQL/Parse.hs
--- a/Language/SQL/SimpleSQL/Parse.hs
+++ b/Language/SQL/SimpleSQL/Parse.hs
@@ -195,8 +195,10 @@
 
     ,ParseErrorBundle(..)
     ,errorBundlePretty
+    ,hidden
+    ,failure
+    ,ErrorItem(..)
 
-    ,(<?>)
     ,(<|>)
     ,token
     ,choice
@@ -209,9 +211,11 @@
     ,some
     ,many
     ,between
+    ,lookAhead
     )
 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
@@ -221,6 +225,7 @@
     )
 
 import qualified Data.Set           as Set
+import qualified Data.List.NonEmpty as NE
 import Data.Void (Void)
 
 import Control.Monad (guard, void)
@@ -228,13 +233,15 @@
 import Data.Char (isDigit)
 import Data.List (sort,groupBy)
 import Data.Function (on)
-import Data.Maybe (catMaybes, isJust, mapMaybe)
+import Data.Maybe (catMaybes, isJust, mapMaybe, fromMaybe)
 import Data.Text (Text)
 import qualified Data.Text as T
 
-import Language.SQL.SimpleSQL.Syntax 
+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 +331,9 @@
           -> Text
           -> Either ParseError a
 wrapParse parser d f p src = do
-    lx <- either (Left . LexError) Right $ L.lexSQLWithPositions d f p src
-    either (Left . ParseError) Right $ 
-        runReader (runParserT (parser <* (eof <?> "")) (T.unpack f)
+    lx <- either (Left . LexError) Right $ L.lexSQLWithPositions d True f p src
+    either (Left . ParseError) Right $
+        runReader (runParserT (parser <* (hidden eof)) (T.unpack f)
                    $ L.SQLStream (T.unpack src) $ filter notSpace lx) d
   where
     notSpace = notSpace' . L.tokenVal
@@ -378,21 +385,22 @@
 u&"example quoted"
 -}
 
-name :: Parser Name
-name = do
+name :: Text -> Parser Name
+name lbl = label lbl $ do
     bl <- askDialect diKeywords
     uncurry Name <$> identifierTok bl
 
 -- todo: replace (:[]) with a named function all over
 
-names :: Parser [Name]
-names = reverse <$> (((:[]) <$> name) <??*> anotherName)
+names :: Text -> Parser [Name]
+names lbl =
+    label lbl (reverse <$> (((:[]) <$> name lbl) `chainrSuffix` 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 lbl)))
 
 {-
 = Type Names
@@ -501,41 +509,53 @@
 
 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)
+        `chainrSuffix` tnSuffix)
   where
     rowTypeName =
-        RowTypeName <$> (keyword_ "row" *> parens (commaSep1 rowField))
-    rowField = (,) <$> name <*> typeName
+        RowTypeName <$> (hidden (keyword_ "row") *> parens (commaSep1 rowField))
+    rowField = (,) <$> name "type 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)
+    nameOfType = reservedTypeNames <|> names "type name"
+    charTypeName = charSet <**> (option [] tcollate <**> pure (flip4 CharTypeName))
+                   <|> pure [] <**> (tcollate <**> pure (flip4 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))
+        <**> pure (flip3 PrecScaleTypeName)
     precLengthTypeName =
         Just <$> lobPrecSuffix
-        <**> (optional lobUnits <$$$$> PrecLengthTypeName)
-        <|> pure Nothing <**> ((Just <$> lobUnits) <$$$$> PrecLengthTypeName)
-    timeTypeName = tz <$$$> TimeTypeName
+        <**> (optional lobUnits <**> pure (flip4 PrecLengthTypeName))
+        <|> pure Nothing <**> ((Just <$> lobUnits) <**> pure (flip4 PrecLengthTypeName))
+    timeTypeName = tz <**> pure (flip3 TimeTypeName)
     ----------------------------
     lobPrecSuffix = PrecK <$ keyword_ "k"
                     <|> PrecM <$ keyword_ "m"
@@ -550,13 +570,13 @@
                <|> PrecOctets <$ keyword_ "byte"
     tz = True <$ keywords_ ["with", "time","zone"]
          <|> False <$ keywords_ ["without", "time","zone"]
-    charSet = keywords_ ["character", "set"] *> names
-    tcollate = keyword_ "collate" *> names
+    charSet = keywords_ ["character", "set"] *> names "character set name"
+    tcollate = keyword_ "collate" *> names "collation name"
     ----------------------------
     tnSuffix = multiset <|> array
     multiset = MultisetTypeName <$ keyword_ "multiset"
     array = keyword_ "array" *>
-        (optional (brackets unsignedInteger) <$$> ArrayTypeName)
+        (optional (brackets unsignedInteger) <**> pure (flip ArrayTypeName))
     ----------------------------
     -- this parser handles the fixed set of multi word
     -- type names, plus all the type names which are
@@ -564,8 +584,8 @@
     reservedTypeNames = do
         stn <- askDialect diSpecialTypeNames
         (:[]) . Name Nothing . T.unwords <$> makeKeywordTree stn
-        
 
+
 {-
 = Scalar expressions
 
@@ -589,12 +609,19 @@
 === star
 
 used in select *, select x.*, and agg(*) variations, and some other
-places as well. The parser doesn't attempt to check that the star is
-in a valid context, it parses it OK in any scalar expression context.
+places as well. The parser makes an attempt to not parse star in
+most contexts, to provide better experience when the user makes a mistake
+in an expression containing * meaning multiple. It will parse a *
+at the top level of a select item, or in arg in a app argument list.
 -}
 
 star :: Parser ScalarExpr
-star = Star <$ symbol "*"
+star =
+    hidden $ choice
+    [Star <$ symbol "*"
+    -- much easier to use try here than to left factor where
+    -- this is allowed and not allowed
+    ,try (QStar <$> (names "qualified star" <* symbol "." <* symbol "*"))]
 
 {-
 == parameter
@@ -609,7 +636,7 @@
     [Parameter <$ questionMark
     ,HostParameter
      <$> hostParamTok
-     <*> optional (keyword "indicator" *> hostParamTok)]
+     <*> hoptional (keyword "indicator" *> hostParamTok)]
 
 -- == positional arg
 
@@ -624,7 +651,11 @@
 
 parensExpr :: Parser ScalarExpr
 parensExpr = parens $ choice
-    [SubQueryExpr SqSq <$> queryExpr
+    -- no parens here used for nested parens expressions
+    -- this could be fixed to be general with some refactoring, but at
+    -- the moment, you can't use additional redundant parens in a
+    -- subqueryexpr
+    [SubQueryExpr SqSq <$> queryExprNoParens
     ,ctor <$> commaSep1 scalarExpr]
   where
     ctor [a] = Parens a
@@ -710,7 +741,7 @@
 
 nextValueFor :: Parser ScalarExpr
 nextValueFor = keywords_ ["next","value","for"] >>
-    NextValueFor <$> names
+    NextValueFor <$> names "sequence generator name"
 
 {-
 === interval
@@ -734,11 +765,12 @@
 -}
 
 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 +796,43 @@
 
 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 "identifier" <**> (hidden app <|> pure Iden)))
+        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] <**> (hidden app <|> pure Iden)
+              | 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 +866,7 @@
               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 +973,27 @@
 
 app :: Parser ([Name] -> ScalarExpr)
 app =
-    openParen *> choice
-    [duplicates
-     <**> (commaSep1 scalarExpr
-           <**> ((option [] orderBy <* closeParen)
-                 <**> (optional afilter <$$$$$> AggregateApp)))
+    hidden openParen *> choice
+    [hidden duplicates
+     <**> (commaSep1 scalarExprOrStar
+           <**> ((hoption [] orderBy <* closeParen)
+                 <**> (hoptional afilter <**> pure (flip5 AggregateApp))))
      -- separate cases with no all or distinct which must have at
      -- least one scalar expr
-    ,commaSep1 scalarExpr
+    ,commaSep1 scalarExprOrStar
      <**> choice
-          [closeParen *> choice
+          [closeParen *> hidden (choice
                          [window
                          ,withinGroup
-                         ,(Just <$> afilter) <$$$> aggAppWithoutDupeOrd
-                         ,pure (flip App)]
-          ,orderBy <* closeParen
-           <**> (optional afilter <$$$$> aggAppWithoutDupe)]
+                         ,(Just <$> afilter) <**> pure (flip3 aggAppWithoutDupeOrd)
+                         ,pure (flip App)])
+          ,hidden orderBy <* closeParen
+           <**> (hoptional afilter <**> pure (flip4 aggAppWithoutDupe))]
      -- no scalarExprs: duplicates and order by not allowed
-    ,([] <$ closeParen) <**> option (flip App) (window <|> withinGroup)
+    ,([] <$ closeParen) <**> choice
+                             [window
+                             ,withinGroup
+                             ,pure $ flip App]
     ]
   where
     aggAppWithoutDupeOrd n es f = AggregateApp n SQDefault es [] f
@@ -949,7 +1004,7 @@
 
 withinGroup :: Parser ([ScalarExpr] -> [Name] -> ScalarExpr)
 withinGroup =
-    (keywords_ ["within", "group"] *> parens orderBy) <$$$> AggregateAppGroup
+    (keywords_ ["within", "group"] *> parens orderBy) <**> pure (flip3 AggregateAppGroup)
 
 {-
 ==== window
@@ -968,14 +1023,17 @@
 window =
   keyword_ "over" *> openParen *> option [] partitionBy
   <**> (option [] orderBy
-        <**> ((optional frameClause <* closeParen) <$$$$$> WindowApp))
+        <**> ((optional frameClause <* closeParen) <**> pure (flip5 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)
-                            <$$$> FrameBetween)
+                            <**> pure (flip3 FrameBetween))
                       -- maybe this should still use a b expression
                       -- for consistency
                      ,frameLimit False <**> pure (flip FrameFrom)]
@@ -1116,7 +1174,7 @@
 collateSuffix:: Parser (ScalarExpr -> ScalarExpr)
 collateSuffix = do
     keyword_ "collate"
-    i <- names
+    i <- names "collation name"
     pure $ \v -> Collate v i
 
 {-
@@ -1128,8 +1186,8 @@
 
 
 odbcExpr :: Parser ScalarExpr
-odbcExpr = between (symbol "{") (symbol "}")
-           (odbcTimeLit <|> odbcFunc)
+odbcExpr =
+    braces (odbcTimeLit <|> odbcFunc)
   where
     odbcTimeLit =
         OdbcLiteral <$> choice [OLDate <$ keyword "d"
@@ -1232,33 +1290,33 @@
 
        ]
   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 +1329,43 @@
 -}
 
 scalarExpr :: Parser ScalarExpr
-scalarExpr = E.makeExprParser term (opTable False)
+scalarExpr = expressionLabel $ E.makeExprParser term (opTable False)
 
+-- used when parsing contexts where a * or x.* is allowed
+-- currently at the top level of a select item or top level of
+-- argument passed to an app-like. This list may need to be extended.
+
+scalarExprOrStar :: Parser ScalarExpr
+scalarExprOrStar = star <|> scalarExpr
+
+-- use this to get a nice unexpected keyword error which doesn't also
+-- mangle other errors
+expressionLabel :: Parser a -> Parser a
+expressionLabel p = label "expression" p <|> failOnKeyword
+
 term :: Parser ScalarExpr
-term = choice [simpleLiteral
-              ,parameter
-              ,positionalArg
-              ,star
-              ,parensExpr
-              ,caseExpr
-              ,cast
-              ,convertSqlServer
-              ,arrayCtor
-              ,multisetCtor
-              ,nextValueFor
-              ,subquery
-              ,intervalLit
-              ,specialOpKs
-              ,idenExpr
-              ,odbcExpr]
-       <?> "scalar expression"
+term = expressionLabel $
+    choice
+    [simpleLiteral
+    ,parameter
+    ,positionalArg
+    ,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 +1391,10 @@
 -}
 
 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 +1415,12 @@
 -}
 
 selectItem :: Parser (ScalarExpr,Maybe Name)
-selectItem = (,) <$> scalarExpr <*> optional als
-  where als = optional (keyword_ "as") *> name
+selectItem =
+    label "select item" $ choice
+    [(,Nothing) <$> star
+    ,(,) <$> scalarExpr <*> optional als]
+  where
+    als = label "alias" $ optional (keyword_ "as") *> name "alias"
 
 selectList :: Parser [(ScalarExpr,Maybe Name)]
 selectList = commaSep1 selectItem
@@ -1366,34 +1441,35 @@
 -}
 
 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
-    nonJoinTref = choice
-        [parens $ choice
-             [TRQueryExpr <$> queryExpr
+    tref = nonJoinTref <**> (hidden (chainl tjoin) <|> pure id)
+    nonJoinTref =
+        label "table ref" $ choice
+        [hidden $ parens $ choice
+             -- will be tricky to figure out how to support mixes of nested
+             -- query expr parens and table ref parens
+             [TRQueryExpr <$> queryExprNoParens
              ,TRParens <$> tref]
-        ,TRLateral <$> (keyword_ "lateral"
-                        *> nonJoinTref)
+        ,TRLateral <$> (hidden (keyword_ "lateral") *> nonJoinTref)
         ,do
-         n <- names
-         choice [TRFunction n
-                 <$> parens (commaSep scalarExpr)
+         n <- names "table name"
+         choice [TRFunction n <$> 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 "}")
-        ] <??> aliasSuffix
-    aliasSuffix = fromAlias <$$> TRAlias
-    joinTrefSuffix t =
-        ((TRJoin t <$> option False (True <$ keyword_ "natural")
-                  <*> joinType
-                  <*> nonJoinTref
-                  <*> optional joinCondition)
-        >>= optionSuffix joinTrefSuffix) <?> ""
+        ,TROdbc <$> (hidden (braces (keyword_ "oj" *> tref)))
+        ] <**> (talias <|> pure id)
+    talias = fromAlias <**> pure (flip TRAlias)
+    tjoin =
+        (\jn jt tr1 jc tr0 -> TRJoin tr0 jn jt tr1 jc)
+        <$> option False (True <$ keyword_ "natural")
+        <*> joinType
+        <*> nonJoinTref
+        <*> hoptional joinCondition
+    chainl p = foldr (.) id . reverse <$> some p
 
+
 {-
 TODO: factor the join stuff to produce better error messages (and make
 it more readable)
@@ -1417,13 +1493,13 @@
 joinCondition :: Parser JoinCondition
 joinCondition = choice
     [keyword_ "on" >> JoinOn <$> scalarExpr
-    ,keyword_ "using" >> JoinUsing <$> parens (commaSep1 name)]
+    ,keyword_ "using" >> JoinUsing <$> parens (commaSep1 (name "column name"))]
 
 fromAlias :: Parser Alias
 fromAlias = Alias <$> tableAlias <*> columnAliases
   where
-    tableAlias = optional (keyword_ "as") *> name
-    columnAliases = optional $ parens $ commaSep1 name
+    tableAlias = hoptional (keyword_ "as") *> name "alias"
+    columnAliases = hoptional $ parens $ commaSep1 (name "column name")
 
 {-
 == simple other parts
@@ -1433,12 +1509,15 @@
 -}
 
 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
+    groupingExpression =
+      label "grouping expression" $
+      choice
       [keyword_ "cube" >>
        Cube <$> parens (commaSep groupingExpression)
       ,keyword_ "rollup" >>
@@ -1450,16 +1529,16 @@
       ]
 
 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 +1556,9 @@
     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 +1575,13 @@
 
 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
+    withAlias = Alias <$> name "alias" <*> columnAliases
+    columnAliases = hoptional $ parens $ commaSep1 (name "column alias")
 
 
 {-
@@ -1513,36 +1592,46 @@
 -}
 
 queryExpr :: Parser QueryExpr
-queryExpr = E.makeExprParser qeterm qeOpTable
+queryExpr = queryExpr' True
+queryExprNoParens :: Parser QueryExpr
+queryExprNoParens = queryExpr' False
+
+queryExpr' :: Bool -> Parser QueryExpr
+queryExpr' allowParens = label "query expr" $ E.makeExprParser qeterm qeOpTable
   where
-    qeterm = with <|> select <|> table <|> values
-    
+    qeterm
+        | allowParens =
+              label "query expr" (with <|> select <|> table <|> values <|> qeParens)
+        | otherwise =
+              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)) =
         Select d sl f w g h od ofs fe
     values = keyword_ "values"
              >> Values <$> commaSep (parens (commaSep scalarExpr))
-    table = keyword_ "table" >> Table <$> names
+    table = keyword_ "table" >> Table <$> names "table name"
+    qeParens = QueryExprParens <$> parens queryExpr
 
     qeOpTable =
         [[E.InfixL $ setOp Intersect "intersect"]
         ,[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")
 
-    
+
 {-
 local data type to help with parsing the bit after the select list,
 called 'table expression' in the ansi sql grammar. Maybe this should
@@ -1560,12 +1649,15 @@
       ,_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 +1681,8 @@
 -}
 
 statementWithoutSemicolon :: Parser Statement
-statementWithoutSemicolon = choice
+statementWithoutSemicolon =
+    label "statement" $ choice
     [keyword_ "create" *> choice [createSchema
                                  ,createTable
                                  ,createIndex
@@ -1623,78 +1716,71 @@
     ]
 
 statement :: Parser Statement
-statement = statementWithoutSemicolon <* optional semi <|> EmptyStatement <$ semi
+statement = statementWithoutSemicolon <* optional semi <|> EmptyStatement <$ hidden semi
 
 createSchema :: Parser Statement
 createSchema = keyword_ "schema" >>
-    CreateSchema <$> names
+    CreateSchema <$> names "schema name"
 
 createTable :: Parser Statement
-createTable = do 
+createTable = do
   d <- askDialect id
-  let 
-    parseColumnDef = TableColumnDef <$> columnDef 
+  let
+    parseColumnDef = TableColumnDef <$> columnDef
     parseConstraintDef = uncurry TableConstraintDef <$> tableConstraintDef
     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
+    withoutRowid = if diWithoutRowidTables d
+      then fromMaybe False <$> optional (keywords_ ["without", "rowid"] >> pure True)
+      else pure False
 
   keyword_ "table" >>
     CreateTable
-    <$> names 
+    <$> names  "table name"
     <*> parens entries
+    <*> withoutRowid
 
 createIndex :: Parser Statement
 createIndex =
     CreateIndex
     <$> ((keyword_ "index" >> pure False) <|>
          (keywords_ ["unique", "index"] >> pure True))
-    <*> names
-    <*> (keyword_ "on" >> names)
-    <*> parens (commaSep1 name)
+    <*> names "index name"
+    <*> (keyword_ "on" >> names "table name")
+    <*> parens (commaSep1 (name "column name"))
 
 columnDef :: Parser ColumnDef
-columnDef = ColumnDef <$> name <*> typeName
-            <*> optional defaultClause
-            <*> option [] (some colConstraintDef)
-  where
-    defaultClause = choice [
-        keyword_ "default" >>
-        DefaultClause <$> scalarExpr
-        -- todo: left factor
-       ,try (keywords_ ["generated","always","as"] >>
-             GenerationClause <$> parens scalarExpr)
-       ,keyword_ "generated" >>
-        IdentityColumnSpec
-        <$> (GeneratedAlways <$ keyword_ "always"
-             <|> GeneratedByDefault <$ keywords_ ["by", "default"])
-        <*> (keywords_ ["as", "identity"] *>
-             option [] (parens sequenceGeneratorOptions))
-       ]
+columnDef = do
+  optionalType <- askDialect diOptionalColumnTypes
+  ColumnDef <$> name "column name"
+    <*> (if optionalType then optional typeName else Just <$> typeName)
+    <*> option [] (some colConstraintDef)
 
 tableConstraintDef :: Parser (Maybe [Name], TableConstraint)
 tableConstraintDef =
+    label "table constraint" $
     (,)
-    <$> optional (keyword_ "constraint" *> names)
+    <$> optional (keyword_ "constraint" *> names "constraint name")
     <*> (unique <|> primaryKey <|> check <|> references)
   where
     unique = keyword_ "unique" >>
-        TableUniqueConstraint <$> parens (commaSep1 name)
+        TableUniqueConstraint <$> parens (commaSep1 $ name "column name")
     primaryKey = keywords_ ["primary", "key"] >>
-        TablePrimaryKeyConstraint <$> parens (commaSep1 name)
+        TablePrimaryKeyConstraint <$> parens (commaSep1 $ name "column name")
     check = keyword_ "check" >> TableCheckConstraint <$> parens scalarExpr
     references = keywords_ ["foreign", "key"] >>
         (\cs ft ftcs m (u,d) -> TableReferencesConstraint cs ft ftcs m u d)
-        <$> parens (commaSep1 name)
-        <*> (keyword_ "references" *> names)
-        <*> optional (parens $ commaSep1 name)
+        <$> parens (commaSep1 $ name "column name")
+        <*> (keyword_ "references" *> names "table name")
+        <*> hoptional (parens $ commaSep1 $ name "column name")
         <*> refMatch
         <*> refActions
 
 refMatch :: Parser ReferenceMatch
-refMatch = option DefaultReferenceMatch
+refMatch = hoption DefaultReferenceMatch
             (keyword_ "match" *>
              choice [MatchFull <$ keyword_ "full"
                     ,MatchPartial <$ keyword_ "partial"
@@ -1719,26 +1805,47 @@
 colConstraintDef :: Parser ColConstraintDef
 colConstraintDef =
     ColConstraintDef
-    <$> optional (keyword_ "constraint" *> names)
-    <*> (nullable <|> notNull <|> unique <|> primaryKey <|> check <|> references)
+    <$> optional (keyword_ "constraint" *> names "constraint name")
+    <*> (nullable
+          <|> notNull
+          <|> unique
+          <|> primaryKey
+          <|> check
+          <|> references
+          <|> defaultClause
+        )
   where
     nullable = ColNullableConstraint <$ keyword "null"
     notNull = ColNotNullConstraint <$ keywords_ ["not", "null"]
     unique = ColUniqueConstraint <$ keyword_ "unique"
     primaryKey = do
-      keywords_ ["primary", "key"] 
+      keywords_ ["primary", "key"]
       d <- askDialect id
-      autoincrement <- if diAutoincrement d 
+      autoincrement <- if diAutoincrement d
         then optional (keyword_ "autoincrement")
         else pure Nothing
       pure $ ColPrimaryKeyConstraint $ isJust autoincrement
     check = keyword_ "check" >> ColCheckConstraint <$> parens scalarExpr
     references = keyword_ "references" >>
         (\t c m (ou,od) -> ColReferencesConstraint t c m ou od)
-        <$> names
-        <*> optional (parens name)
+        <$> names "table name"
+        <*> optional (parens $ name "column name")
         <*> refMatch
         <*> refActions
+    defaultClause = label "column default clause" $
+        ColDefaultClause <$> choice
+          [keyword_ "default"
+             >> DefaultClause <$> scalarExpr
+          -- todo: left factor
+          ,try (keywords_ ["generated","always","as"] >>
+             GenerationClause <$> parens scalarExpr)
+          ,keyword_ "generated" >>
+            IdentityColumnSpec
+            <$> (GeneratedAlways <$ keyword_ "always"
+                <|> GeneratedByDefault <$ keywords_ ["by", "default"])
+            <*> (keywords_ ["as", "identity"] *>
+                option [] (parens sequenceGeneratorOptions))
+          ]
 
 -- slightly hacky parser for signed integers
 
@@ -1788,56 +1895,58 @@
 alterTable :: Parser Statement
 alterTable = keyword_ "table" >>
     -- the choices have been ordered so that it works
-    AlterTable <$> names <*> choice [addConstraint
-                                    ,dropConstraint
-                                    ,addColumnDef
-                                    ,alterColumn
-                                    ,dropColumn
-                                    ]
+    AlterTable <$> names "table name"
+    <*> choice [addConstraint
+               ,dropConstraint
+               ,addColumnDef
+               ,alterColumn
+               ,dropColumn
+               ]
   where
     addColumnDef = try (keyword_ "add"
                         *> optional (keyword_ "column")) >>
                    AddColumnDef <$> columnDef
     alterColumn = keyword_ "alter" >> optional (keyword_ "column") >>
-                  name <**> choice [setDefault
-                                   ,dropDefault
-                                   ,setNotNull
-                                   ,dropNotNull
-                                   ,setDataType]
+                  name "column name"
+                  <**> choice [setDefault
+                              ,dropDefault
+                              ,setNotNull
+                              ,dropNotNull
+                              ,setDataType]
     setDefault :: Parser (Name -> AlterTableAction)
     -- todo: left factor
     setDefault = try (keywords_ ["set","default"]) >>
-                 scalarExpr <$$> AlterColumnSetDefault
+                 scalarExpr <**> pure (flip AlterColumnSetDefault)
     dropDefault = AlterColumnDropDefault <$ try (keywords_ ["drop","default"])
     setNotNull = AlterColumnSetNotNull <$ try (keywords_ ["set","not","null"])
     dropNotNull = AlterColumnDropNotNull <$ try (keywords_ ["drop","not","null"])
     setDataType = try (keywords_ ["set","data","type"]) >>
-                  typeName <$$> AlterColumnSetDataType
+                  typeName <**> pure (flip AlterColumnSetDataType)
     dropColumn = try (keyword_ "drop" *> optional (keyword_ "column")) >>
-                 DropColumn <$> name <*> dropBehaviour
+                 DropColumn <$> name "column name" <*> dropBehaviour
     -- todo: left factor, this try is especially bad
     addConstraint = try (keyword_ "add" >>
         uncurry AddTableConstraintDef <$> tableConstraintDef)
     dropConstraint = try (keywords_ ["drop","constraint"]) >>
-        DropTableConstraintDef <$> names <*> dropBehaviour
+        DropTableConstraintDef <$> names "constraint name" <*> dropBehaviour
 
 
 dropSchema :: Parser Statement
 dropSchema = keyword_ "schema" >>
-    DropSchema <$> names <*> dropBehaviour
+    DropSchema <$> names "schema name" <*> dropBehaviour
 
 dropTable :: Parser Statement
 dropTable = keyword_ "table" >>
-    DropTable <$> names <*> dropBehaviour
+    DropTable <$> names "table name" <*> dropBehaviour
 
 createView :: Parser Statement
 createView =
     CreateView
-    <$> (option False (True <$ keyword_ "recursive") <* keyword_ "view")
-    <*> names
-    <*> optional (parens (commaSep1 name))
+    <$> (hoption False (True <$ keyword_ "recursive") <* keyword_ "view")
+    <*> names "view name"
+    <*> optional (parens (commaSep1 $ name "column name"))
     <*> (keyword_ "as" *> queryExpr)
-    <*> optional (choice [
+    <*> hoptional (choice [
             -- todo: left factor
             DefaultCheckOption <$ try (keywords_ ["with", "check", "option"])
            ,CascadedCheckOption <$ try (keywords_ ["with", "cascaded", "check", "option"])
@@ -1846,64 +1955,64 @@
 
 dropView :: Parser Statement
 dropView = keyword_ "view" >>
-    DropView <$> names <*> dropBehaviour
+    DropView <$> names "view name" <*> dropBehaviour
 
 createDomain :: Parser Statement
 createDomain = keyword_ "domain" >>
     CreateDomain
-    <$> names
-    <*> (optional (keyword_ "as") *> typeName)
+    <$> names "domain name"
+    <*> ((optional (keyword_ "as") *> typeName) <?> "alias")
     <*> optional (keyword_ "default" *> scalarExpr)
     <*> many con
   where
-    con = (,) <$> optional (keyword_ "constraint" *> names)
+    con = (,) <$> optional (keyword_ "constraint" *> names "constraint name")
           <*> (keyword_ "check" *> parens scalarExpr)
 
 alterDomain :: Parser Statement
 alterDomain = keyword_ "domain" >>
     AlterDomain
-    <$> names
+    <$> names "domain name"
     <*> (setDefault <|> constraint
          <|> (keyword_ "drop" *> (dropDefault <|> dropConstraint)))
   where
     setDefault = keywords_ ["set", "default"] >> ADSetDefault <$> scalarExpr
     constraint = keyword_ "add" >>
        ADAddConstraint
-       <$> optional (keyword_ "constraint" *> names)
+       <$> optional (keyword_ "constraint" *> names "constraint name")
        <*> (keyword_ "check" *> parens scalarExpr)
     dropDefault = ADDropDefault <$ keyword_ "default"
-    dropConstraint = keyword_ "constraint" >> ADDropConstraint <$> names
+    dropConstraint = keyword_ "constraint" >> ADDropConstraint <$> names "constraint name"
 
 dropDomain :: Parser Statement
 dropDomain = keyword_ "domain" >>
-    DropDomain <$> names <*> dropBehaviour
+    DropDomain <$> names "domain name" <*> dropBehaviour
 
 createSequence :: Parser Statement
 createSequence = keyword_ "sequence" >>
     CreateSequence
-    <$> names
+    <$> names "sequence name"
     <*> sequenceGeneratorOptions
 
 alterSequence :: Parser Statement
 alterSequence = keyword_ "sequence" >>
     AlterSequence
-    <$> names
+    <$> names "sequence name"
     <*> sequenceGeneratorOptions
 
 dropSequence :: Parser Statement
 dropSequence = keyword_ "sequence" >>
-    DropSequence <$> names <*> dropBehaviour
+    DropSequence <$> names "sequence name" <*> dropBehaviour
 
 createAssertion :: Parser Statement
 createAssertion = keyword_ "assertion" >>
     CreateAssertion
-    <$> names
+    <$> names "assertion name"
     <*> (keyword_ "check" *> parens scalarExpr)
 
 
 dropAssertion :: Parser Statement
 dropAssertion = keyword_ "assertion" >>
-    DropAssertion <$> names <*> dropBehaviour
+    DropAssertion <$> names "assertion name" <*> dropBehaviour
 
 {-
 -----------------
@@ -1914,40 +2023,42 @@
 delete :: Parser Statement
 delete = keywords_ ["delete","from"] >>
     Delete
-    <$> names
-    <*> optional (optional (keyword_ "as") *> name)
+    <$> names "table name"
+    <*> optional (hoptional (keyword_ "as") *> name "alias")
     <*> optional (keyword_ "where" *> scalarExpr)
 
 truncateSt :: Parser Statement
 truncateSt = keywords_ ["truncate", "table"] >>
     Truncate
-    <$> names
-    <*> option DefaultIdentityRestart
+    <$> names "table name"
+    <*> hoption DefaultIdentityRestart
         (ContinueIdentity <$ keywords_ ["continue","identity"]
          <|> RestartIdentity <$ keywords_ ["restart","identity"])
 
 insert :: Parser Statement
 insert = keywords_ ["insert", "into"] >>
     Insert
-    <$> names
-    <*> optional (parens $ commaSep1 name)
-    <*> (DefaultInsertValues <$ keywords_ ["default", "values"]
+    <$> names "table name"
+    <*> (hoptional (parens $ commaSep1 $ name "column name"))
+    <*>
+        -- slight hack
+        (DefaultInsertValues <$ label "values" (keywords_ ["default", "values"])
          <|> InsertQuery <$> queryExpr)
 
 update :: Parser Statement
 update = keywords_ ["update"] >>
     Update
-    <$> names
-    <*> optional (optional (keyword_ "as") *> name)
+    <$> names "table name"
+    <*> label "alias" (optional (optional (keyword_ "as") *> name "alias"))
     <*> (keyword_ "set" *> commaSep1 setClause)
     <*> optional (keyword_ "where" *> scalarExpr)
   where
-    setClause = multipleSet <|> singleSet
+    setClause = label "set clause" (multipleSet <|> singleSet)
     multipleSet = SetMultiple
-                  <$> parens (commaSep1 names)
+                  <$> parens (commaSep1 $ names "column name")
                   <*> (symbol "=" *> parens (commaSep1 scalarExpr))
     singleSet = Set
-                <$> names
+                <$> names "column name"
                 <*> (symbol "=" *> scalarExpr)
 
 dropBehaviour :: Parser DropBehaviour
@@ -1967,18 +2078,18 @@
 
 savepoint :: Parser Statement
 savepoint = keyword_ "savepoint" >>
-    Savepoint <$> name
+    Savepoint <$> name "savepoint name"
 
 releaseSavepoint :: Parser Statement
 releaseSavepoint = keywords_ ["release","savepoint"] >>
-    ReleaseSavepoint <$> name
+    ReleaseSavepoint <$> name "savepoint 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 <$> optional (keywords_ ["to", "savepoint"] *> name)
+rollback = keyword_ "rollback" >> hoptional (keyword_ "work") >>
+    Rollback <$> optional (keywords_ ["to", "savepoint"] *> name "savepoint name")
 
 
 {-
@@ -1995,22 +2106,22 @@
     priv = GrantPrivilege
            <$> commaSep privilegeAction
            <*> (keyword_ "on" *> privilegeObject)
-           <*> (keyword_ "to" *> commaSep name)
+           <*> (keyword_ "to" *> commaSep (name "role name"))
            <*> option WithoutGrantOption
                (WithGrantOption <$ keywords_ ["with","grant","option"])
     role = GrantRole
-           <$> commaSep name
-           <*> (keyword_ "to" *> commaSep name)
+           <$> commaSep (name "role name")
+           <*> (keyword_ "to" *> commaSep (name "role name"))
            <*> option WithoutAdminOption
                (WithAdminOption <$ keywords_ ["with","admin","option"])
 
 createRole :: Parser Statement
 createRole = keyword_ "role" >>
-    CreateRole <$> name
+    CreateRole <$> name "role name"
 
 dropRole :: Parser Statement
 dropRole = keyword_ "role" >>
-    DropRole <$> name
+    DropRole <$> name "role name"
 
 -- TODO: fix try at the 'on'
 
@@ -2022,39 +2133,39 @@
                (GrantOptionFor <$ keywords_ ["grant","option","for"])
            <*> commaSep privilegeAction
            <*> (keyword_ "on" *> privilegeObject)
-           <*> (keyword_ "from" *> commaSep name)
+           <*> (keyword_ "from" *> commaSep (name "role name"))
            <*> dropBehaviour
     role = RevokeRole
            <$> option NoAdminOptionFor
                (AdminOptionFor <$ keywords_ ["admin","option", "for"])
-           <*> commaSep name
-           <*> (keyword_ "from" *> commaSep name)
+           <*> commaSep (name "role name")
+           <*> (keyword_ "from" *> commaSep (name "role name"))
            <*> dropBehaviour
 
 privilegeAction :: Parser PrivilegeAction
 privilegeAction = choice
     [PrivAll <$ keywords_ ["all","privileges"]
     ,keyword_ "select" >>
-     PrivSelect <$> option [] (parens $ commaSep name)
+     PrivSelect <$> option [] (parens $ commaSep $ name "column name")
     ,PrivDelete <$ keyword_ "delete"
     ,PrivUsage <$ keyword_ "usage"
     ,PrivTrigger <$ keyword_ "trigger"
     ,PrivExecute <$ keyword_ "execute"
     ,keyword_ "insert" >>
-     PrivInsert <$> option [] (parens $ commaSep name)
+     PrivInsert <$> option [] (parens $ commaSep $ name "column name")
     ,keyword_ "update" >>
-     PrivUpdate <$> option [] (parens $ commaSep name)
+     PrivUpdate <$> option [] (parens $ commaSep $ name "column name")
     ,keyword_ "references" >>
-     PrivReferences <$> option [] (parens $ commaSep name)
+     PrivReferences <$> option [] (parens $ commaSep $ name "column name")
     ]
 
 privilegeObject :: Parser PrivilegeObject
 privilegeObject = choice
-    [keyword_ "domain" >> PrivDomain <$> names
-    ,keyword_ "type" >> PrivType <$> names
-    ,keyword_ "sequence" >> PrivSequence <$> names
-    ,keywords_ ["specific","function"] >> PrivFunction <$> names
-    ,optional (keyword_ "table") >> PrivTable <$> names
+    [keyword_ "domain" >> PrivDomain <$> names "domain name"
+    ,keyword_ "type" >> PrivType <$> names "type name"
+    ,keyword_ "sequence" >> PrivSequence <$> names "sequence name"
+    ,keywords_ ["specific","function"] >> PrivFunction <$> names "function name"
+    ,optional (keyword_ "table") >> PrivTable <$> names "table name"
     ]
 
 
@@ -2091,6 +2202,7 @@
 
 makeKeywordTree :: [Text] -> Parser [Text]
 makeKeywordTree sets =
+    label (T.intercalate ", " sets) $
     parseTrees (sort $ map T.words sets)
   where
     parseTrees :: [[Text]] -> Parser [Text]
@@ -2116,40 +2228,24 @@
 
 -- parser helpers
 
-(<$$>) :: Applicative f =>
-      f b -> (a -> b -> c) -> f (a -> c)
-(<$$>) pa c = pa <**> pure (flip c)
-
-(<$$$>) :: Applicative f =>
-          f c -> (a -> b -> c -> t) -> f (b -> a -> t)
-p <$$$> c = p <**> pure (flip3 c)
-
-(<$$$$>) :: Applicative f =>
-          f d -> (a -> b -> c -> d -> t) -> f (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)
-p <$$$$$> c = p <**> pure (flip5 c)
-
-optionSuffix :: (a -> Parser a) -> a -> Parser a
-optionSuffix p a = option a (p a)
-
 {-
 parses an optional postfix element and applies its result to its left
 hand result, taken from uu-parsinglib
 
 TODO: make sure the precedence higher than <|> and lower than the
 other operators so it can be used nicely
+
+TODO: this name is not so good because it's similar to <?> which does
+something completely different
 -}
 
 (<??>) :: 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)
+chainrSuffix :: Parser a -> Parser (a -> a) -> Parser a
+chainrSuffix p q = foldr ($) <$> p <*> (reverse <$> many (hidden q))
 
 {-
 These are to help with left factored parsers:
@@ -2177,7 +2273,7 @@
 -- 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 +2281,39 @@
 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,8 +2322,20 @@
 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)
+
 ------------------------------------------------------------------------------
 
 -- interfacing with the lexing
@@ -2262,6 +2365,8 @@
 and it will parse as a single string
 
 It is only allowed when all the strings are quoted with ' atm.
+
+TODO: move this to the lexer?
 -}
 
 stringTokExtend :: Parser (Text,Text,Text)
@@ -2277,25 +2382,25 @@
         ]
 
 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 +2408,9 @@
             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,22 +2449,31 @@
 -}
 
 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
+
+
+unexpectedKeywordError :: Text -> Parser a
+unexpectedKeywordError kw =
+    failure (Just $ Label (NE.fromList $ T.unpack $ "keyword " <> kw)) Set.empty
+
+failOnKeyword :: Parser a
+failOnKeyword = do
+    kws <- asks diKeywords
+    i <- lookAhead $ keywordTok kws
+    unexpectedKeywordError i
 
 ------------------------------------------------------------------------------
 
diff --git a/Language/SQL/SimpleSQL/Pretty.hs b/Language/SQL/SimpleSQL/Pretty.hs
--- a/Language/SQL/SimpleSQL/Pretty.hs
+++ b/Language/SQL/SimpleSQL/Pretty.hs
@@ -87,6 +87,7 @@
     <+> me (\x -> pretty "to" <+> intervalTypeField x) t
 scalarExpr _ (Iden i) = names i
 scalarExpr _ Star = pretty "*"
+scalarExpr _ (QStar nms) = names nms <> pretty ".*"
 scalarExpr _ Parameter = pretty "?"
 scalarExpr _ (PositionalArg n) = pretty $ T.cons '$' $ showText n
 scalarExpr _ (HostParameter p i) =
@@ -405,6 +406,7 @@
     pretty "values"
     <+> nest 7 (commaSep (map (parens . commaSep . map (scalarExpr d)) vs))
 queryExpr _ (Table t) = pretty "table" <+> names t
+queryExpr d (QueryExprParens qe) = parens (queryExpr d qe)
 queryExpr d (QEComment cmt v) =
     vsep $ map comment cmt <> [queryExpr d v]
 
@@ -490,9 +492,10 @@
 statement _ (CreateSchema nm) =
     pretty "create" <+> pretty "schema" <+> names nm
 
-statement d (CreateTable nm cds) =
+statement d (CreateTable nm cds withoutRowid) =
     pretty "create" <+> pretty "table" <+> names nm
     <+> parens (commaSep $ map cd cds)
+    <+> (if withoutRowid then texts [ "without", "rowid" ] else mempty)
   where
     cd (TableConstraintDef n con) =
         maybe mempty (\s -> pretty "constraint" <+> names s) n
@@ -695,25 +698,9 @@
 dropBehav Cascade = pretty "cascade"
 dropBehav Restrict = pretty "restrict"
 
-
 columnDef :: Dialect -> ColumnDef -> Doc a
-columnDef d (ColumnDef n t mdef cons) =
-      name n <+> typeName t
-      <+> case mdef of
-             Nothing -> mempty
-             Just (DefaultClause def) ->
-                 pretty "default" <+> scalarExpr d def
-             Just (GenerationClause e) ->
-                 texts ["generated","always","as"] <+> parens (scalarExpr d e)
-             Just (IdentityColumnSpec w o) ->
-                 pretty "generated"
-                 <+> (case w of
-                         GeneratedAlways -> pretty "always"
-                         GeneratedByDefault -> pretty "by" <+> pretty "default")
-                 <+> pretty "as" <+> pretty "identity"
-                 <+> (case o of
-                         [] -> mempty
-                         os -> parens (sep $ map sequenceGeneratorOption os))
+columnDef d (ColumnDef n t cons) =
+      name n <+> maybe mempty typeName t
       <+> sep (map cdef cons)
   where
     cdef (ColConstraintDef cnm con) =
@@ -722,7 +709,7 @@
     pcon ColNotNullConstraint = texts ["not","null"]
     pcon ColNullableConstraint = texts ["null"]
     pcon ColUniqueConstraint = pretty "unique"
-    pcon (ColPrimaryKeyConstraint autoincrement) = 
+    pcon (ColPrimaryKeyConstraint autoincrement) =
       texts $ ["primary","key"] <> ["autoincrement"|autoincrement]
     --pcon ColPrimaryKeyConstraint = texts ["primary","key"]
     pcon (ColCheckConstraint v) = pretty "check" <+> parens (scalarExpr d v)
@@ -733,6 +720,20 @@
         <+> refMatch m
         <+> refAct "update" u
         <+> refAct "delete" del
+    pcon (ColDefaultClause clause) = case clause of
+        DefaultClause def ->
+            pretty "default" <+> scalarExpr d def
+        GenerationClause e ->
+            texts ["generated","always","as"] <+> parens (scalarExpr d e)
+        IdentityColumnSpec w o ->
+            pretty "generated"
+            <+> (case w of
+                    GeneratedAlways -> pretty "always"
+                    GeneratedByDefault -> pretty "by" <+> pretty "default")
+            <+> pretty "as" <+> pretty "identity"
+            <+> (case o of
+                    [] -> mempty
+                    os -> parens (sep $ map sequenceGeneratorOption os))
 
 sequenceGeneratorOption :: SequenceGeneratorOption -> Doc a
 sequenceGeneratorOption (SGODataType t) =
diff --git a/Language/SQL/SimpleSQL/Syntax.hs b/Language/SQL/SimpleSQL/Syntax.hs
--- a/Language/SQL/SimpleSQL/Syntax.hs
+++ b/Language/SQL/SimpleSQL/Syntax.hs
@@ -105,8 +105,10 @@
 
       -- | identifier with parts separated by dots
     | Iden [Name]
-      -- | star, as in select *, t.*, count(*)
+      -- | star, as in select *, count(*)
     | Star
+      -- | qualified star, as in a.*, b.c.*
+    | QStar [Name]
 
     | Parameter -- ^ Represents a ? in a parameterized query
     | PositionalArg Int -- ^ Represents an e.g. $1 in a parameterized query
@@ -371,6 +373,7 @@
       ,qeQueryExpression :: QueryExpr}
     | Values [[ScalarExpr]]
     | Table [Name]
+    | QueryExprParens QueryExpr
     | QEComment [Comment] QueryExpr
       deriving (Eq,Show,Read,Data,Typeable)
 
@@ -441,7 +444,7 @@
     -- ddl
     CreateSchema [Name]
   | DropSchema [Name] DropBehaviour
-  | CreateTable [Name] [TableElement]
+  | CreateTable [Name] [TableElement] Bool
   | AlterTable [Name] AlterTableAction
   | DropTable [Name] DropBehaviour
   | CreateIndex Bool [Name] [Name] [Name]
@@ -551,8 +554,7 @@
   | TableConstraintDef (Maybe [Name]) TableConstraint
     deriving (Eq,Show,Read,Data,Typeable)
 
-data ColumnDef = ColumnDef Name TypeName
-       (Maybe DefaultClause)
+data ColumnDef = ColumnDef Name (Maybe TypeName)
        [ColConstraintDef]
        -- (Maybe CollateClause)
     deriving (Eq,Show,Read,Data,Typeable)
@@ -574,6 +576,7 @@
        ReferentialAction
        ReferentialAction
   | ColCheckConstraint ScalarExpr
+  | ColDefaultClause DefaultClause
     deriving (Eq,Show,Read,Data,Typeable)
 
 data TableConstraint =
diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,7 +1,18 @@
+0.8.0   lexer has new option to output an invalid token on some kinds of
+          parse errors
+        switch tests to hspec
+        improve parse error messages
+	* and x.* changed to only parse in some expression contexts -
+  	  select items and function application argument lists
+	support sqlite 'without rowid'
+	make types in columndefs optional
+	allow column constraints and defaults to be in arbitrary order
+	partially support parentheses at query expression level (some nested parens don't parse yet)
 0.7.1   fix error message source quoting
 0.7.0   support autoincrement for sqlite
 	support table constraints without separating comma for sqlite
 	switch source from literate to regular haskell
+	use megaparsec instead of parsec
 	use prettyprinter lib instead of pretty
 	parsing nested block comments regressed - post a bug if you need this
 	fixed fixity parsing of union, except and intersect (matches postgres docs now)
diff --git a/examples/SimpleSQLParserTool.hs b/examples/SimpleSQLParserTool.hs
--- a/examples/SimpleSQLParserTool.hs
+++ b/examples/SimpleSQLParserTool.hs
@@ -38,7 +38,8 @@
     args <- getArgs
     case args of
         [] -> do
-              showHelp $ Just "no command given"
+              -- exit with 0 in this case
+              showHelp Nothing --  $ Just "no command given"
         (c:as) -> do
              let cmd = lookup c commands
              maybe (showHelp (Just "command not recognised"))
@@ -89,7 +90,7 @@
       (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)
   )
 
 
diff --git a/simple-sql-parser.cabal b/simple-sql-parser.cabal
--- a/simple-sql-parser.cabal
+++ b/simple-sql-parser.cabal
@@ -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
@@ -38,9 +38,7 @@
                        prettyprinter >= 1.7 && < 1.8,
                        text >= 2.0 && < 2.2,
                        containers >= 0.6 && < 0.8
-
   ghc-options:         -Wall
-
   
 library
   import:              shared-properties
@@ -56,8 +54,13 @@
   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,
+                       hspec-golden,
+                       filepath,
+                       pretty-show,
 
   Other-Modules:       Language.SQL.SimpleSQL.ErrorMessages,
                        Language.SQL.SimpleSQL.FullQueries,
@@ -68,6 +71,7 @@
                        Language.SQL.SimpleSQL.Oracle,
                        Language.SQL.SimpleSQL.QueryExprComponents,
                        Language.SQL.SimpleSQL.QueryExprs,
+                       Language.SQL.SimpleSQL.QueryExprParens,
                        Language.SQL.SimpleSQL.SQL2011Queries,
                        Language.SQL.SimpleSQL.SQL2011AccessControl,
                        Language.SQL.SimpleSQL.SQL2011Bits,
@@ -82,11 +86,15 @@
                        Language.SQL.SimpleSQL.CustomDialect,
                        Language.SQL.SimpleSQL.EmptyStatement,
                        Language.SQL.SimpleSQL.CreateIndex
+                       Language.SQL.SimpleSQL.Expectations
+                       Language.SQL.SimpleSQL.TestRunners
                        
   ghc-options:         -threaded
 
-executable SimpleSQLParserTool
+-- this is a testing tool, do some dumb stuff to hide the dependencies in hackage
+Test-Suite SimpleSQLParserTool
   import:              shared-properties
+  type:                exitcode-stdio-1.0
   main-is:             SimpleSQLParserTool.hs
   hs-source-dirs:      examples
   Build-Depends:       simple-sql-parser,
@@ -95,3 +103,4 @@
     buildable:         True
   else
     buildable:         False
+
diff --git a/tests/Language/SQL/SimpleSQL/CreateIndex.hs b/tests/Language/SQL/SimpleSQL/CreateIndex.hs
--- a/tests/Language/SQL/SimpleSQL/CreateIndex.hs
+++ b/tests/Language/SQL/SimpleSQL/CreateIndex.hs
@@ -4,15 +4,19 @@
 
 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
diff --git a/tests/Language/SQL/SimpleSQL/CustomDialect.hs b/tests/Language/SQL/SimpleSQL/CustomDialect.hs
--- a/tests/Language/SQL/SimpleSQL/CustomDialect.hs
+++ b/tests/Language/SQL/SimpleSQL/CustomDialect.hs
@@ -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
diff --git a/tests/Language/SQL/SimpleSQL/EmptyStatement.hs b/tests/Language/SQL/SimpleSQL/EmptyStatement.hs
--- a/tests/Language/SQL/SimpleSQL/EmptyStatement.hs
+++ b/tests/Language/SQL/SimpleSQL/EmptyStatement.hs
@@ -3,19 +3,26 @@
 
 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
diff --git a/tests/Language/SQL/SimpleSQL/ErrorMessages.hs b/tests/Language/SQL/SimpleSQL/ErrorMessages.hs
--- a/tests/Language/SQL/SimpleSQL/ErrorMessages.hs
+++ b/tests/Language/SQL/SimpleSQL/ErrorMessages.hs
@@ -1,156 +1,697 @@
 
 {-
-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.
 
-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?
+Quick tests for error messages, all the tests use the entire formatted
+output of parse failures to compare, it's slightly fragile. Most of
+the tests use a huge golden file which contains tons of parse error
+examples.
 
-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
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+module Language.SQL.SimpleSQL.ErrorMessages
+    (errorMessageTests
+    ) where
 
-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.
+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
 
-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.
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.IO as T
+import Test.Hspec.Golden
+    (Golden(..)
+    )
 
-Contexts:
+import qualified Text.RawString.QQ as R
+import System.FilePath ((</>))
+import Text.Show.Pretty (ppShow)
 
-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.
+errorMessageTests :: TestItem
+errorMessageTests = Group "error messages"
+    [gp (parseQueryExpr ansi2011 "" Nothing) prettyError [R.r|
 
-plan:
+select
+a
+from t
+where
+  something
+order by 1,2,3 where
 
-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
+        |]
+        [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 $@
 
-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.
+        |]
+        [R.r|8:16:
+  |
+8 | order by 1,2,3 $@
+  |                ^
+unexpected '$'
+|]
+   ,let fn = "expected-parse-errors"
+        got = generateParseResults parseErrorData
+    in GoldenErrorTest fn parseErrorData $ it "parse error regressions" $ myGolden (T.unpack fn) got
+        ]
+  where
+    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)
 
-Then, create some query expressions to focus on the non value
-expression parts.
--}
+------------------------------------------------------------------------------
 
+-- golden parse error tests
+    
+myGolden :: String -> Text -> Golden Text
+myGolden name actualOutput =
+  Golden {
+    output = actualOutput,
+    encodePretty = show,
+    writeToFile = T.writeFile,
+    readFromFile = T.readFile,
+    goldenFile = name </> "golden",
+    actualFile = Just (name </> "actual"),
+    failFirstTime = False
+  }
 
-module Language.SQL.SimpleSQL.ErrorMessages where
+parseErrorData :: [(Text,Text,Text)]
+parseErrorData =
+    concat
+    [simpleExpressions1
+    ,pgExprs
+    ,sqlServerIden
+    ,mysqliden
+    ,paramvariations
+    ,odbcexpr
+    ,odbcqexpr
+    ,queryExprExamples
+    ,statementParseErrorExamples]
 
-{-import Language.SQL.SimpleSQL.Parser
-import Data.List
-import Text.Groom
+generateParseResults :: [(Text,Text,Text)] -> Text
+generateParseResults dat =
+    let 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
+                        "statement" ->
+                            either prettyError (T.pack . ppShow)
+                            $ parseStatement 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 T.unlines [parser, dialect, src, resadj]
+    in T.unlines $ map testLine dat
 
-valueExpressions :: [String]
-valueExpressions =
-    ["10.."
-    ,"..10"
-    ,"10e1e2"
-    ,"10e--3"
-    ,"1a"
-    ,"1%"
+parseExampleStrings :: Text -> [Text]
+parseExampleStrings = filter (not . T.null) . map T.strip . T.splitOn ";"
 
-    ,"'b'ad'"
-    ,"'bad"
-    ,"bad'"
+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
+;
+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[
+;
+(a
+;
+(
+;
+a >*
+;
+a >* b
+;
+( ( a
+;
+( ( a )
+;
+( ( a  + )
+|]
 
-    ,"interval '5' ay"
-    ,"interval '5' day (4.4)"
-    ,"interval '5' day (a)"
-    ,"intervala '5' day"
-    ,"interval 'x' day (3"
-    ,"interval 'x' day 3)"
+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$
+;
+:::
+;
+|||
+;
+...
+;
 
-    ,"1badiden"
-    ,"$"
-    ,"!"
-    ,"*.a"
+|]
 
-    ,"??"
-    ,"3?"
-    ,"?a"
+sqlServerIden :: [(Text,Text,Text)]
+sqlServerIden = flip map (parseExampleStrings src) $ \e ->
+    ("scalarExpr", "sqlserver", e)
+  where src = [R.r|
+]
+;
+[test
+;
+[]
+;
+[[test]]
 
-    ,"row"
-    ,"row 1,2"
-    ,"row(1,2"
-    ,"row 1,2)"
-    ,"row(1 2)"
+|]
 
-    ,"f("
-    ,"f)"
+mysqliden :: [(Text,Text,Text)]
+mysqliden = flip map (parseExampleStrings src) $ \e ->
+    ("scalarExpr", "mysql", e)
+  where src = [R.r|
+`open
+;
+```
+;
+``
 
-    ,"f(a"
-    ,"f a)"
-    ,"f(a b)"
+|]
 
-{-
-TODO:
-case
-operators
--}
+paramvariations :: [(Text,Text,Text)]
+paramvariations = flip map (parseExampleStrings src) $ \e ->
+    ("scalarExpr", "params", e)
+  where src = [R.r|
+:3
+;
+@3
+;
+#3
 
-   ,"a + (b + c"
+|]
 
-{-
-casts
-subqueries: + whole set of parentheses use
-in list
-'keyword' functions
-aggregates
-window functions
--}
 
+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)
 
-   ]
+|]
 
-queryExpressions :: [String]
-queryExpressions =
-    map sl1 valueExpressions
-    ++ map sl2 valueExpressions
-    ++ map sl3 valueExpressions
-    ++
-    ["select a from t inner jin u"]
-  where
-    sl1 x = "select " ++ x ++ " from t"
-    sl2 x = "select " ++ x ++ ", y from t"
-    sl3 x = "select " ++ x ++ " fom t"
+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
 
-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)
+    
+queryExprExamples :: [(Text,Text,Text)]
+queryExprExamples = 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
+'
+;
+select a as
+;
+select a as from t
+;
+select a as, 
+;
+select a,
+;
+select a, from t
+;
+select a as from
+;
+select a as from from
+;
+select a as from2 from
+;
+select a fromt
+;
+select a b fromt
 
+;
+select a from t u v
+;
+select a from t as
+;
+select a from t, 
+;
+select a from group by b
+;
+select a from t join group by a
+;
+select a from t join
+;
+select a from (@
+;
+select a from ()
+;
+select a from t left join u on
+;
+select a from t left join u on group by a
+;
+select a from t left join u using
+;
+select a from t left join u using (
+;
+select a from t left join u using (a
+;
+select a from t left join u using (a,
+;
+select a from (select a from)
+;
+select a from (select a
 
-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
--}
+;
+select a from t where
+;
+select a from t group by a having b where
+;
+select a from t where (a
+;
+select a from t where group by b
+
+;
+select a from t group by
+;
+select a from t group
+;
+select a from t group by a as
+;
+select a from t group by a,
+;
+select a from t group by order by
+;
+select a <<== b from t
+;
+/*
+;
+select * as a
+;
+select t.* as a
+;
+select 3 + *
+;
+select case when * then 1 end
+;
+select (*)
+;
+select * from (select a
+        from t
+;
+select * from (select a(stuff)
+        from t
+
+;
+select *
+     from (select a,b
+           from t
+           where a = 1
+                 and b > a
+
+;
+select *
+     from (select a,b
+           from t
+           where a = 1
+                 and b > a
+           from t)
+
+|]
+
+
+statementParseErrorExamples :: [(Text,Text,Text)]
+statementParseErrorExamples = flip map (parseExampleStrings src) $ \e ->
+    ("statement", "ansi2011", e)
+  where src = [R.r|
+create
+;
+drop
+;
+delete this
+;
+delete where 7
+;
+delete from where t
+;
+truncate nothing
+;
+truncate nothing nothing
+;
+truncate table from
+;
+truncate table t u
+;
+insert t select u
+;
+insert into t insert
+;
+insert into t (1,2)
+;
+insert into t(
+;
+insert into t(1
+;
+insert into t(a
+;
+insert into t(a,
+;
+insert into t(a,b)
+;
+insert into t(a,b) values
+;
+insert into t(a,b) values (
+;
+insert into t(a,b) values (1
+;
+insert into t(a,b) values (1,
+;
+insert into t(a,b) values (1,2) and stuff
+;
+update set 1
+;
+update t u
+;
+update t u v
+;
+update t set a
+;
+update t set a=
+;
+update t set a=1,
+;
+update t set a=1 where
+;
+update t set a=1 where 1 also
+;
+create table
+;
+create table t (
+  a
+)
+;
+create table t (
+  a
+;
+create table t (
+  a,
+)
+;
+create table t (
+)
+;
+create table t (
+;
+create table t
+;
+create table t. (
+;
+truncate table t.
+;
+drop table t. where
+;
+update t. set
+;
+delete from t. where
+;
+insert into t. values
+;
+with a as (select * from t
+select 1
+;
+with a as (select * from t
+;
+with a as (
+;
+with a (
+;
+with as (select * from t)
+select 1
+;
+with (select * from t) as a
+select 1
+
+
+|]
+
diff --git a/tests/Language/SQL/SimpleSQL/Expectations.hs b/tests/Language/SQL/SimpleSQL/Expectations.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/Expectations.hs
@@ -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
diff --git a/tests/Language/SQL/SimpleSQL/FullQueries.hs b/tests/Language/SQL/SimpleSQL/FullQueries.hs
--- a/tests/Language/SQL/SimpleSQL/FullQueries.hs
+++ b/tests/Language/SQL/SimpleSQL/FullQueries.hs
@@ -6,24 +6,24 @@
 
 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 @@
                                [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
diff --git a/tests/Language/SQL/SimpleSQL/GroupBy.hs b/tests/Language/SQL/SimpleSQL/GroupBy.hs
--- a/tests/Language/SQL/SimpleSQL/GroupBy.hs
+++ b/tests/Language/SQL/SimpleSQL/GroupBy.hs
@@ -6,6 +6,8 @@
 
 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 @@
     ,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 @@
 -}
 
 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 @@
                       ,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 @@
      \(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 @@
      \(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 @@
      \(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 @@
      \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 @@
      \(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 @@
      \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 @@
      \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 @@
      \)\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 @@
      \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 @@
      \(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 @@
     -- 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\
diff --git a/tests/Language/SQL/SimpleSQL/LexerTests.hs b/tests/Language/SQL/SimpleSQL/LexerTests.hs
--- a/tests/Language/SQL/SimpleSQL/LexerTests.hs
+++ b/tests/Language/SQL/SimpleSQL/LexerTests.hs
@@ -23,6 +23,7 @@
     (Token(..)
     ,tokenListWillPrintAndLex
     )
+import Language.SQL.SimpleSQL.TestRunners
 
 import qualified Data.Text as T
 import Data.Text (Text)
@@ -39,50 +40,57 @@
     ,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  "]
+    
+    ,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"]
 
-    ,("1", [SqlNumber "1"])
-    ,("42", [SqlNumber "42"])
+    ,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"]
 
-    -- have to fix the dialect handling in the tests
-    --,("$1", [PositionalArg 1])
-    --,("$200", [PositionalArg 200])
+    ,t "1" [SqlNumber "1"]
+    ,t "42" [SqlNumber "42"]
 
-    ,(":test", [PrefixedVariable ':' "test"])
+    ,tp "$1" [PositionalArg 1]
+    ,tp "$200" [PositionalArg 200]
 
-    ] ++ map (\a -> (a, [Symbol a])) (
+    ,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 @@
        )
     -- 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,40 +145,45 @@
 
 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
 do quickcheck testing:
@@ -303,22 +316,21 @@
        , 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 @@
     ,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 @@
      ,("-/**/", [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 @@
 
 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) ]
+
diff --git a/tests/Language/SQL/SimpleSQL/MySQL.hs b/tests/Language/SQL/SimpleSQL/MySQL.hs
--- a/tests/Language/SQL/SimpleSQL/MySQL.hs
+++ b/tests/Language/SQL/SimpleSQL/MySQL.hs
@@ -6,6 +6,7 @@
 
 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 @@
 -}
 
 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)]
diff --git a/tests/Language/SQL/SimpleSQL/Odbc.hs b/tests/Language/SQL/SimpleSQL/Odbc.hs
--- a/tests/Language/SQL/SimpleSQL/Odbc.hs
+++ b/tests/Language/SQL/SimpleSQL/Odbc.hs
@@ -4,6 +4,8 @@
 
 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 @@
               ,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 @@
                    ,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]
diff --git a/tests/Language/SQL/SimpleSQL/Oracle.hs b/tests/Language/SQL/SimpleSQL/Oracle.hs
--- a/tests/Language/SQL/SimpleSQL/Oracle.hs
+++ b/tests/Language/SQL/SimpleSQL/Oracle.hs
@@ -6,6 +6,7 @@
 
 import Language.SQL.SimpleSQL.TestTypes
 import Language.SQL.SimpleSQL.Syntax
+import Language.SQL.SimpleSQL.TestRunners
 
 oracleTests :: TestItem
 oracleTests = Group "oracle dialect"
@@ -13,18 +14,19 @@
 
 
 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 []]]
-    )
+        (Just (PrecLengthTypeName [Name Nothing "varchar2"] 55 Nothing (Just PrecOctets)))
+        []]
+       False
+    ]
+
diff --git a/tests/Language/SQL/SimpleSQL/Postgres.hs b/tests/Language/SQL/SimpleSQL/Postgres.hs
--- a/tests/Language/SQL/SimpleSQL/Postgres.hs
+++ b/tests/Language/SQL/SimpleSQL/Postgres.hs
@@ -9,9 +9,11 @@
 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 @@
     [-- "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 @@
      \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 @@
      \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 @@
      \)\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 @@
      \)\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 @@
      \)\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 @@
 
 -- 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 @@
      \    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 @@
      \  )\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
diff --git a/tests/Language/SQL/SimpleSQL/QueryExprComponents.hs b/tests/Language/SQL/SimpleSQL/QueryExprComponents.hs
--- a/tests/Language/SQL/SimpleSQL/QueryExprComponents.hs
+++ b/tests/Language/SQL/SimpleSQL/QueryExprComponents.hs
@@ -12,7 +12,8 @@
 
 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 @@
 
 
 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,92 @@
           ,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)]}
+    ,q "select * from t"
+     $ toQueryExpr $ makeSelect {msSelectList = [(Star,Nothing)]
+                                ,msFrom = [TRSimple [Name Nothing "t"]]}
 
+    ,q "select t.* from t"
+     $ toQueryExpr $ makeSelect {msSelectList = [(QStar [Name Nothing "t"],Nothing)]
+                                ,msFrom = [TRSimple [Name Nothing "t"]]}
+
+    ,q "select t.*, a as b, u.* from t"
+     $ toQueryExpr $ makeSelect
+        {msSelectList =
+         [(QStar [Name Nothing "t"],Nothing)
+         ,(Iden [Name Nothing "a"], Just $ Name Nothing "b")
+         ,(QStar [Name Nothing "u"],Nothing)]
+        ,msFrom = [TRSimple [Name Nothing "t"]]}
+    
     ]
 
 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 +138,20 @@
                       ,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 +161,23 @@
              ,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 +189,20 @@
 
 
 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 +213,16 @@
    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
diff --git a/tests/Language/SQL/SimpleSQL/QueryExprParens.hs b/tests/Language/SQL/SimpleSQL/QueryExprParens.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/QueryExprParens.hs
@@ -0,0 +1,47 @@
+
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+module Language.SQL.SimpleSQL.QueryExprParens (queryExprParensTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+import Language.SQL.SimpleSQL.Syntax
+import Language.SQL.SimpleSQL.TestRunners
+import Data.Text (Text)
+import qualified Text.RawString.QQ as R
+
+queryExprParensTests :: TestItem
+queryExprParensTests = Group "query expr parens"
+    [q "(select *  from t)" $ QueryExprParens $ ms "t"
+    ,q "select * from t except (select * from u except select * from v)"
+     $ (ms "t") `sexcept` QueryExprParens (ms "u" `sexcept` ms "v")
+        
+    ,q "(select * from t except select * from u) except select * from v"
+     $ QueryExprParens (ms "t" `sexcept` ms "u") `sexcept` ms "v"
+
+    ,q [R.r|
+select * from t
+union
+with a as (select * from u)
+select * from a
+|]
+     $ ms "t" `sunion` with [("a", ms "u")] (ms "a")
+
+    ,q [R.r|
+select * from t
+union
+(with a as (select * from u)
+ select * from a)
+|]
+     $ ms "t" `sunion` QueryExprParens (with [("a", ms "u")] (ms "a"))
+    ]
+  where
+    q :: HasCallStack => Text -> QueryExpr -> TestItem
+    q src ast = testQueryExpr ansi2011 src ast
+    ms t = toQueryExpr $ makeSelect
+            {msSelectList = [(Star,Nothing)]
+            ,msFrom = [TRSimple [Name Nothing t]]}
+    sexcept = so Except
+    sunion = so Union
+    so op a b = QueryExprSetOp a op SQDefault Respectively b
+    with es s =
+        With False (flip map es $ \(n,sn) -> (Alias (Name Nothing n) Nothing ,sn)) s
diff --git a/tests/Language/SQL/SimpleSQL/QueryExprs.hs b/tests/Language/SQL/SimpleSQL/QueryExprs.hs
--- a/tests/Language/SQL/SimpleSQL/QueryExprs.hs
+++ b/tests/Language/SQL/SimpleSQL/QueryExprs.hs
@@ -9,19 +9,23 @@
 
 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
diff --git a/tests/Language/SQL/SimpleSQL/SQL2011AccessControl.hs b/tests/Language/SQL/SimpleSQL/SQL2011AccessControl.hs
--- a/tests/Language/SQL/SimpleSQL/SQL2011AccessControl.hs
+++ b/tests/Language/SQL/SimpleSQL/SQL2011AccessControl.hs
@@ -11,6 +11,8 @@
 
 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 @@
   | 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 @@
   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 @@
   <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 @@
   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 @@
 -}
 
 
-    ,(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 @@
   <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
diff --git a/tests/Language/SQL/SimpleSQL/SQL2011Bits.hs b/tests/Language/SQL/SimpleSQL/SQL2011Bits.hs
--- a/tests/Language/SQL/SimpleSQL/SQL2011Bits.hs
+++ b/tests/Language/SQL/SimpleSQL/SQL2011Bits.hs
@@ -12,6 +12,8 @@
 
 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,10 +29,8 @@
 BEGIN is not in the standard!
 -}
 
-     (TestStatement ansi2011
-      "start transaction"
-     $ StartTransaction)
-
+     s "start transaction" StartTransaction
+     
 {-
 17.2 <set transaction statement>
 
@@ -84,9 +84,8 @@
   <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 @@
   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 @@
   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 @@
   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 @@
 -}
 
    ]
+
+s :: HasCallStack => Text -> Statement -> TestItem
+s src ast = testStatement ansi2011 src ast
diff --git a/tests/Language/SQL/SimpleSQL/SQL2011DataManipulation.hs b/tests/Language/SQL/SimpleSQL/SQL2011DataManipulation.hs
--- a/tests/Language/SQL/SimpleSQL/SQL2011DataManipulation.hs
+++ b/tests/Language/SQL/SimpleSQL/SQL2011DataManipulation.hs
@@ -7,6 +7,8 @@
 
 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 @@
       [ 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 @@
   | 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 @@
   <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 @@
 -}
 
 
-    ,(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 @@
 
 
    ]
+
+s :: HasCallStack => Text -> Statement -> TestItem
+s src ast = testStatement ansi2011 src ast
diff --git a/tests/Language/SQL/SimpleSQL/SQL2011Queries.hs b/tests/Language/SQL/SimpleSQL/SQL2011Queries.hs
--- a/tests/Language/SQL/SimpleSQL/SQL2011Queries.hs
+++ b/tests/Language/SQL/SimpleSQL/SQL2011Queries.hs
@@ -37,6 +37,7 @@
 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 @@
 
 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 @@
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
                ,"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 @@
 
 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 @@
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 tableReference :: TestItem
 tableReference = Group "table reference"
-    $ map (uncurry (TestQueryExpr ansi2011))
+    $ map (uncurry (testQueryExpr ansi2011))
     [("select * from t", toQueryExpr sel)
 
 {-
@@ -2994,7 +2974,7 @@
 
 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 @@
 
 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 @@
 
 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 @@
 
 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,14 +3277,15 @@
 
 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})
     ,("select * from t", toQueryExpr $ ms {msSelectList = [(Star,Nothing)]})
     ,("select a.* from t"
-     ,toQueryExpr $ ms {msSelectList = [(BinOp (Iden [Name Nothing "a"]) [Name Nothing "."] Star
-                           ,Nothing)]})
+     ,toQueryExpr $ ms {msSelectList =
+                        [(QStar [Name Nothing "a"]
+                         ,Nothing)]})
     ,("select a b from t"
      ,toQueryExpr $ ms {msSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "b")]})
     ,("select a as b from t"
@@ -3369,7 +3350,7 @@
 
 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 +3389,7 @@
 
 explicitTableQueryExpression :: TestItem
 explicitTableQueryExpression= Group "explicit table query expression"
-    $ map (uncurry (TestQueryExpr ansi2011))
+    $ map (uncurry (testQueryExpr ansi2011))
     [("table t", Table [Name Nothing "t"])
     ]
 
@@ -3432,7 +3413,7 @@
 
 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 +3578,7 @@
 
 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 +3796,7 @@
 
 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 +3825,7 @@
 
 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 +3846,7 @@
 
 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 +3886,7 @@
 
 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 +4254,7 @@
 
 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 +4367,7 @@
 
 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 +4464,7 @@
 
 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 +4499,10 @@
     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
diff --git a/tests/Language/SQL/SimpleSQL/SQL2011Schema.hs b/tests/Language/SQL/SimpleSQL/SQL2011Schema.hs
--- a/tests/Language/SQL/SimpleSQL/SQL2011Schema.hs
+++ b/tests/Language/SQL/SimpleSQL/SQL2011Schema.hs
@@ -10,6 +10,8 @@
 
 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 @@
       [ <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 @@
 -}
 
 
-    ,(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,11 @@
       [ 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 "a") (Just (TypeName [Name Nothing "int"])) []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (Just (TypeName [Name Nothing "int"])) []]
+       False
 
 
 {-
@@ -321,35 +324,40 @@
 -}
 
 
-    ,(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]])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing ColNotNullConstraint]]
+       False
 
-    ,(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]])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef (Just [Name Nothing "a_not_null"]) ColNotNullConstraint]]
+       False
 
-    ,(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]])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing ColUniqueConstraint]]
+       False
 
-    ,(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)]])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing (ColPrimaryKeyConstraint False)]]
+       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)]])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing (ColPrimaryKeyConstraint True)]]
+       False
 
 {-
 references t(a,b)
@@ -358,102 +366,114 @@
          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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing DefaultReferenceMatch
-         DefaultReferentialAction DefaultReferentialAction]])
+         DefaultReferentialAction DefaultReferentialAction]]
+       False
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] (Just $ Name Nothing "a") DefaultReferenceMatch
-         DefaultReferentialAction DefaultReferentialAction]])
+         DefaultReferentialAction DefaultReferentialAction]]
+       False
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing MatchFull
-         DefaultReferentialAction DefaultReferentialAction]])
+         DefaultReferentialAction DefaultReferentialAction]]
+       False
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing MatchPartial
-         DefaultReferentialAction DefaultReferentialAction]])
+         DefaultReferentialAction DefaultReferentialAction]]
+       False
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing MatchSimple
-         DefaultReferentialAction DefaultReferentialAction]])
+         DefaultReferentialAction DefaultReferentialAction]]
+       False
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing DefaultReferenceMatch
-         RefCascade DefaultReferentialAction]])
+         RefCascade DefaultReferentialAction]]
+       False
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing DefaultReferenceMatch
-         RefSetNull DefaultReferentialAction]])
+         RefSetNull DefaultReferentialAction]]
+       False
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing DefaultReferenceMatch
-         RefSetDefault DefaultReferentialAction]])
+         RefSetDefault DefaultReferentialAction]]
+       False
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing DefaultReferenceMatch
-         RefNoAction DefaultReferentialAction]])
+         RefNoAction DefaultReferentialAction]]
+       False
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing DefaultReferenceMatch
-         DefaultReferentialAction RefCascade]])
+         DefaultReferentialAction RefCascade]]
+       False
 
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing DefaultReferenceMatch
-         RefCascade RefRestrict]])
+         RefCascade RefRestrict]]
+       False
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing $ ColReferencesConstraint
          [Name Nothing "u"] Nothing DefaultReferenceMatch
-         RefCascade RefRestrict]])
+         RefCascade RefRestrict]]
+       False
 
 {-
 TODO: try combinations and permutations of column constraints and
@@ -461,12 +481,13 @@
 -}
 
 
-    ,(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
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
         [ColConstraintDef Nothing
-         (ColCheckConstraint $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5"))]])
+         (ColCheckConstraint $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5"))]]
+       False
 
 
 
@@ -478,39 +499,51 @@
       [ <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 []) []])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing
+         $ ColDefaultClause
+         $ IdentityColumnSpec GeneratedAlways []]]
+       False
 
-    ,(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 []) []])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing
+         $ ColDefaultClause
+         $ IdentityColumnSpec GeneratedByDefault []]]
+       False
 
 
-    ,(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"]
-       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
-        (Just $ IdentityColumnSpec GeneratedAlways
-         [SGOStartWith 5
-         ,SGOIncrementBy 5
-         ,SGOMaxValue 500
-         ,SGOMinValue 5
-         ,SGOCycle]) []])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing
+         $ ColDefaultClause
+         $ IdentityColumnSpec GeneratedAlways
+          [SGOStartWith 5
+          ,SGOIncrementBy 5
+          ,SGOMaxValue 500
+          ,SGOMinValue 5
+          ,SGOCycle]]]
+       False
 
-    ,(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"]
-       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
-        (Just $ IdentityColumnSpec GeneratedAlways
-         [SGOStartWith (-4)
-         ,SGONoMaxValue
-         ,SGONoMinValue
-         ,SGONoCycle]) []])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing
+         $ ColDefaultClause
+         $ IdentityColumnSpec GeneratedAlways
+          [SGOStartWith (-4)
+          ,SGONoMaxValue
+          ,SGONoMinValue
+          ,SGONoCycle]]]
+       False
 
 {-
 I think <common sequence generator options> is supposed to just
@@ -531,14 +564,16 @@
   <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"))) []])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []
+       ,TableColumnDef $ ColumnDef (Name Nothing "a2") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing
+         $ ColDefaultClause
+         $ GenerationClause (BinOp (Iden [Name Nothing "a"]) [Name Nothing "*"] (NumLit "2"))]]
+       False
 
 
 
@@ -563,10 +598,13 @@
 -}
 
 
-    ,(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") []])
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing
+         $ ColDefaultClause
+         $ DefaultClause $ NumLit "0"]]
+       False
 
 
 
@@ -597,40 +635,44 @@
   <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 []
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []
        ,TableConstraintDef Nothing $ TableUniqueConstraint [Name Nothing "a"]
-        ])
+        ]
+       False
 
-    ,(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 []
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []
        ,TableConstraintDef (Just [Name Nothing "a_unique"]) $
             TableUniqueConstraint [Name Nothing "a"]
-        ])
+        ]
+       False
 
 -- 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 []
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (Just (TypeName [Name Nothing "int"])) []
        ,TableConstraintDef Nothing $
             TableUniqueConstraint [Name Nothing "a", Name Nothing "b"]
-        ])
+        ]
+       False
 
-    ,(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 []
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (Just (TypeName [Name Nothing "int"])) []
        ,TableConstraintDef Nothing $
             TablePrimaryKeyConstraint [Name Nothing "a", Name Nothing "b"]
-        ])
+        ]
+       False
 
 
 {-
@@ -649,40 +691,42 @@
 -}
 
 
-    ,(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"]
-       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
-       ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (Just (TypeName [Name Nothing "int"])) []
        ,TableConstraintDef Nothing $
             TableReferencesConstraint
                 [Name Nothing "a", Name Nothing "b"]
                 [Name Nothing "u"]
                 (Just [Name Nothing "c", Name Nothing "d"])
                 MatchFull RefCascade RefRestrict
-       ])
+       ]
+       False
 
-    ,(TestStatement ansi2011
+    ,s
       "create table t (a int,\n\
       \                constraint tfku1 foreign key (a) references u);"
      $ CreateTable [Name Nothing "t"]
-       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []
        ,TableConstraintDef (Just [Name Nothing "tfku1"]) $
             TableReferencesConstraint
                 [Name Nothing "a"]
                 [Name Nothing "u"]
                 Nothing DefaultReferenceMatch
                 DefaultReferentialAction DefaultReferentialAction
-       ])
+       ]
+       False
 
-    ,(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));"
      $ 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 "a") (Just (TypeName [Name Nothing "int"])) []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (Just (TypeName [Name Nothing "int"])) []
        ,TableConstraintDef Nothing $
             TableReferencesConstraint
                 [Name Nothing "a"]
@@ -697,7 +741,40 @@
                 (Just [Name Nothing "d"])
                 DefaultReferenceMatch
                 DefaultReferentialAction DefaultReferentialAction
-       ])
+       ]
+       False
+    ,testStatement ansi2011{ diWithoutRowidTables = True }
+      "create table t (a int) without rowid;"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []]
+       True
+    ,testStatement ansi2011{ diOptionalColumnTypes = True }
+      "create table t (a,b);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") Nothing []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") Nothing []
+       ]
+       False
+    ,testStatement ansi2011{ diDefaultClausesAsConstraints = True }
+      "create table t (a int default 1 default 2);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing
+         $ ColDefaultClause
+         $ DefaultClause $ NumLit "1"
+        ,ColConstraintDef Nothing
+         $ ColDefaultClause
+         $ DefaultClause $ NumLit "2"]]
+       False
+    ,testStatement ansi2011{ diDefaultClausesAsConstraints = True }
+      "create table t (a int not null default 2);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"]))
+        [ColConstraintDef Nothing ColNotNullConstraint
+        ,ColConstraintDef Nothing
+         $ ColDefaultClause
+         $ DefaultClause $ NumLit "2"]]
+       False
 
 
 {-
@@ -755,28 +832,30 @@
      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"]
-       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
-       ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (Just (TypeName [Name Nothing "int"])) []
        ,TableConstraintDef Nothing $
             TableCheckConstraint
             (BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (Iden [Name Nothing "b"]))
-       ])
+       ]
+       False
 
 
-    ,(TestStatement ansi2011
+    ,s
       "create table t (a int, b int, \n\
       \                constraint agtb check (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 []
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (Just (TypeName [Name Nothing "int"])) []
        ,TableConstraintDef (Just [Name Nothing "agtb"]) $
             TableCheckConstraint
             (BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (Iden [Name Nothing "b"]))
-       ])
+       ]
+       False
 
 
 {-
@@ -810,11 +889,10 @@
 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 []
-       )
+       $ ColumnDef (Name Nothing "a") (Just (TypeName [Name Nothing "int"])) []
 
 {-
 todo: more add column
@@ -844,10 +922,10 @@
 -}
 
 
-    ,(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 +934,9 @@
   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 +946,9 @@
   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 +957,9 @@
   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 +978,10 @@
   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 +1079,20 @@
   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 +1103,17 @@
   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 +1129,15 @@
   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 +1189,13 @@
   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 +1237,51 @@
   <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 +1292,13 @@
 -}
 
 
-    ,(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 +1315,37 @@
       <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 +1367,10 @@
   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 +1380,10 @@
   DROP DEFAULT
 -}
 
-    ,(TestStatement ansi2011
+    ,s
       "alter domain my_int drop default"
      $ AlterDomain [Name Nothing "my_int"]
-       $ ADDropDefault)
+       $ ADDropDefault
 
 
 {-
@@ -1315,17 +1393,17 @@
   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 +1413,10 @@
   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 +1425,13 @@
   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 +1503,7 @@
          [ <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 +1511,7 @@
                  {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 +1520,13 @@
   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 +2163,21 @@
   | 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 +2200,21 @@
   <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 +2224,16 @@
   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
diff --git a/tests/Language/SQL/SimpleSQL/ScalarExprs.hs b/tests/Language/SQL/SimpleSQL/ScalarExprs.hs
--- a/tests/Language/SQL/SimpleSQL/ScalarExprs.hs
+++ b/tests/Language/SQL/SimpleSQL/ScalarExprs.hs
@@ -6,7 +6,10 @@
 
 import Language.SQL.SimpleSQL.TestTypes
 import Language.SQL.SimpleSQL.Syntax
+import Language.SQL.SimpleSQL.TestRunners
 
+import Data.Text (Text)
+
 scalarExprTests :: TestItem
 scalarExprTests = Group "scalarExprTests"
     [literals
@@ -25,101 +28,105 @@
     ,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)
-    --,("t.*", Star2 "t")
-    --,("ROW(t.*,42)", App "ROW" [Star2 "t", NumLit "42"])
+star = Group "star"
+    [t "count(*)" $ App [Name Nothing "count"] [Star]
+    ,t "ROW(t.*,42)" $ App [Name Nothing "ROW"] [QStar [Name Nothing "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 "a.b.c" $ Iden [Name Nothing "a",Name Nothing "b",Name Nothing "c"]
+    ,t "ROW(t.*,42)" $ App [Name Nothing "ROW"] [QStar [Name Nothing "t"], 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 +137,69 @@
     ,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"])
-
-    ,("int '3'"
-     ,TypedLit (TypeName [Name Nothing "int"]) "3")
+casts = Group "operators"
+    [t "cast('1' as int)"
+     $ Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "int"]
 
-    ,("cast('1' as double precision)"
-     ,Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "double precision"])
+    ,t "int '3'"
+     $ TypedLit (TypeName [Name Nothing "int"]) "3"
 
-    ,("cast('1' as float(8))"
-     ,Cast (StringLit "'" "'" "1") $ PrecTypeName [Name Nothing "float"] 8)
+    ,t "cast('1' as double precision)"
+     $ Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "double precision"]
 
-    ,("cast('1' as decimal(15,2))"
-     ,Cast (StringLit "'" "'" "1") $ PrecScaleTypeName [Name Nothing "decimal"] 15 2)
+    ,t "cast('1' as float(8))"
+     $ Cast (StringLit "'" "'" "1") $ PrecTypeName [Name Nothing "float"] 8
 
+    ,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 +208,93 @@
          }
 
 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"])
-
-    ,("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"]))
+miscOps = Group "unaryOperators"
+    [t "a in (1,2,3)"
+     $ In True (Iden [Name Nothing "a"]) $ InList $ map NumLit ["1","2","3"]
 
-    ,("a is not distinct from b"
-     ,BinOp (Iden [Name Nothing "a"]) [Name Nothing "is not 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 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 is not distinct from b"
+     $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "is not distinct from"] (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"]))
+    ,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 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 +304,133 @@
 
 
 
-    ,("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"]]
diff --git a/tests/Language/SQL/SimpleSQL/TableRefs.hs b/tests/Language/SQL/SimpleSQL/TableRefs.hs
--- a/tests/Language/SQL/SimpleSQL/TableRefs.hs
+++ b/tests/Language/SQL/SimpleSQL/TableRefs.hs
@@ -9,100 +9,103 @@
 
 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
diff --git a/tests/Language/SQL/SimpleSQL/TestRunners.hs b/tests/Language/SQL/SimpleSQL/TestRunners.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/TestRunners.hs
@@ -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
+
diff --git a/tests/Language/SQL/SimpleSQL/TestTypes.hs b/tests/Language/SQL/SimpleSQL/TestTypes.hs
--- a/tests/Language/SQL/SimpleSQL/TestTypes.hs
+++ b/tests/Language/SQL/SimpleSQL/TestTypes.hs
@@ -13,6 +13,9 @@
 import Language.SQL.SimpleSQL.Lex (Token)
 import Language.SQL.SimpleSQL.Dialect
 
+import Test.Hspec (SpecWith)
+
+
 import Data.Text (Text)
 
 {-
@@ -20,13 +23,19 @@
 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,14 @@
 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 ())
+              | GoldenErrorTest Text [(Text,Text,Text)] (SpecWith ())
+
diff --git a/tests/Language/SQL/SimpleSQL/Tests.hs b/tests/Language/SQL/SimpleSQL/Tests.hs
--- a/tests/Language/SQL/SimpleSQL/Tests.hs
+++ b/tests/Language/SQL/SimpleSQL/Tests.hs
@@ -12,13 +12,11 @@
     ,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
 
@@ -27,6 +25,7 @@
 import Language.SQL.SimpleSQL.Postgres
 import Language.SQL.SimpleSQL.QueryExprComponents
 import Language.SQL.SimpleSQL.QueryExprs
+import Language.SQL.SimpleSQL.QueryExprParens
 import Language.SQL.SimpleSQL.TableRefs
 import Language.SQL.SimpleSQL.ScalarExprs
 import Language.SQL.SimpleSQL.Odbc
@@ -44,11 +43,10 @@
 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.
@@ -62,6 +60,7 @@
     ,odbcTests
     ,queryExprComponentTests
     ,queryExprsTests
+    ,queryExprParensTests
     ,tableRefTests
     ,groupByTests
     ,fullQueriesTests
@@ -77,104 +76,23 @@
     ,customDialectTests
     ,emptyStatementTests
     ,createIndexTests
+    ,errorMessageTests
     ]
 
-tests :: T.TestTree
-tests = itemToTest testData
-
---runTests :: IO ()
---runTests = void $ H.runTestTT $ itemToTest testData
+tests :: SpecWith ()
+tests = parallel $ 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
+itemToTest (GoldenErrorTest _ _ t) = t
diff --git a/tests/Language/SQL/SimpleSQL/Tpch.hs b/tests/Language/SQL/SimpleSQL/Tpch.hs
--- a/tests/Language/SQL/SimpleSQL/Tpch.hs
+++ b/tests/Language/SQL/SimpleSQL/Tpch.hs
@@ -14,15 +14,14 @@
 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 @@
          \        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 @@
          \        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 @@
          \ 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 @@
          \ 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 @@
          \ 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 @@
          \         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 @@
          \ 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 @@
          \ 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 @@
          \         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 @@
          \         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 @@
          \                                 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 @@
          \ 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 @@
          \         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 @@
          \ 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 @@
          \                         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 @@
          \         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 @@
          \                         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 @@
          \ 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 @@
          \                 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 @@
          \         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 @@
          \ 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 @@
          \ group by\n\
          \         cntrycode\n\
          \ order by\n\
-         \         cntrycode")
+         \         cntrycode"
   ]
+  where
+    q :: HasCallStack => Text -> Text -> TestItem
+    q _ src = testParseQueryExpr ansi2011 src
diff --git a/tests/RunTests.hs b/tests/RunTests.hs
--- a/tests/RunTests.hs
+++ b/tests/RunTests.hs
@@ -1,8 +1,9 @@
 
 
-import Test.Tasty
+import Test.Hspec (hspec)
 
+
 import Language.SQL.SimpleSQL.Tests
 
 main :: IO ()
-main = defaultMain tests
+main = hspec tests
