diff --git a/Language/SQL/SimpleSQL/Fixity.lhs b/Language/SQL/SimpleSQL/Fixity.lhs
--- a/Language/SQL/SimpleSQL/Fixity.lhs
+++ b/Language/SQL/SimpleSQL/Fixity.lhs
@@ -64,7 +64,7 @@
 > fixFixities fs se =
 >   runIdentity $ toSql <$> HSE.applyFixities (toHSEFixity fs) (toHaskell se)
 
-Now have to convert all our scalar exprs to haskell and back again.
+Now have to convert all our scalar exprs to Haskell and back again.
 Have to come up with a recipe for each ctor. Only continue if you have
 a strong stomach. Probably would have been less effort to just write
 the fixity code.
@@ -73,41 +73,39 @@
 > toHaskell e = case e of
 >     BinOp e0 op e1 -> HSE.InfixApp
 >                       (toHaskell e0)
->                       (HSE.QVarOp $ sym op)
+>                       (HSE.QVarOp $ sym $ name op)
 >                       (toHaskell e1)
 >     Iden {} -> str ('v':show e)
 >     StringLit {} -> str ('v':show e)
 >     NumLit {} -> str ('v':show e)
->     App n es -> HSE.App (var ('f':n)) $ ltoh es
+>     App n es -> HSE.App (var ('f':name n)) $ ltoh es
 >     Parens e0 -> HSE.Paren $ toHaskell e0
 >     IntervalLit {} -> str ('v':show e)
->     Iden2 {} -> str ('v':show e)
 >     Star -> str ('v':show e)
->     Star2 {} -> str ('v':show e)
 >     AggregateApp nm d es od ->
->         HSE.App (var ('a':nm))
->         $ HSE.List [str $ show (d,map snd od)
+>         HSE.App (var ('a':name nm))
+>         $ HSE.List [str $ show (d,orderInf od)
 >                    ,HSE.List $ map toHaskell es
->                    ,HSE.List $ map (toHaskell . fst) od]
->     WindowApp nm es pb od ->
->         HSE.App (var ('w':nm))
->         $ HSE.List [str $ show (map snd od)
+>                    ,HSE.List $ orderExps od]
+>     WindowApp nm es pb od r ->
+>         HSE.App (var ('w':name nm))
+>         $ HSE.List [str $ show (orderInf od, r)
 >                    ,HSE.List $ map toHaskell es
 >                    ,HSE.List $ map toHaskell pb
->                    ,HSE.List $ map (toHaskell . fst) od]
+>                    ,HSE.List $ orderExps od]
 >     PrefixOp nm e0 ->
->         HSE.App (HSE.Var $ sym nm) (toHaskell e0)
+>         HSE.App (HSE.Var $ sym $ name nm) (toHaskell e0)
 >     PostfixOp nm e0 ->
->         HSE.App (HSE.Var $ sym ('p':nm)) (toHaskell e0)
+>         HSE.App (HSE.Var $ sym ('p':name nm)) (toHaskell e0)
 >     SpecialOp nm es ->
->         HSE.App (var ('s':nm)) $ HSE.List $ map toHaskell es
+>         HSE.App (var ('s':name nm)) $ HSE.List $ map toHaskell es
 >     -- map the two maybes to lists with either 0 or 1 element
 >     Case v ts el -> HSE.App (var "$case")
 >                     (HSE.List [ltoh $ maybeToList v
->                               ,HSE.List $ map (ltoh . (\(a,b) -> [a,b])) ts
+>                               ,HSE.List $ map (ltoh . (\(a,b) -> b:a)) ts
 >                               ,ltoh $ maybeToList el])
 >     Cast e0 tn -> HSE.App (str ('c':show tn)) $ toHaskell e0
->     CastOp {} -> str ('v':show e)
+>     TypedLit {} -> str ('v':show e)
 >     SubQueryExpr {} -> str ('v': show e)
 >     In b e0 (InList l) ->
 >         HSE.App (str ('i':show b))
@@ -118,43 +116,49 @@
 >     str = HSE.Lit . HSE.String
 >     var = HSE.Var . HSE.UnQual . HSE.Ident
 >     sym = HSE.UnQual . HSE.Symbol
+>     name n = case n of
+>        QName q -> '"' : q
+>        Name m -> m
+>     orderExps = map (toHaskell . (\(OrderField a _ _) -> a))
+>     orderInf = map (\(OrderField _ b c) -> (b,c))
 
 
+
+
 > toSql :: HSE.Exp -> ScalarExpr
 > toSql e = case e of
 
 
 >     HSE.InfixApp e0 (HSE.QVarOp (HSE.UnQual (HSE.Symbol n))) e1 ->
->         BinOp (toSql e0) n (toSql e1)
+>         BinOp (toSql e0) (unname n) (toSql e1)
 >     HSE.Lit (HSE.String ('v':l)) -> read l
 >     HSE.App (HSE.Var (HSE.UnQual (HSE.Ident ('f':i))))
->             (HSE.List es) -> App i $ map toSql es
+>             (HSE.List es) -> App (unname i) $ map toSql es
 >     HSE.Paren e0 -> Parens $ toSql e0
 >     HSE.App (HSE.Var (HSE.UnQual (HSE.Ident ('a':i))))
 >             (HSE.List [HSE.Lit (HSE.String vs)
 >                       ,HSE.List es
 >                       ,HSE.List od]) ->
->         let (d,dir) = read vs
->         in AggregateApp i d (map toSql es)
->                         $ zip (map toSql od) dir
+>         let (d,oinf) = read vs
+>         in AggregateApp (unname i) d (map toSql es)
+>                         $ sord oinf od
 >     HSE.App (HSE.Var (HSE.UnQual (HSE.Ident ('w':i))))
 >             (HSE.List [HSE.Lit (HSE.String vs)
 >                       ,HSE.List es
 >                       ,HSE.List pb
 >                       ,HSE.List od]) ->
->         let dir = read vs
->         in WindowApp i (map toSql es)
->                        (map toSql pb)
->                        $ zip (map toSql od) dir
+>         let (oinf,r) = read vs
+>         in WindowApp (unname i) (map toSql es) (map toSql pb)
+>                        (sord oinf od) r
 >     HSE.App (HSE.Var (HSE.UnQual (HSE.Symbol ('p':nm)))) e0 ->
->         PostfixOp nm $ toSql e0
+>         PostfixOp (unname nm) $ toSql e0
 >     HSE.App (HSE.Var (HSE.UnQual (HSE.Symbol nm))) e0 ->
->         PrefixOp nm $ toSql e0
+>         PrefixOp (unname nm) $ toSql e0
 >     HSE.App (HSE.Var (HSE.UnQual (HSE.Ident ('s':nm)))) (HSE.List es) ->
->         SpecialOp nm $ map toSql es
+>         SpecialOp (unname nm) $ map toSql es
 >     HSE.App (HSE.Var (HSE.UnQual (HSE.Ident "$case")))
 >             (HSE.List [v,ts,el]) ->
->         Case (ltom v) (pairs ts) (ltom el)
+>         Case (ltom v) (whens ts) (ltom el)
 >     HSE.App (HSE.Lit (HSE.String ('c':nm))) e0 ->
 >         Cast (toSql e0) (read nm)
 >     HSE.App (HSE.Lit (HSE.String ('i':nm)))
@@ -165,10 +169,13 @@
 >         in In b (toSql e0) sq
 >     _ -> err e
 >   where
+>     sord = zipWith (\(i0,i1) ce -> OrderField (toSql ce) i0 i1)
 >     ltom (HSE.List []) = Nothing
 >     ltom (HSE.List [ex]) = Just $ toSql ex
 >     ltom ex = err ex
->     pairs (HSE.List l) = map (\(HSE.List [a,b]) -> (toSql a, toSql b)) l
->     pairs ex = err ex
+>     whens (HSE.List l) = map (\(HSE.List (t:ws)) -> (map toSql ws, toSql t)) l
+>     whens ex = err ex
 >     err :: Show a => a -> e
 >     err a = error $ "simple-sql-parser: internal fixity error " ++ show a
+>     unname ('"':nm) = QName nm
+>     unname n = Name n
diff --git a/Language/SQL/SimpleSQL/Parser.lhs b/Language/SQL/SimpleSQL/Parser.lhs
--- a/Language/SQL/SimpleSQL/Parser.lhs
+++ b/Language/SQL/SimpleSQL/Parser.lhs
@@ -12,6 +12,7 @@
 > import Data.Char
 > import Text.Parsec hiding (ParseError)
 > import qualified Text.Parsec as P
+> import Text.Parsec.Perm
 
 > import Language.SQL.SimpleSQL.Syntax
 > import Language.SQL.SimpleSQL.Fixity
@@ -19,23 +20,33 @@
 The public API functions.
 
 > -- | Parses a query expr, trailing semicolon optional.
-> parseQueryExpr :: FilePath -- ^ filename to use in errors
->                -> Maybe (Int,Int) -- ^ line number and column number to use in errors
->                -> String -- ^ the SQL source to parse
+> parseQueryExpr :: FilePath
+>                   -- ^ filename to use in errors
+>                -> Maybe (Int,Int)
+>                   -- ^ line number and column number to use in errors
+>                -> String
+>                   -- ^ the SQL source to parse
 >                -> Either ParseError QueryExpr
 > parseQueryExpr = wrapParse topLevelQueryExpr
 
-> -- | Parses a list of query exprs, with semi colons between them. The final semicolon is optional.
-> parseQueryExprs :: FilePath -- ^ filename to use in errors
->                 -> Maybe (Int,Int) -- ^ line number and column number to use in errors
->                 -> String -- ^ the SQL source to parse
+> -- | Parses a list of query exprs, with semi colons between
+> -- them. The final semicolon is optional.
+> parseQueryExprs :: FilePath
+>                    -- ^ filename to use in errors
+>                 -> Maybe (Int,Int)
+>                    -- ^ line number and column number to use in errors
+>                 -> String
+>                    -- ^ the SQL source to parse
 >                 -> Either ParseError [QueryExpr]
 > parseQueryExprs = wrapParse queryExprs
 
 > -- | Parses a scalar expression.
-> parseScalarExpr :: FilePath -- ^ filename to use in errors
->                 -> Maybe (Int,Int) -- ^ line number and column number to use in errors
->                 -> String -- ^ the SQL source to parse
+> parseScalarExpr :: FilePath
+>                    -- ^ filename to use in errors
+>                 -> Maybe (Int,Int)
+>                    -- ^ line number and column number to use in errors
+>                 -> String
+>                    -- ^ the SQL source to parse
 >                 -> Either ParseError ScalarExpr
 > parseScalarExpr = wrapParse scalarExpr
 
@@ -57,10 +68,15 @@
 
 > -- | Type to represent parse errors.
 > data ParseError = ParseError
->                   {peErrorString :: String -- ^ contains the error message
->                   ,peFilename :: FilePath -- ^ filename location for the error
->                   ,pePosition :: (Int,Int) -- ^ line number and column number location for the error
->                   ,peFormattedError :: String -- ^ formatted error with the position, error message and source context
+>                   {peErrorString :: String
+>                    -- ^ contains the error message
+>                   ,peFilename :: FilePath
+>                    -- ^ filename location for the error
+>                   ,pePosition :: (Int,Int)
+>                    -- ^ line number and column number location for the error
+>                   ,peFormattedError :: String
+>                    -- ^ formatted error with the position, error
+>                    -- message and source context
 >                   } deriving (Eq,Show)
 
 ------------------------------------------------
@@ -84,37 +100,41 @@
 or
 interval '5' month
 
+wrap the whole lot in try, in case we get something like this:
+interval '3 days'
+which parses as a typed literal
+
 > interval :: P ScalarExpr
-> interval = try (keyword_ "interval") >>
+> interval = try (keyword_ "interval" >>
 >     IntervalLit
 >     <$> stringLiteral
 >     <*> identifierString
->     <*> optionMaybe (try $ parens integerLiteral)
+>     <*> optionMaybe (try $ parens integerLiteral))
 
 > literal :: P ScalarExpr
 > literal = number <|> estring <|> interval
 
 == identifiers
 
-Uses the identifierString 'lexer'. See this function for notes on identifiers.
-
-> identifier :: P ScalarExpr
-> identifier = Iden <$> identifierString
+Uses the identifierString 'lexer'. See this function for notes on
+identifiers.
 
-Identifier with one dot in it. This should be extended to any amount
-of dots.
+> name :: P Name
+> name = choice [QName <$> quotedIdentifier
+>               ,Name <$> identifierString]
 
-> dottedIden :: P ScalarExpr
-> dottedIden = Iden2 <$> identifierString
->                    <*> (symbol "." *> identifierString)
+> identifier :: P ScalarExpr
+> identifier = Iden <$> name
 
 == star
 
-used in select *, select x.*, and agg(*) variations.
+used in select *, select x.*, and agg(*) variations, and some other
+places as well. Because it is quite general, the parser doesn't
+attempt to check that the star is in a valid context, it parses it OK
+in any scalar expression context.
 
 > star :: P ScalarExpr
-> star = choice [Star <$ symbol "*"
->               ,Star2 <$> (identifierString <* symbol "." <* symbol "*")]
+> star = Star <$ symbol "*"
 
 == function application, aggregates and windows
 
@@ -129,10 +149,9 @@
 > aggOrApp :: P ScalarExpr
 > aggOrApp =
 >     makeApp
->     <$> identifierString
+>     <$> name
 >     <*> parens ((,,) <$> try duplicates
->                      <*> choice [(:[]) <$> try star
->                                 ,commaSep scalarExpr']
+>                      <*> choice [commaSep scalarExpr']
 >                      <*> try (optionMaybe orderBy))
 >   where
 >     makeApp i (Nothing,es,Nothing) = App i es
@@ -158,10 +177,36 @@
 >     try (keyword_ "over")
 >     *> parens (WindowApp f es
 >                <$> option [] partitionBy
->                <*> option [] orderBy)
+>                <*> option [] orderBy
+>                <*> optionMaybe frameClause)
 >   where
 >     partitionBy = try (keyword_ "partition") >>
 >         keyword_ "by" >> commaSep1 scalarExpr'
+>     frameClause =
+>         mkFrame <$> choice [FrameRows <$ keyword_ "rows"
+>                            ,FrameRange <$ keyword_ "range"]
+>                 <*> frameStartEnd
+>     frameStartEnd =
+>         choice
+>         [try (keyword_ "between") >>
+>          mkFrameBetween <$> frameLimit True
+>                         <*> (keyword_ "and" *> frameLimit True)
+>         ,mkFrameFrom <$> frameLimit False]
+>     -- use the bexpression style from the between parsing for frame between
+>     frameLimit useB =
+>         choice
+>         [Current <$ try (keyword_ "current") <* keyword_ "row"
+>         ,try (keyword_ "unbounded") >>
+>          choice [UnboundedPreceding <$ keyword_ "preceding"
+>                 ,UnboundedFollowing <$ keyword_ "following"]
+>         ,do
+>          e <- if useB then scalarExprB else scalarExpr
+>          choice [Preceding e <$ keyword_ "preceding"
+>                 ,Following e <$ keyword_ "following"]
+>         ]
+>     mkFrameBetween s e rs = FrameBetween rs s e
+>     mkFrameFrom s rs = FrameFrom rs s
+>     mkFrame rs c = c rs
 > windowSuffix _ = fail ""
 
 > app :: P ScalarExpr
@@ -177,7 +222,8 @@
 >          <* keyword_ "end"
 >   where
 >     swhen = keyword_ "when" *>
->             ((,) <$> scalarExpr' <*> (keyword_ "then" *> scalarExpr'))
+>             ((,) <$> commaSep1 scalarExpr'
+>                  <*> (keyword_ "then" *> scalarExpr'))
 
 == miscellaneous keyword operators
 
@@ -194,16 +240,16 @@
 >     parensCast = try (keyword_ "cast") >>
 >                  parens (Cast <$> scalarExpr'
 >                          <*> (keyword_ "as" *> typeName))
->     prefixCast = try (CastOp <$> typeName
+>     prefixCast = try (TypedLit <$> typeName
 >                              <*> stringLiteral)
 
 extract(id from expr)
 
 > extract :: P ScalarExpr
 > extract = try (keyword_ "extract") >>
->     parens (makeOp <$> identifierString
+>     parens (makeOp <$> name
 >                    <*> (keyword_ "from" *> scalarExpr'))
->   where makeOp n e = SpecialOp "extract" [Iden n, e]
+>   where makeOp n e = SpecialOp (Name "extract") [Iden n, e]
 
 substring(x from expr to expr)
 
@@ -215,7 +261,7 @@
 >                    <*> (keyword_ "from" *> scalarExpr')
 >                    <*> (keyword_ "for" *> scalarExpr')
 >                    )
->   where makeOp a b c = SpecialOp "substring" [a,b,c]
+>   where makeOp a b c = SpecialOp (Name "substring") [a,b,c]
 
 in: two variations:
 a in (expr0, expr1, ...)
@@ -247,7 +293,7 @@
 
 > betweenSuffix :: ScalarExpr -> P ScalarExpr
 > betweenSuffix e =
->     makeOp <$> opName
+>     makeOp <$> (Name <$> opName)
 >            <*> return e
 >            <*> scalarExpr'' True
 >            <*> (keyword_ "and" *> scalarExpr'' True)
@@ -281,12 +327,22 @@
 >      <$ try (keyword_ "double" <* keyword_ "precision")
 >     ,TypeName "character varying"
 >      <$ try (keyword_ "character" <* keyword_ "varying")
->     ,TypeName <$> identifierString]
+>     ,TypeName <$> identifierString] >>= optionSuffix precision
+>   where
+>     precision t = try (parens (commaSep integerLiteral)) >>= makeWrap t
+>     makeWrap (TypeName t) [a] = return $ PrecTypeName t a
+>     makeWrap (TypeName t) [a,b] = return $ PrecScaleTypeName t a b
+>     makeWrap _ _ = fail "there must be one or two precision components"
 
-== scalar parens
 
+== scalar parens and row ctor
+
 > sparens :: P ScalarExpr
-> sparens = Parens <$> parens scalarExpr'
+> sparens =
+>     ctor <$> parens (commaSep1 scalarExpr')
+>   where
+>     ctor [a] = Parens a
+>     ctor as = SpecialOp (Name "rowctor") as
 
 
 == operator parsing
@@ -303,8 +359,10 @@
 > binOpSymbolNames :: [String]
 > binOpSymbolNames =
 >     ["=", "<=", ">=", "!=", "<>", "<", ">"
->     ,"*", "/", "+", "-"
->     ,"||"]
+>     ,"*", "/", "+", "-", "%"
+>     ,"||", "."
+>     ,"^", "|", "&"
+>     ]
 
 > binOpKeywordNames :: [String]
 > binOpKeywordNames = ["and", "or", "like", "overlaps"]
@@ -328,7 +386,7 @@
 > prefixUnOpKeywordNames = ["not"]
 
 > prefixUnOpSymbolNames :: [String]
-> prefixUnOpSymbolNames = ["+", "-"]
+> prefixUnOpSymbolNames = ["+", "-", "~"]
 
 There aren't any single keyword postfix operators currently
 supported. Maybe all these 'is's can be left factored?
@@ -347,7 +405,7 @@
 
 > prefixUnaryOp :: P ScalarExpr
 > prefixUnaryOp =
->     PrefixOp <$> opSymbol <*> scalarExpr'
+>     PrefixOp <$> (Name <$> opSymbol) <*> scalarExpr'
 >   where
 >     opSymbol = choice (map (try . symbol) prefixUnOpSymbolNames
 >                        ++ map (try . keyword) prefixUnOpKeywordNames)
@@ -361,7 +419,7 @@
 >     try $ choice $ map makeOp opPairs
 >   where
 >     opPairs = flip map postfixOpKeywords $ \o -> (o, words o)
->     makeOp (o,ws) = try $ PostfixOp o e <$ keywords_ ws
+>     makeOp (o,ws) = try $ PostfixOp (Name o) e <$ keywords_ ws
 >     keywords_ = try . mapM_ keyword_
 
 All the binary operators are parsed as same precedence and left
@@ -369,7 +427,7 @@
 
 > binaryOperatorSuffix :: Bool -> ScalarExpr -> P ScalarExpr
 > binaryOperatorSuffix bExpr e0 =
->     BinOp e0 <$> opSymbol <*> factor
+>     BinOp e0 <$> (Name <$> opSymbol) <*> factor
 >   where
 >     opSymbol = choice
 >         (map (try . symbol) binOpSymbolNames
@@ -388,7 +446,8 @@
 >              ++ prefixUnOpKeywordNames ++ prefixUnOpSymbolNames
 >              ++ postfixOpKeywords
 >     -- these are the ops with the highest precedence in order
->     highPrec = [infixl_ ["*","/"]
+>     highPrec = [infixl_ ["."]
+>                ,infixl_ ["*","/", "%"]
 >                ,infixl_ ["+", "-"]
 >                ,infixl_ ["<=",">=","!=","<>","||","like"]
 >                ]
@@ -428,7 +487,7 @@
 >                 ,subquery
 >                 ,prefixUnaryOp
 >                 ,try app
->                 ,try dottedIden
+>                 ,try star
 >                 ,identifier
 >                 ,sparens]
 
@@ -458,21 +517,25 @@
 expression tree (for efficiency and code clarity).
 
 > scalarExpr :: P ScalarExpr
-> scalarExpr =
->     choice [try star
->            ,fixFixities sqlFixities <$> scalarExpr']
+> scalarExpr = fixFixities sqlFixities <$> scalarExpr'
 
+expose the b expression for window frame clause range between
+
+> scalarExprB :: P ScalarExpr
+> scalarExprB = fixFixities sqlFixities <$> scalarExpr'' True
+
+
 -------------------------------------------------
 
 = query expressions
 
 == select lists
 
-> selectItem :: P (Maybe String, ScalarExpr)
-> selectItem = flip (,) <$> scalarExpr <*> optionMaybe (try alias)
->   where alias = optional (try (keyword_ "as")) *> identifierString
+> selectItem :: P (Maybe Name, ScalarExpr)
+> selectItem = flip (,) <$> scalarExpr <*> optionMaybe (try als)
+>   where als = optional (try (keyword_ "as")) *> name
 
-> selectList :: P [(Maybe String,ScalarExpr)]
+> selectList :: P [(Maybe Name,ScalarExpr)]
 > selectList = commaSep1 selectItem
 
 == from
@@ -490,35 +553,43 @@
 >     tref = nonJoinTref >>= optionSuffix joinTrefSuffix
 >     nonJoinTref = choice [try (TRQueryExpr <$> parens queryExpr)
 >                          ,TRParens <$> parens tref
->                          ,TRSimple <$> identifierString]
+>                          ,TRLateral <$> (try (keyword_ "lateral")
+>                                          *> nonJoinTref)
+>                          ,try (TRFunction <$> name
+>                                           <*> parens (commaSep scalarExpr))
+>                          ,TRSimple <$> name]
 >                   >>= optionSuffix aliasSuffix
->     aliasSuffix j =
->         let tableAlias = optional (try $ keyword_ "as") *> identifierString
->             columnAliases = optionMaybe $ try $ parens
->                             $ commaSep1 identifierString
->         in option j (TRAlias j <$> try tableAlias <*> try columnAliases)
+>     aliasSuffix j = option j (TRAlias j <$> alias)
 >     joinTrefSuffix t = (do
 >          nat <- option False $ try (True <$ try (keyword_ "natural"))
 >          TRJoin t <$> joinType
 >                   <*> nonJoinTref
 >                   <*> optionMaybe (joinCondition nat))
 >         >>= optionSuffix joinTrefSuffix
->     joinType = choice
->                [JCross <$ try (keyword_ "cross")
->                ,JInner <$ try (keyword_ "inner")
->                ,choice [JLeft <$ try (keyword_ "left")
->                        ,JRight <$ try (keyword_ "right")
->                        ,JFull <$ try (keyword_ "full")]
->                 <* optional (try $ keyword_ "outer")]
->                <* keyword "join"
+>     joinType =
+>         choice [choice
+>                 [JCross <$ try (keyword_ "cross")
+>                 ,JInner <$ try (keyword_ "inner")
+>                 ,choice [JLeft <$ try (keyword_ "left")
+>                         ,JRight <$ try (keyword_ "right")
+>                         ,JFull <$ try (keyword_ "full")]
+>                  <* optional (try $ keyword_ "outer")]
+>                 <* keyword "join"
+>                ,JInner <$ keyword_ "join"]
 >     joinCondition nat =
 >         choice [guard nat >> return JoinNatural
 >                ,try (keyword_ "on") >>
 >                 JoinOn <$> scalarExpr
 >                ,try (keyword_ "using") >>
->                 JoinUsing <$> parens (commaSep1 identifierString)
+>                 JoinUsing <$> parens (commaSep1 name)
 >                ]
 
+> alias :: P Alias
+> alias = Alias <$> try tableAlias <*> try columnAliases
+>   where
+>     tableAlias = optional (try $ keyword_ "as") *> name
+>     columnAliases = optionMaybe $ try $ parens $ commaSep1 name
+
 == simple other parts
 
 Parsers for where, group by, having, order by and limit, which are
@@ -533,35 +604,68 @@
 > swhere :: P ScalarExpr
 > swhere = keywordScalarExpr "where"
 
-> sgroupBy :: P [ScalarExpr]
+> sgroupBy :: P [GroupingExpr]
 > sgroupBy = try (keyword_ "group")
 >            *> keyword_ "by"
->            *> commaSep1 scalarExpr
+>            *> commaSep1 groupingExpression
+>   where
+>     groupingExpression =
+>       choice
+>       [try (keyword_ "cube") >>
+>        Cube <$> parens (commaSep groupingExpression)
+>       ,try (keyword_ "rollup") >>
+>        Rollup <$> parens (commaSep groupingExpression)
+>       ,GroupingParens <$> parens (commaSep groupingExpression)
+>       ,try (keyword_ "grouping") >> keyword_ "sets" >>
+>        GroupingSets <$> parens (commaSep groupingExpression)
+>       ,SimpleGroup <$> scalarExpr
+>       ]
 
 > having :: P ScalarExpr
 > having = keywordScalarExpr "having"
 
-> orderBy :: P [(ScalarExpr,Direction)]
+> orderBy :: P [OrderField]
 > orderBy = try (keyword_ "order") *> keyword_ "by" *> commaSep1 ob
 >   where
->     ob = (,) <$> scalarExpr
->              <*> option Asc (choice [Asc <$ keyword_ "asc"
->                                     ,Desc <$ keyword_ "desc"])
+>     ob = OrderField
+>          <$> scalarExpr
+>          <*> option Asc (choice [Asc <$ keyword_ "asc"
+>                                 ,Desc <$ keyword_ "desc"])
+>          <*> option NullsOrderDefault
+>              (try (keyword_ "nulls" >>
+>                     choice [NullsFirst <$ keyword "first"
+>                            ,NullsLast <$ keyword "last"]))
 
-> limit :: P ScalarExpr
-> limit = keywordScalarExpr "limit"
+allows offset and fetch in either order
++ postgresql offset without row(s) and limit instead of fetch also
 
+> offsetFetch :: P (Maybe ScalarExpr, Maybe ScalarExpr)
+> offsetFetch = permute ((,) <$?> (Nothing, Just <$> offset)
+>                            <|?> (Nothing, Just <$> fetch))
+
 > offset :: P ScalarExpr
-> offset = keywordScalarExpr "offset"
+> offset = try (keyword_ "offset") *> scalarExpr
+>          <* option () (try $ choice [try (keyword_ "rows"),keyword_ "row"])
 
+> fetch :: P ScalarExpr
+> fetch = choice [ansiFetch, limit]
+>   where
+>     ansiFetch = try (keyword_ "fetch") >>
+>         choice [keyword_ "first",keyword_ "next"]
+>         *> scalarExpr
+>         <* choice [keyword_ "rows",keyword_ "row"]
+>         <* keyword_ "only"
+>     limit = try (keyword_ "limit") *> scalarExpr
+
 == common table expressions
 
 > with :: P QueryExpr
 > with = try (keyword_ "with") >>
->     With <$> commaSep1 withQuery <*> queryExpr
+>     With <$> option False (try (True <$ keyword_ "recursive"))
+>          <*> commaSep1 withQuery <*> queryExpr
 >   where
 >     withQuery =
->         (,) <$> (identifierString <* optional (try $ keyword_ "as"))
+>         (,) <$> (alias <* optional (try $ keyword_ "as"))
 >             <*> parens queryExpr
 
 == query expression
@@ -572,10 +676,11 @@
 > queryExpr :: P QueryExpr
 > queryExpr =
 >   choice [with
->          ,select >>= optionSuffix queryExprSuffix]
+>          ,choice [values,table, select]
+>           >>= optionSuffix queryExprSuffix]
 >   where
 >     select = try (keyword_ "select") >>
->         Select
+>         mkSelect
 >         <$> (fromMaybe All <$> duplicates)
 >         <*> selectList
 >         <*> option [] from
@@ -583,8 +688,12 @@
 >         <*> option [] sgroupBy
 >         <*> optionMaybe having
 >         <*> option [] orderBy
->         <*> optionMaybe limit
->         <*> optionMaybe offset
+>         <*> offsetFetch
+>     mkSelect d sl f w g h od (ofs,fe) =
+>         Select d sl f w g h od ofs fe
+>     values = try (keyword_ "values")
+>              >> Values <$> commaSep (parens (commaSep scalarExpr))
+>     table = try (keyword_ "table") >> Table <$> name
 
 > queryExprSuffix :: QueryExpr -> P QueryExpr
 > queryExprSuffix qe =
@@ -633,10 +742,14 @@
 > symbol_ :: String -> P ()
 > symbol_ s = symbol s *> return ()
 
+TODO: now that keyword has try in it, a lot of the trys above can be
+removed
+
 > keyword :: String -> P String
-> keyword s = (map toLower <$> string s)
->             <* notFollowedBy (char '_' <|> alphaNum)
->             <* whiteSpace
+> keyword s = try $ do
+>     i <- identifierRaw
+>     guard (map toLower i == map toLower s)
+>     return i
 
 > keyword_ :: String -> P ()
 > keyword_ s = keyword s *> return ()
@@ -646,22 +759,28 @@
 support quoting other other sorts of identifiers yet. There is a
 blacklist of keywords which aren't supported as identifiers.
 
-> identifierString :: P String
-> identifierString = do
->     s <- (:) <$> letterOrUnderscore
->              <*> many letterDigitOrUnderscore <* whiteSpace
->     guard (s `notElem` blacklist)
->     return s
+the identifier raw doesn't check the blacklist since it is used by the
+keyword parser also
+
+> identifierRaw :: P String
+> identifierRaw = (:) <$> letterOrUnderscore
+>                     <*> many letterDigitOrUnderscore <* whiteSpace
 >   where
 >     letterOrUnderscore = char '_' <|> letter
 >     letterDigitOrUnderscore = char '_' <|> alphaNum
 
+> identifierString :: P String
+> identifierString = do
+>     s <- identifierRaw
+>     guard (map toLower s `notElem` blacklist)
+>     return s
+
 > blacklist :: [String]
 > blacklist =
 >     ["select", "as", "from", "where", "having", "group", "order"
->     ,"limit", "offset"
+>     ,"limit", "offset", "fetch"
 >     ,"inner", "left", "right", "full", "natural", "join"
->     ,"cross", "on", "using"
+>     ,"cross", "on", "using", "lateral"
 >     ,"when", "then", "case", "end", "in"
 >     ,"except", "intersect", "union"]
 
@@ -672,11 +791,21 @@
 could be tuned differently for each place the identifierString/
 identifier parsers are used to only blacklist the bare minimum.
 
+> quotedIdentifier :: P String
+> quotedIdentifier = char '"' *> manyTill anyChar (symbol_ "\"")
+
+
 String literals: limited at the moment, no escaping \' or other
 variations.
 
 > stringLiteral :: P String
-> stringLiteral = char '\'' *> manyTill anyChar (symbol_ "'")
+> stringLiteral = (char '\'' *> manyTill anyChar (char '\'')
+>                  >>= optionSuffix moreString) <* whiteSpace
+>   where
+>     moreString s0 = try $ do
+>         void $ char '\''
+>         s <- manyTill anyChar (char '\'')
+>         optionSuffix moreString (s0 ++ "'" ++ s)
 
 number literals
 
diff --git a/Language/SQL/SimpleSQL/Pretty.lhs b/Language/SQL/SimpleSQL/Pretty.lhs
--- a/Language/SQL/SimpleSQL/Pretty.lhs
+++ b/Language/SQL/SimpleSQL/Pretty.lhs
@@ -28,21 +28,23 @@
 = scalar expressions
 
 > scalarExpr :: ScalarExpr -> Doc
-> scalarExpr (StringLit s) = quotes $ text s
+> scalarExpr (StringLit s) = quotes $ text $ doubleUpQuotes s
+>   where doubleUpQuotes [] = []
+>         doubleUpQuotes ('\'':cs) = '\'':'\'':doubleUpQuotes cs
+>         doubleUpQuotes (c:cs) = c:doubleUpQuotes cs
+
 > scalarExpr (NumLit s) = text s
 > scalarExpr (IntervalLit v u p) =
 >     text "interval" <+> quotes (text v)
 >     <+> text u
 >     <+> maybe empty (parens . text . show ) p
-> scalarExpr (Iden i) = text i
-> scalarExpr (Iden2 q i) = text q <> text "." <> text i
+> scalarExpr (Iden i) = name i
 > scalarExpr Star = text "*"
-> scalarExpr (Star2 q) = text q <> text "." <> text "*"
 
-> scalarExpr (App f es) = text f <> parens (commaSep (map scalarExpr es))
+> scalarExpr (App f es) = name f <> parens (commaSep (map scalarExpr es))
 
 > scalarExpr (AggregateApp f d es od) =
->     text f
+>     name f
 >     <> parens ((case d of
 >                   Just Distinct -> text "distinct"
 >                   Just All -> text "all"
@@ -50,50 +52,70 @@
 >                <+> commaSep (map scalarExpr es)
 >                <+> orderBy od)
 
-> scalarExpr (WindowApp f es pb od) =
->     text f <> parens (commaSep $ map scalarExpr es)
+> scalarExpr (WindowApp f es pb od fr) =
+>     name f <> parens (commaSep $ map scalarExpr es)
 >     <+> text "over"
 >     <+> parens ((case pb of
 >                     [] -> empty
 >                     _ -> text "partition by"
 >                           <+> nest 13 (commaSep $ map scalarExpr pb))
->                 <+> orderBy od)
+>                 <+> orderBy od
+>     <+> maybe empty frd fr)
+>   where
+>     frd (FrameFrom rs fp) = rsd rs <+> fpd fp
+>     frd (FrameBetween rs fps fpe) =
+>         rsd rs <+> text "between" <+> fpd fps
+>         <+> text "and" <+> fpd fpe
+>     rsd rs = case rs of
+>                  FrameRows -> text "rows"
+>                  FrameRange -> text "range"
+>     fpd UnboundedPreceding = text "unbounded preceding"
+>     fpd UnboundedFollowing = text "unbounded following"
+>     fpd Current = text "current row"
+>     fpd (Preceding e) = scalarExpr e <+> text "preceding"
+>     fpd (Following e) = scalarExpr e <+> text "following"
 
-> scalarExpr (SpecialOp nm [a,b,c]) | nm `elem` ["between", "not between"] =
+> scalarExpr (SpecialOp nm [a,b,c]) | nm `elem` [Name "between"
+>                                               ,Name "not between"] =
 >   sep [scalarExpr a
->       ,text nm <+> scalarExpr b
->       ,nest (length nm + 1)
->        $ text "and" <+> scalarExpr c]
+>       ,name nm <+> scalarExpr b
+>       ,nest (length (unname nm) + 1) $ text "and" <+> scalarExpr c]
 
-> scalarExpr (SpecialOp "extract" [a,n]) =
+> scalarExpr (SpecialOp (Name "extract") [a,n]) =
 >   text "extract" <> parens (scalarExpr a
 >                             <+> text "from"
 >                             <+> scalarExpr n)
 
-> scalarExpr (SpecialOp "substring" [a,s,e]) =
+> scalarExpr (SpecialOp (Name "substring") [a,s,e]) =
 >   text "substring" <> parens (scalarExpr a
 >                             <+> text "from"
 >                             <+> scalarExpr s
 >                             <+> text "for"
 >                             <+> scalarExpr e)
 
+> scalarExpr (SpecialOp (Name "rowctor") as) =
+>     parens $ commaSep $ map scalarExpr as
+
 > scalarExpr (SpecialOp nm es) =
->   text nm <+> parens (commaSep $ map scalarExpr es)
+>   name nm <+> parens (commaSep $ map scalarExpr es)
 
-> scalarExpr (PrefixOp f e) = text f <+> scalarExpr e
-> scalarExpr (PostfixOp f e) = scalarExpr e <+> text f
-> scalarExpr e@(BinOp _ op _) | op `elem` ["and", "or"] =
+> scalarExpr (PrefixOp f e) = name f <+> scalarExpr e
+> scalarExpr (PostfixOp f e) = scalarExpr e <+> name f
+> scalarExpr e@(BinOp _ op _) | op `elem` [Name "and", Name "or"] =
 >     -- special case for and, or, get all the ands so we can vcat them
 >     -- nicely
 >     case ands e of
 >       (e':es) -> vcat (scalarExpr e'
->                        : map ((text op <+>) . scalarExpr) es)
+>                        : map ((name op <+>) . scalarExpr) es)
 >       [] -> empty -- shouldn't be possible
 >   where
 >     ands (BinOp a op' b) | op == op' = ands a ++ ands b
 >     ands x = [x]
+> -- special case for . we don't use whitespace
+> scalarExpr (BinOp e0 (Name ".") e1) =
+>     scalarExpr e0 <> text "." <> scalarExpr e1
 > scalarExpr (BinOp e0 f e1) =
->     scalarExpr e0 <+> text f <+> scalarExpr e1
+>     scalarExpr e0 <+> name f <+> scalarExpr e1
 
 > scalarExpr (Case t ws els) =
 >     sep $ [text "case" <+> maybe empty scalarExpr t]
@@ -102,17 +124,17 @@
 >           ++ [text "end"]
 >   where
 >     w (t0,t1) =
->       text "when" <+> nest 5 (scalarExpr t0)
+>       text "when" <+> nest 5 (commaSep $ map scalarExpr t0)
 >       <+> text "then" <+> nest 5 (scalarExpr t1)
 >     e el = text "else" <+> nest 5 (scalarExpr el)
 > scalarExpr (Parens e) = parens $ scalarExpr e
-> scalarExpr (Cast e (TypeName tn)) =
+> scalarExpr (Cast e tn) =
 >     text "cast" <> parens (sep [scalarExpr e
 >                                ,text "as"
->                                ,text tn])
+>                                ,typeName tn])
 
-> scalarExpr (CastOp (TypeName tn) s) =
->     text tn <+> quotes (text s)
+> scalarExpr (TypedLit tn s) =
+>     typeName tn <+> quotes (text s)
 
 > scalarExpr (SubQueryExpr ty qe) =
 >     (case ty of
@@ -132,10 +154,25 @@
 >                      InList es -> commaSep $ map scalarExpr es
 >                      InQueryExpr qe -> queryExpr qe)
 
+> unname :: Name -> String
+> unname (QName n) = "\"" ++ n ++ "\""
+> unname (Name n) = n
+
+> name :: Name -> Doc
+> name (QName n) = doubleQuotes $ text n
+> name (Name n) = text n
+
+> typeName :: TypeName -> Doc
+> typeName (TypeName t) = text t
+> typeName (PrecTypeName t a) = text t <+> parens (text $ show a)
+> typeName (PrecScaleTypeName t a b) =
+>     text t <+> parens (text (show a) <+> comma <+> text (show b))
+
+
 = query expressions
 
 > queryExpr :: QueryExpr -> Doc
-> queryExpr (Select d sl fr wh gb hv od lm off) =
+> queryExpr (Select d sl fr wh gb hv od off fe) =
 >   sep [text "select"
 >       ,case d of
 >           All -> empty
@@ -146,8 +183,9 @@
 >       ,grpBy gb
 >       ,maybeScalarExpr "having" hv
 >       ,orderBy od
->       ,maybeScalarExpr "limit" lm
->       ,maybeScalarExpr "offset" off
+>       ,maybe empty (\e -> text "offset" <+> scalarExpr e <+> text "rows") off
+>       ,maybe empty (\e -> text "fetch next" <+> scalarExpr e
+>                           <+> text "rows only") fe
 >       ]
 > queryExpr (CombineQueryExpr q1 ct d c q2) =
 >   sep [queryExpr q1
@@ -162,18 +200,28 @@
 >                Corresponding -> text "corresponding"
 >                Respectively -> empty
 >       ,queryExpr q2]
-> queryExpr (With withs qe) =
->   text "with"
+> queryExpr (With rc withs qe) =
+>   text "with" <+> (if rc then text "recursive" else empty)
 >   <+> vcat [nest 5
 >             (vcat $ punctuate comma $ flip map withs $ \(n,q) ->
->              text n <+> text "as" <+> parens (queryExpr q))
+>              alias n <+> text "as" <+> parens (queryExpr q))
 >            ,queryExpr qe]
+> queryExpr (Values vs) =
+>     text "values"
+>     <+> nest 7 (commaSep (map (parens . commaSep . map scalarExpr) vs))
+> queryExpr (Table t) = text "table" <+> name t
 
-> selectList :: [(Maybe String, ScalarExpr)] -> Doc
+
+> alias :: Alias -> Doc
+> alias (Alias nm cols) =
+>     text "as" <+> name nm
+>     <+> maybe empty (parens . commaSep . map name) cols
+
+> selectList :: [(Maybe Name, ScalarExpr)] -> Doc
 > selectList is = commaSep $ map si is
 >   where
->     si (al,e) = scalarExpr e <+> maybe empty alias al
->     alias al = text "as" <+> text al
+>     si (al,e) = scalarExpr e <+> maybe empty als al
+>     als al = text "as" <+> name al
 
 > from :: [TableRef] -> Doc
 > from [] = empty
@@ -181,11 +229,11 @@
 >     sep [text "from"
 >         ,nest 5 $ vcat $ punctuate comma $ map tr ts]
 >   where
->     tr (TRSimple t) = text t
->     tr (TRAlias t a cs) =
->         sep [tr t
->             ,text "as" <+> text a
->              <+> maybe empty (parens . commaSep . map text) cs]
+>     tr (TRSimple t) = name t
+>     tr (TRLateral t) = text "lateral" <+> tr t
+>     tr (TRFunction f as) =
+>         name f <> parens (commaSep $ map scalarExpr as)
+>     tr (TRAlias t a) = sep [tr t, alias a]
 >     tr (TRParens t) = parens $ tr t
 >     tr (TRQueryExpr q) = parens $ queryExpr q
 >     tr (TRJoin t0 jt t1 jc) =
@@ -205,7 +253,7 @@
 >           ,text "join"]
 >     joinCond (Just (JoinOn e)) = text "on" <+> scalarExpr e
 >     joinCond (Just (JoinUsing es)) =
->         text "using" <+> parens (commaSep $ map text es)
+>         text "using" <+> parens (commaSep $ map name es)
 >     joinCond Nothing = empty
 >     joinCond (Just JoinNatural) = empty
 
@@ -214,18 +262,29 @@
 >       (\e -> sep [text k
 >                  ,nest (length k + 1) $ scalarExpr e])
 
-> grpBy :: [ScalarExpr] -> Doc
+> grpBy :: [GroupingExpr] -> Doc
 > grpBy [] = empty
 > grpBy gs = sep [text "group by"
->                ,nest 9 $ commaSep $ map scalarExpr gs]
+>                ,nest 9 $ commaSep $ map ge gs]
+>   where
+>     ge (SimpleGroup e) = scalarExpr e
+>     ge (GroupingParens g) = parens (commaSep $ map ge g)
+>     ge (Cube es) = text "cube" <> parens (commaSep $ map ge es)
+>     ge (Rollup es) = text "rollup" <> parens (commaSep $ map ge es)
+>     ge (GroupingSets es) = text "grouping sets" <> parens (commaSep $ map ge es)
 
-> orderBy :: [(ScalarExpr,Direction)] -> Doc
+> orderBy :: [OrderField] -> Doc
 > orderBy [] = empty
 > orderBy os = sep [text "order by"
 >                  ,nest 9 $ commaSep $ map f os]
 >   where
->     f (e,Asc) = scalarExpr e
->     f (e,Desc) = scalarExpr e <+> text "desc"
+>     f (OrderField e d n) =
+>         scalarExpr e
+>         <+> (if d == Asc then empty else text "desc")
+>         <+> (case n of
+>                 NullsOrderDefault -> empty
+>                 NullsFirst -> text "nulls" <+> text "first"
+>                 NullsLast -> text "nulls" <+> text "last")
 
 = utils
 
diff --git a/Language/SQL/SimpleSQL/Syntax.lhs b/Language/SQL/SimpleSQL/Syntax.lhs
--- a/Language/SQL/SimpleSQL/Syntax.lhs
+++ b/Language/SQL/SimpleSQL/Syntax.lhs
@@ -1,25 +1,33 @@
 
-> -- | The AST for SQL queries
+> -- | The AST for SQL queries.
 > module Language.SQL.SimpleSQL.Syntax
 >     (-- * Scalar expressions
 >      ScalarExpr(..)
+>     ,Name(..)
 >     ,TypeName(..)
 >     ,Duplicates(..)
+>     ,OrderField(..)
 >     ,Direction(..)
+>     ,NullsOrder(..)
 >     ,InThing(..)
 >     ,SubQueryExprType(..)
+>     ,Frame(..)
+>     ,FrameRows(..)
+>     ,FramePos(..)
 >      -- * Query expressions
 >     ,QueryExpr(..)
 >     ,makeSelect
 >     ,CombineOp(..)
 >     ,Corresponding(..)
+>     ,Alias(..)
+>     ,GroupingExpr(..)
 >      -- ** From
 >     ,TableRef(..)
 >     ,JoinType(..)
 >     ,JoinCondition(..)
 >     ) where
 
-> -- | Represents a scalar expression
+> -- | Represents a scalar expression.
 > data ScalarExpr
 >     = -- | a numeric literal optional decimal point, e+-
 >       -- integral exponent, e.g
@@ -37,58 +45,66 @@
 >       -- * 12.34e-6
 >       NumLit String
 >       -- | string literal, currently only basic strings between
->       -- single quotes without escapes (no single quotes in strings
->       -- then)
+>       -- single quotes with a single quote escaped using ''
 >     | StringLit String
 >       -- | text of interval literal, units of interval precision,
 >       -- e.g. interval 3 days (3)
->     | IntervalLit String String (Maybe Int)
+>     | IntervalLit
+>       {ilLiteral :: String -- ^ literal text
+>       ,ilUnits :: String -- ^ units
+>       ,ilPrecision :: Maybe Int -- ^ precision
+>       }
 >       -- | identifier without dots
->     | Iden String
->       -- | identifier with one dot
->     | Iden2 String String
->       -- | star
+>     | Iden Name
+>       -- | star, as in select *, t.*, count(*)
 >     | Star
->       -- | star with qualifier, e.g t.*
->     | Star2 String
 >       -- | function application (anything that looks like c style
 >       -- function application syntactically)
->     | App String [ScalarExpr]
+>     | App Name [ScalarExpr]
 >       -- | aggregate application, which adds distinct or all, and
 >       -- order by, to regular function application
->     | AggregateApp String (Maybe Duplicates)
->                    [ScalarExpr]
->                    [(ScalarExpr,Direction)]
+>     | AggregateApp
+>       {aggName :: Name -- ^ aggregate function name
+>       ,aggDistinct :: Maybe Duplicates -- ^ distinct
+>       ,aggArgs :: [ScalarExpr]-- ^ args
+>       ,aggOrderBy :: [OrderField] -- ^ order by
+>       }
 >       -- | window application, which adds over (partition by a order
 >       -- by b) to regular function application. Explicit frames are
 >       -- not currently supported
->     | WindowApp String [ScalarExpr] [ScalarExpr] [(ScalarExpr,Direction)]
+>     | WindowApp
+>       {wnName :: Name -- ^ window function name
+>       ,wnArgs :: [ScalarExpr] -- ^ args
+>       ,wnPartition :: [ScalarExpr] -- ^ partition by
+>       ,wnOrderBy :: [OrderField] -- ^ order by
+>       ,wnFrame :: Maybe Frame -- ^ frame clause
+>       }
 >       -- | Infix binary operators. This is used for symbol operators
 >       -- (a + b), keyword operators (a and b) and multiple keyword
 >       -- operators (a is similar to b)
->     | BinOp ScalarExpr String ScalarExpr
+>     | BinOp ScalarExpr Name ScalarExpr
 >       -- | Prefix unary operators. This is used for symbol
 >       -- operators, keyword operators and multiple keyword operators
->     | PrefixOp String ScalarExpr
+>     | PrefixOp Name ScalarExpr
 >       -- | Postfix unary operators. This is used for symbol
 >       -- operators, keyword operators and multiple keyword operators
->     | PostfixOp String ScalarExpr
+>     | PostfixOp Name ScalarExpr
 >       -- | Used for ternary, mixfix and other non orthodox
 >       -- operators, including the function looking calls which use
 >       -- keywords instead of commas to separate the arguments,
 >       -- e.g. substring(t from 1 to 5)
->     | SpecialOp String [ScalarExpr]
->       -- | case expression. both flavours supported. Multiple
->       -- condition when branches not currently supported (case when
->       -- a=4,b=5 then x end)
->     | Case (Maybe ScalarExpr) -- test value
->            [(ScalarExpr,ScalarExpr)] -- when branches
->            (Maybe ScalarExpr) -- else value
+>     | SpecialOp Name [ScalarExpr]
+>       -- | case expression. both flavours supported
+>     | Case
+>       {caseTest :: Maybe ScalarExpr -- ^ test value
+>       ,caseWhens :: [([ScalarExpr],ScalarExpr)] -- ^ when branches
+>       ,caseElse :: Maybe ScalarExpr -- ^ else value
+>       }
 >     | Parens ScalarExpr
 >       -- | cast(a as typename)
 >     | Cast ScalarExpr TypeName
 >       -- | prefix 'typed literal', e.g. int '42'
->     | CastOp TypeName String
+>     | TypedLit TypeName String
 >       -- | exists, all, any, some subqueries
 >     | SubQueryExpr SubQueryExprType QueryExpr
 >       -- | in list literal and in subquery, if the bool is false it
@@ -96,17 +112,25 @@
 >     | In Bool ScalarExpr InThing
 >       deriving (Eq,Show,Read)
 
+> -- | Represents an identifier name, which can be quoted or unquoted.
+> data Name = Name String
+>           | QName String
+>           deriving (Eq,Show,Read)
+
 > -- | Represents a type name, used in casts.
-> data TypeName = TypeName String deriving (Eq,Show,Read)
+> data TypeName = TypeName String
+>               | PrecTypeName String Int
+>               | PrecScaleTypeName String Int Int
+>                 deriving (Eq,Show,Read)
 
 
 > -- | Used for 'expr in (scalar expression list)', and 'expr in
-> -- (subquery)' syntax
+> -- (subquery)' syntax.
 > data InThing = InList [ScalarExpr]
 >              | InQueryExpr QueryExpr
 >              deriving (Eq,Show,Read)
 
-> -- | A subquery in a scalar expression
+> -- | A subquery in a scalar expression.
 > data SubQueryExprType
 >     = -- | exists (query expr)
 >       SqExists
@@ -120,6 +144,35 @@
 >     | SqAny
 >       deriving (Eq,Show,Read)
 
+> -- | Represents one field in an order by list.
+> data OrderField = OrderField ScalarExpr Direction NullsOrder
+>                   deriving (Eq,Show,Read)
+
+> -- | Represents 'nulls first' or 'nulls last' in an order by clause.
+> data NullsOrder = NullsOrderDefault
+>                 | NullsFirst
+>                 | NullsLast
+>                   deriving (Eq,Show,Read)
+
+> -- | Represents the frame clause of a window
+> -- this can be [range | rows] frame_start
+> -- or [range | rows] between frame_start and frame_end
+> data Frame = FrameFrom FrameRows FramePos
+>            | FrameBetween FrameRows FramePos FramePos
+>              deriving (Eq,Show,Read)
+
+> -- | Represents whether a window frame clause is over rows or ranges.
+> data FrameRows = FrameRows | FrameRange
+>                  deriving (Eq,Show,Read)
+
+> -- | represents the start or end of a frame
+> data FramePos = UnboundedPreceding
+>               | Preceding ScalarExpr
+>               | Current
+>               | Following ScalarExpr
+>               | UnboundedFollowing
+>                 deriving (Eq,Show,Read)
+
 > -- | Represents a query expression, which can be:
 > --
 > -- * a regular select;
@@ -128,51 +181,44 @@
 > --
 > -- * a common table expression (with);
 > --
-> -- * a values expression (not yet supported);
+> -- * a values expression;
 > --
 > -- * or the table syntax - 'table t', shorthand for 'select * from
-> --    t' (not yet supported).
+> --    t'.
 > data QueryExpr
 >     = Select
 >       {qeDuplicates :: Duplicates
->       ,qeSelectList :: [(Maybe String,ScalarExpr)]
+>       ,qeSelectList :: [(Maybe Name,ScalarExpr)]
 >        -- ^ the column aliases and the expressions
 >       ,qeFrom :: [TableRef]
 >       ,qeWhere :: Maybe ScalarExpr
->       ,qeGroupBy :: [ScalarExpr]
+>       ,qeGroupBy :: [GroupingExpr]
 >       ,qeHaving :: Maybe ScalarExpr
->       ,qeOrderBy :: [(ScalarExpr,Direction)]
->       ,qeLimit :: Maybe ScalarExpr
+>       ,qeOrderBy :: [OrderField]
 >       ,qeOffset :: Maybe ScalarExpr
+>       ,qeFetch :: Maybe ScalarExpr
 >       }
 >     | CombineQueryExpr
->       {qe1 :: QueryExpr
+>       {qe0 :: QueryExpr
 >       ,qeCombOp :: CombineOp
 >       ,qeDuplicates :: Duplicates
 >       ,qeCorresponding :: Corresponding
->       ,qe2 :: QueryExpr
+>       ,qe1 :: QueryExpr
 >       }
->     | With [(String,QueryExpr)] QueryExpr
+>     | With
+>       {qeWithRecursive :: Bool
+>       ,qeViews :: [(Alias,QueryExpr)]
+>       ,qeQueryExpression :: QueryExpr}
+>     | Values [[ScalarExpr]]
+>     | Table Name
 >       deriving (Eq,Show,Read)
 
 TODO: add queryexpr parens to deal with e.g.
 (select 1 union select 2) union select 3
 I'm not sure if this is valid syntax or not.
 
-> -- | represents the Distinct or All keywords, which can be used
-> -- before a select list, in an aggregate/window function
-> -- application, or in a query expression set operator
-> data Duplicates = Distinct | All deriving (Eq,Show,Read)
-
-> -- | The direction for a column in order by.
-> data Direction = Asc | Desc deriving (Eq,Show,Read)
-> -- | Query expression set operators
-> data CombineOp = Union | Except | Intersect deriving (Eq,Show,Read)
-> -- | Corresponding, an option for the set operators
-> data Corresponding = Corresponding | Respectively deriving (Eq,Show,Read)
-
-> -- | helper/'default' value for query exprs to make creating query
-> -- expr values a little easier
+> -- | Helper/'default' value for query exprs to make creating query
+> -- expr values a little easier.
 > makeSelect :: QueryExpr
 > makeSelect = Select {qeDuplicates = All
 >                     ,qeSelectList = []
@@ -181,30 +227,60 @@
 >                     ,qeGroupBy = []
 >                     ,qeHaving = Nothing
 >                     ,qeOrderBy = []
->                     ,qeLimit = Nothing
->                     ,qeOffset = Nothing}
+>                     ,qeOffset = Nothing
+>                     ,qeFetch = Nothing}
 
+
+> -- | Represents the Distinct or All keywords, which can be used
+> -- before a select list, in an aggregate/window function
+> -- application, or in a query expression set operator.
+> data Duplicates = Distinct | All deriving (Eq,Show,Read)
+
+> -- | The direction for a column in order by.
+> data Direction = Asc | Desc deriving (Eq,Show,Read)
+> -- | Query expression set operators.
+> data CombineOp = Union | Except | Intersect deriving (Eq,Show,Read)
+> -- | Corresponding, an option for the set operators.
+> data Corresponding = Corresponding | Respectively deriving (Eq,Show,Read)
+
+> -- | Represents an item in a group by clause.
+> data GroupingExpr
+>     = GroupingParens [GroupingExpr]
+>     | Cube [GroupingExpr]
+>     | Rollup [GroupingExpr]
+>     | GroupingSets [GroupingExpr]
+>     | SimpleGroup ScalarExpr
+>       deriving (Eq,Show,Read)
+
 > -- | Represents a entry in the csv of tables in the from clause.
 > data TableRef = -- | from t
->                 TRSimple String
+>                 TRSimple Name
 >                 -- | from a join b
 >               | TRJoin TableRef JoinType TableRef (Maybe JoinCondition)
 >                 -- | from (a)
 >               | TRParens TableRef
 >                 -- | from a as b(c,d)
->               | TRAlias TableRef String (Maybe [String])
+>               | TRAlias TableRef Alias
 >                 -- | from (query expr)
 >               | TRQueryExpr QueryExpr
+>                 -- | from function(args)
+>               | TRFunction Name [ScalarExpr]
+>                 -- | from lateral t
+>               | TRLateral TableRef
 >                 deriving (Eq,Show,Read)
 
-TODO: add function table ref
+> -- | Represents an alias for a table valued expression, used in with
+> -- queries and in from alias, e.g. select a from t u, select a from t u(b),
+> -- with a(c) as select 1, select * from a.
+> data Alias = Alias Name (Maybe [Name])
+>              deriving (Eq,Show,Read)
 
-> -- | The type of a join
+> -- | The type of a join.
 > data JoinType = JInner | JLeft | JRight | JFull | JCross
 >                 deriving (Eq,Show,Read)
 
 > -- | The join condition.
 > data JoinCondition = JoinOn ScalarExpr -- ^ on expr
->                    | JoinUsing [String] -- ^ using (column list)
+>                    | JoinUsing [Name] -- ^ using (column list)
 >                    | JoinNatural -- ^ natural join was used
 >                      deriving (Eq,Show,Read)
diff --git a/README b/README
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
 A parser for SQL queries in Haskell.
 
-Homepage: http://jakewheat.github.io/simple_sql_parser/
+Homepage: http://jakewheat.github.io/simple-sql-parser/
 
 Contact: jakewheatmail@gmail.com
diff --git a/changelog b/changelog
new file mode 100644
--- /dev/null
+++ b/changelog
@@ -0,0 +1,29 @@
+0.2.0.0
+	last update on commit a53578c6c27d94a33d104d404547cc02b4ad36c3
+	'' quotes in string literal
+	parse simple interval literal e.g. "interval '1 week'"
+	support . in identifiers as a dot operator
+	support quoted identifiers
+	partial support for explicit window frames
+	support multiple test expressions in when branches in case expressions
+	rename CastOp to TypedLit
+	support typenames with precision and scale in casts
+	support nulls first and nulls last in order by
+	support grouping expressions: group by (), grouping sets, cube,
+	  rollup and parens
+	support with recursive
+	support values table literal
+	support 'table t' syntax
+	rename fields qe1 and qe1 in combinequeryexpr to qe0 and qe1
+	add support for functions in from clause
+	add support for lateral in from clause
+	support column aliases in common table expressions
+	refactor the tests and add lots more
+	parse * in any scalar context instead of trying to restrict it
+	support row ctor without 'row' e.g. (a,b) = (c,d)
+	add % ^ | & ~ operators
+	support ansi standard syntax for offset n rows and fetch first n
+	  rows only
+	fix keyword parsing to be case insensitive
+0.1.0.0
+	initial version
diff --git a/simple-sql-parser.cabal b/simple-sql-parser.cabal
--- a/simple-sql-parser.cabal
+++ b/simple-sql-parser.cabal
@@ -1,9 +1,9 @@
 name:                simple-sql-parser
-version:             0.1.0.0
+version:             0.2.0
 synopsis:            A parser for SQL queries
-description:         A parser for SQL queries
+description:         A parser for SQL queries. Please see the homepage for more information <http://jakewheat.github.io/simple-sql-parser/>.
 
-homepage:            http://jakewheat.github.io/simple_sql_parser/
+homepage:            http://jakewheat.github.io/simple-sql-parser/
 license:             BSD3
 license-file:        LICENSE
 author:              Jake Wheat
@@ -11,13 +11,13 @@
 copyright:           Copyright Jake Wheat 2013
 category:            Database,Language
 build-type:          Simple
-extra-source-files:  README,LICENSE
+extra-source-files:  README,LICENSE,changelog
 cabal-version:       >=1.10
-bug-reports:         https://github.com/JakeWheat/simple_sql_parser/issues
+bug-reports:         https://github.com/JakeWheat/simple-sql-parser/issues
 
 source-repository head
   type:                git
-  location:            https://github.com/JakeWheat/simple_sql_parser.git
+  location:            https://github.com/JakeWheat/simple-sql-parser.git
 
 library
   exposed-modules:     Language.SQL.SimpleSQL.Pretty,
@@ -52,8 +52,18 @@
                        Language.SQL.SimpleSQL.Parser,
                        Language.SQL.SimpleSQL.Syntax,
                        Language.SQL.SimpleSQL.Fixity,
+
+                       Language.SQL.SimpleSQL.FullQueries,
+                       Language.SQL.SimpleSQL.GroupBy,
+                       Language.SQL.SimpleSQL.Postgres,
+                       Language.SQL.SimpleSQL.QueryExprComponents,
+                       Language.SQL.SimpleSQL.QueryExprs,
+                       Language.SQL.SimpleSQL.ScalarExprs,
+                       Language.SQL.SimpleSQL.TableRefs,
+                       Language.SQL.SimpleSQL.TestTypes,
                        Language.SQL.SimpleSQL.Tests,
-                       Tpch
-  other-extensions:    TupleSections
+                       Language.SQL.SimpleSQL.Tpch
+
+  other-extensions:    TupleSections,OverloadedStrings
   default-language:    Haskell2010
   ghc-options:         -Wall
diff --git a/tools/Language/SQL/SimpleSQL/FullQueries.lhs b/tools/Language/SQL/SimpleSQL/FullQueries.lhs
new file mode 100644
--- /dev/null
+++ b/tools/Language/SQL/SimpleSQL/FullQueries.lhs
@@ -0,0 +1,39 @@
+
+Some tests for parsing full queries.
+
+> {-# LANGUAGE OverloadedStrings #-}
+> module Language.SQL.SimpleSQL.FullQueries (fullQueriesTests) where
+
+> import Language.SQL.SimpleSQL.TestTypes
+> import Language.SQL.SimpleSQL.Syntax
+
+
+> fullQueriesTests :: TestItem
+> fullQueriesTests = Group "queries" $ map (uncurry TestQueryExpr)
+>     [("select count(*) from t"
+>      ,makeSelect
+>       {qeSelectList = [(Nothing, App "count" [Star])]
+>       ,qeFrom = [TRSimple "t"]
+>       }
+>      )
+
+>     ,("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"
+>      ,makeSelect
+>       {qeSelectList = [(Nothing, Iden "a")
+>                       ,(Just "s"
+>                        ,App "sum" [BinOp (Iden "c")
+>                                          "+" (Iden "d")])]
+>       ,qeFrom = [TRSimple "t", TRSimple "u"]
+>       ,qeWhere = Just $ BinOp (Iden "a") ">" (NumLit "5")
+>       ,qeGroupBy = [SimpleGroup $ Iden "a"]
+>       ,qeHaving = Just $ BinOp (App "count" [NumLit "1"])
+>                                ">" (NumLit "5")
+>       ,qeOrderBy = [OrderField (Iden "s") Asc NullsOrderDefault]
+>       }
+>      )
+>     ]
diff --git a/tools/Language/SQL/SimpleSQL/GroupBy.lhs b/tools/Language/SQL/SimpleSQL/GroupBy.lhs
new file mode 100644
--- /dev/null
+++ b/tools/Language/SQL/SimpleSQL/GroupBy.lhs
@@ -0,0 +1,233 @@
+
+Here are the tests for the group by component of query exprs
+
+> {-# LANGUAGE OverloadedStrings #-}
+> module Language.SQL.SimpleSQL.GroupBy (groupByTests) where
+
+> import Language.SQL.SimpleSQL.TestTypes
+> import Language.SQL.SimpleSQL.Syntax
+
+
+> groupByTests :: TestItem
+> groupByTests = Group "groupByTests"
+>     [simpleGroupBy
+>     ,newGroupBy
+>     ,randomGroupBy
+>     ]
+
+> simpleGroupBy :: TestItem
+> simpleGroupBy = Group "simpleGroupBy" $ map (uncurry TestQueryExpr)
+>     [("select a,sum(b) from t group by a"
+>      ,makeSelect {qeSelectList = [(Nothing, Iden "a")
+>                                  ,(Nothing, App "sum" [Iden "b"])]
+>                  ,qeFrom = [TRSimple "t"]
+>                  ,qeGroupBy = [SimpleGroup $ Iden "a"]
+>                  })
+
+>     ,("select a,b,sum(c) from t group by a,b"
+>      ,makeSelect {qeSelectList = [(Nothing, Iden "a")
+>                                  ,(Nothing, Iden "b")
+>                                  ,(Nothing, App "sum" [Iden "c"])]
+>                  ,qeFrom = [TRSimple "t"]
+>                  ,qeGroupBy = [SimpleGroup $ Iden "a"
+>                               ,SimpleGroup $ Iden "b"]
+>                  })
+>     ]
+
+test the new group by (), grouping sets, cube and rollup syntax (not
+sure which sql version they were introduced, 1999 or 2003 I think).
+
+> newGroupBy :: TestItem
+> newGroupBy = Group "newGroupBy" $ map (uncurry TestQueryExpr)
+>     [("select * from t group by ()", ms [GroupingParens []])
+>     ,("select * from t group by grouping sets ((), (a))"
+>      ,ms [GroupingSets [GroupingParens []
+>                        ,GroupingParens [SimpleGroup $ Iden "a"]]])
+>     ,("select * from t group by cube(a,b)"
+>      ,ms [Cube [SimpleGroup $ Iden "a", SimpleGroup $ Iden "b"]])
+>     ,("select * from t group by rollup(a,b)"
+>      ,ms [Rollup [SimpleGroup $ Iden "a", SimpleGroup $ Iden "b"]])
+>     ]
+>   where
+>     ms g = makeSelect {qeSelectList = [(Nothing,Star)]
+>                       ,qeFrom = [TRSimple "t"]
+>                       ,qeGroupBy = g}
+
+> randomGroupBy :: TestItem
+> randomGroupBy = Group "randomGroupBy" $ map ParseQueryExpr
+>     ["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\
+>      \(a),\n\
+>      \() )"
+>     ,"select * from t GROUP BY ROLLUP(b,a)"
+>     ,"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\
+>      \(a,b),\n\
+>      \(a,c),\n\
+>      \(b,c),\n\
+>      \(a),\n\
+>      \(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\
+>      \(Province),\n\
+>      \() )"
+>     ,"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\
+>      \(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\
+>      \(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\
+>      \(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\
+>      \(a,b),\n\
+>      \(a,c),\n\
+>      \(a),\n\
+>      \(b,c),\n\
+>      \(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\
+>      \(a,b,c),\n\
+>      \(a,b),\n\
+>      \(a,c,d),\n\
+>      \(a,c),\n\
+>      \(a),\n\
+>      \(b,c,d),\n\
+>      \(b,c),\n\
+>      \(b),\n\
+>      \(c,d),\n\
+>      \(c),\n\
+>      \() )"
+>     ,"select * from t GROUP BY a, ROLLUP(a,b)"
+>     ,"select * from t GROUP BY GROUPING SETS((a,b),\n\
+>      \(a) )"
+>     ,"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\
+>      \YEAR(Sales_Date), MONTH(Sales_Date) )"
+
+>     ,"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\
+>      \WHERE WEEK(SALES_DATE) = 13\n\
+>      \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\
+>      \DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
+>      \SALES_PERSON, SUM(SALES) AS UNITS_SOLD\n\
+>      \FROM SALES\n\
+>      \WHERE WEEK(SALES_DATE) = 13\n\
+>      \GROUP BY GROUPING SETS ( (WEEK(SALES_DATE), SALES_PERSON),\n\
+>      \(DAYOFWEEK(SALES_DATE), SALES_PERSON))\n\
+>      \ORDER BY WEEK, DAY_WEEK, SALES_PERSON"
+
+>     ,"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\
+>      \WHERE WEEK(SALES_DATE) = 13\n\
+>      \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\
+>      \DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
+>      \SALES_PERSON, SUM(SALES) AS UNITS_SOLD\n\
+>      \FROM SALES\n\
+>      \WHERE WEEK(SALES_DATE) = 13\n\
+>      \GROUP BY CUBE ( WEEK(SALES_DATE), DAYOFWEEK(SALES_DATE), SALES_PERSON )\n\
+>      \ORDER BY WEEK, DAY_WEEK, SALES_PERSON"
+
+>     ,"SELECT SALES_PERSON,\n\
+>      \MONTH(SALES_DATE) AS MONTH,\n\
+>      \SUM(SALES) AS UNITS_SOLD\n\
+>      \FROM SALES\n\
+>      \GROUP BY GROUPING SETS ( (SALES_PERSON, MONTH(SALES_DATE)),\n\
+>      \()\n\
+>      \)\n\
+>      \ORDER BY SALES_PERSON, MONTH"
+
+>     ,"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\
+>      \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\
+>      \DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
+>      \MONTH(SALES_DATE) AS MONTH,\n\
+>      \REGION,\n\
+>      \SUM(SALES) AS UNITS_SOLD\n\
+>      \FROM SALES\n\
+>      \GROUP BY GROUPING SETS ( ROLLUP( WEEK(SALES_DATE), DAYOFWEEK(SALES_DATE) ),\n\
+>      \ROLLUP( MONTH(SALES_DATE), REGION ) )\n\
+>      \ORDER BY WEEK, DAY_WEEK, MONTH, REGION"
+
+>     ,"SELECT R1, R2,\n\
+>      \WEEK(SALES_DATE) AS WEEK,\n\
+>      \DAYOFWEEK(SALES_DATE) AS DAY_WEEK,\n\
+>      \MONTH(SALES_DATE) AS MONTH,\n\
+>      \REGION, SUM(SALES) AS UNITS_SOLD\n\
+>      \FROM SALES,(VALUES('GROUP 1','GROUP 2')) AS X(R1,R2)\n\
+>      \GROUP BY GROUPING SETS ((R1, ROLLUP(WEEK(SALES_DATE),\n\
+>      \DAYOFWEEK(SALES_DATE))),\n\
+>      \(R2,ROLLUP( MONTH(SALES_DATE), REGION ) ))\n\
+>      \ORDER BY WEEK, DAY_WEEK, MONTH, REGION"
+
+>     {-,"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\
+>      \REGION, SUM(SALES) AS UNITS_SOLD\n\
+>      \FROM SALES,(VALUES('GROUP 1','GROUP 2')) AS X(R1,R2)\n\
+>      \GROUP BY GROUPING SETS ((R1, ROLLUP(WEEK(SALES_DATE),\n\
+>      \DAYOFWEEK(SALES_DATE))),\n\
+>      \(R2,ROLLUP( MONTH(SALES_DATE), REGION ) ))\n\
+>      \ORDER BY GROUP, WEEK, DAY_WEEK, MONTH, REGION"-}
+>      -- as group - needs more subtle keyword blacklisting
+
+>     ,"SELECT MONTH(SALES_DATE) AS MONTH,\n\
+>      \REGION,\n\
+>      \SUM(SALES) AS UNITS_SOLD,\n\
+>      \MAX(SALES) AS BEST_SALE,\n\
+>      \CAST(ROUND(AVG(DECIMAL(SALES)),2) AS DECIMAL(5,2)) AS AVG_UNITS_SOLD\n\
+>      \FROM SALES\n\
+>      \GROUP BY CUBE(MONTH(SALES_DATE),REGION)\n\
+>      \ORDER BY MONTH, REGION"
+
+>     ]
diff --git a/tools/Language/SQL/SimpleSQL/Postgres.lhs b/tools/Language/SQL/SimpleSQL/Postgres.lhs
new file mode 100644
--- /dev/null
+++ b/tools/Language/SQL/SimpleSQL/Postgres.lhs
@@ -0,0 +1,269 @@
+
+Here are some tests taken from the SQL in the postgres manual. Almost
+all of the postgres specific syntax has been skipped, this can be
+revisited when the dialect support is added.
+
+> module Language.SQL.SimpleSQL.Postgres (postgresTests) where
+
+> import Language.SQL.SimpleSQL.TestTypes
+> --import Language.SQL.SimpleSQL.Syntax
+
+> postgresTests :: TestItem
+> postgresTests = Group "postgresTests" $ map ParseQueryExpr
+
+lexical syntax section
+
+TODO: get all the commented out tests working
+
+>     [-- "SELECT 'foo'\n\
+>     -- \'bar';" -- this should parse as select 'foobar'
+>     -- ,
+>      "SELECT name, (SELECT max(pop) FROM cities\n\
+>      \ WHERE cities.state = states.name)\n\
+>      \    FROM states;"
+>     ,"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));"
+
+>     ,"SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same');"
+
+>     ,"SELECT ROW(table.*) IS NULL FROM table;"
+
+>     ,"SELECT true OR somefunc();"
+
+>     ,"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';"
+
+>     ,"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\
+>      \ 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\
+>      \    WHERE foosubid IN (\n\
+>      \                        SELECT foosubid\n\
+>      \                        FROM getfoo(foo.fooid) z\n\
+>      \                        WHERE z.fooid = foo.fooid\n\
+>      \                      );"
+>     {-,"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;"
+
+>     {-,"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\
+>      \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\
+>      \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"
+
+>     ,"SELECT * FROM fdt WHERE c1 IN (1, 2, 3)"
+
+>     ,"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)"
+
+>     ,"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)"
+
+>     ,"SELECT * FROM test1;"
+
+>     ,"SELECT x FROM test1 GROUP BY x;"
+>     ,"SELECT x, sum(y) FROM test1 GROUP BY x;"
+>     ,"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\
+>      \    FROM products p LEFT JOIN sales s USING (product_id)\n\
+>      \    WHERE s.date > 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"
+
+>     ,"SELECT tbl1.a, tbl2.a, tbl1.b FROM t"
+
+>     ,"SELECT tbl1.*, tbl2.a FROM t"
+
+>     ,"SELECT a AS value, b + c AS sum FROM t"
+
+>     ,"SELECT a \"value\", b + c AS sum FROM t"
+
+>     ,"SELECT DISTINCT select_list t"
+
+>     ,"VALUES (1, 'one'), (2, 'two'), (3, 'three');"
+
+>     ,"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);"
+
+>     ,"WITH regional_sales AS (\n\
+>      \        SELECT region, SUM(amount) AS total_sales\n\
+>      \        FROM orders\n\
+>      \        GROUP BY region\n\
+>      \     ), top_regions AS (\n\
+>      \        SELECT region\n\
+>      \        FROM regional_sales\n\
+>      \        WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)\n\
+>      \     )\n\
+>      \SELECT region,\n\
+>      \       product,\n\
+>      \       SUM(quantity) AS product_units,\n\
+>      \       SUM(amount) AS product_sales\n\
+>      \FROM orders\n\
+>      \WHERE region IN (SELECT region FROM top_regions)\n\
+>      \GROUP BY region, product;"
+
+>     ,"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\
+>      \    SELECT sub_part, part, quantity FROM parts WHERE part = 'our_product'\n\
+>      \  UNION ALL\n\
+>      \    SELECT p.sub_part, p.part, p.quantity\n\
+>      \    FROM included_parts pr, parts p\n\
+>      \    WHERE p.part = pr.sub_part\n\
+>      \  )\n\
+>      \SELECT sub_part, SUM(quantity) as total_quantity\n\
+>      \FROM included_parts\n\
+>      \GROUP BY sub_part"
+
+>     ,"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\
+>      \        SELECT g.id, g.link, g.data, sg.depth + 1\n\
+>      \        FROM graph g, search_graph sg\n\
+>      \        WHERE g.id = sg.link\n\
+>      \)\n\
+>      \SELECT * FROM search_graph;"
+
+>     {-,"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\
+>      \        FROM graph g\n\
+>      \      UNION ALL\n\
+>      \        SELECT g.id, g.link, g.data, sg.depth + 1,\n\
+>      \          path || g.id,\n\
+>      \          g.id = ANY(path)\n\
+>      \        FROM graph g, search_graph sg\n\
+>      \        WHERE g.id = sg.link AND NOT cycle\n\
+>      \)\n\
+>      \SELECT * FROM search_graph;"-} -- ARRAY
+
+>     {-,"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\
+>      \        FROM graph g\n\
+>      \      UNION ALL\n\
+>      \        SELECT g.id, g.link, g.data, sg.depth + 1,\n\
+>      \          path || ROW(g.f1, g.f2),\n\
+>      \          ROW(g.f1, g.f2) = ANY(path)\n\
+>      \        FROM graph g, search_graph sg\n\
+>      \        WHERE g.id = sg.link AND NOT cycle\n\
+>      \)\n\
+>      \SELECT * FROM search_graph;"-} -- ARRAY
+
+>     ,"WITH RECURSIVE t(n) AS (\n\
+>      \    SELECT 1\n\
+>      \  UNION ALL\n\
+>      \    SELECT n+1 FROM t\n\
+>      \)\n\
+>      \SELECT n FROM t LIMIT 100;"
+
+select page reference
+
+>     ,"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\
+>      \    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;"
+
+>     ,"SELECT distributors.name\n\
+>      \    FROM distributors\n\
+>      \    WHERE distributors.name LIKE 'W%'\n\
+>      \UNION\n\
+>      \SELECT actors.name\n\
+>      \    FROM actors\n\
+>      \    WHERE actors.name LIKE 'W%';"
+
+>     ,"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\
+>      \    SELECT 1, employee_name, manager_name\n\
+>      \    FROM employee\n\
+>      \    WHERE manager_name = 'Mary'\n\
+>      \  UNION ALL\n\
+>      \    SELECT er.distance + 1, e.employee_name, e.manager_name\n\
+>      \    FROM employee_recursive er, employee e\n\
+>      \    WHERE er.employee_name = e.manager_name\n\
+>      \  )\n\
+>      \SELECT distance, employee_name FROM employee_recursive;"
+
+>     ,"SELECT m.name AS mname, pname\n\
+>      \FROM manufacturers m, LATERAL get_product_names(m.id) pname;"
+
+>     ,"SELECT m.name AS mname, pname\n\
+>      \FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true;"
+
+>     ,"SELECT 2+2;"
+
+>     ,"SELECT distributors.* WHERE distributors.name = 'Westward';"
+
+>     ]
diff --git a/tools/Language/SQL/SimpleSQL/QueryExprComponents.lhs b/tools/Language/SQL/SimpleSQL/QueryExprComponents.lhs
new file mode 100644
--- /dev/null
+++ b/tools/Language/SQL/SimpleSQL/QueryExprComponents.lhs
@@ -0,0 +1,202 @@
+
+These are the tests for the query expression components apart from the
+table refs which are in a separate file.
+
+
+These are a few misc tests which don't fit anywhere else.
+
+> {-# LANGUAGE OverloadedStrings #-}
+> module Language.SQL.SimpleSQL.QueryExprComponents (queryExprComponentTests) where
+
+> import Language.SQL.SimpleSQL.TestTypes
+> import Language.SQL.SimpleSQL.Syntax
+
+
+> queryExprComponentTests :: TestItem
+> queryExprComponentTests = Group "queryExprComponentTests"
+>     [duplicates
+>     ,selectLists
+>     ,whereClause
+>     ,having
+>     ,orderBy
+>     ,offsetFetch
+>     ,combos
+>     ,withQueries
+>     ,values
+>     ,tables
+>     ]
+
+
+
+> duplicates :: TestItem
+> duplicates = Group "duplicates" $ map (uncurry TestQueryExpr)
+>     [("select a from t" ,ms All)
+>     ,("select all a from t" ,ms All)
+>     ,("select distinct a from t", ms Distinct)
+>     ]
+>  where
+>    ms d = makeSelect
+>           {qeDuplicates = d
+>           ,qeSelectList = [(Nothing,Iden "a")]
+>           ,qeFrom = [TRSimple "t"]}
+
+> selectLists :: TestItem
+> selectLists = Group "selectLists" $ map (uncurry TestQueryExpr)
+>     [("select 1",
+>       makeSelect {qeSelectList = [(Nothing,NumLit "1")]})
+
+>     ,("select a"
+>      ,makeSelect {qeSelectList = [(Nothing,Iden "a")]})
+
+>     ,("select a,b"
+>      ,makeSelect {qeSelectList = [(Nothing,Iden "a")
+>                                  ,(Nothing,Iden "b")]})
+
+>     ,("select 1+2,3+4"
+>      ,makeSelect {qeSelectList =
+>                      [(Nothing,BinOp (NumLit "1") "+" (NumLit "2"))
+>                      ,(Nothing,BinOp (NumLit "3") "+" (NumLit "4"))]})
+
+>     ,("select a as a, /*comment*/ b as b"
+>      ,makeSelect {qeSelectList = [(Just "a", Iden "a")
+>                                  ,(Just "b", Iden "b")]})
+
+>     ,("select a a, b b"
+>      ,makeSelect {qeSelectList = [(Just "a", Iden "a")
+>                                  ,(Just "b", Iden "b")]})
+>     ]
+
+> whereClause :: TestItem
+> whereClause = Group "whereClause" $ map (uncurry TestQueryExpr)
+>     [("select a from t where a = 5"
+>      ,makeSelect {qeSelectList = [(Nothing,Iden "a")]
+>                  ,qeFrom = [TRSimple "t"]
+>                  ,qeWhere = Just $ BinOp (Iden "a") "=" (NumLit "5")})
+>     ]
+
+> having :: TestItem
+> having = Group "having" $ map (uncurry TestQueryExpr)
+>     [("select a,sum(b) from t group by a having sum(b) > 5"
+>      ,makeSelect {qeSelectList = [(Nothing, Iden "a")
+>                                  ,(Nothing, App "sum" [Iden "b"])]
+>                  ,qeFrom = [TRSimple "t"]
+>                  ,qeGroupBy = [SimpleGroup $ Iden "a"]
+>                  ,qeHaving = Just $ BinOp (App "sum" [Iden "b"])
+>                                           ">" (NumLit "5")
+>                  })
+>     ]
+
+> orderBy :: TestItem
+> orderBy = Group "orderBy" $ map (uncurry TestQueryExpr)
+>     [("select a from t order by a"
+>      ,ms [OrderField (Iden "a") Asc NullsOrderDefault])
+
+>     ,("select a from t order by a, b"
+>      ,ms [OrderField (Iden "a") Asc NullsOrderDefault
+>          ,OrderField (Iden "b") Asc NullsOrderDefault])
+
+>     ,("select a from t order by a asc"
+>      ,ms [OrderField (Iden "a") Asc NullsOrderDefault])
+
+>     ,("select a from t order by a desc, b desc"
+>      ,ms [OrderField (Iden "a") Desc NullsOrderDefault
+>          ,OrderField (Iden "b") Desc NullsOrderDefault])
+
+>     ,("select a from t order by a desc nulls first, b desc nulls last"
+>      ,ms [OrderField (Iden "a") Desc NullsFirst
+>          ,OrderField (Iden "b") Desc NullsLast])
+
+>     ]
+>   where
+>     ms o = makeSelect {qeSelectList = [(Nothing,Iden "a")]
+>                       ,qeFrom = [TRSimple "t"]
+>                       ,qeOrderBy = o}
+
+> offsetFetch :: TestItem
+> offsetFetch = Group "offsetFetch" $ map (uncurry TestQueryExpr)
+>     [-- 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"))
+>      -- postgres
+>     ,("select a from t limit 10 offset 5"
+>      ,ms (Just $ NumLit "5") (Just $ NumLit "10"))
+>     ]
+>   where
+>     ms o l = makeSelect
+>              {qeSelectList = [(Nothing,Iden "a")]
+>              ,qeFrom = [TRSimple "t"]
+>              ,qeOffset = o
+>              ,qeFetch = l}
+
+> combos :: TestItem
+> combos = Group "combos" $ map (uncurry TestQueryExpr)
+>     [("select a from t union select b from u"
+>      ,CombineQueryExpr ms1 Union All Respectively ms2)
+
+>     ,("select a from t intersect select b from u"
+>      ,CombineQueryExpr ms1 Intersect All Respectively ms2)
+
+>     ,("select a from t except all select b from u"
+>      ,CombineQueryExpr ms1 Except All Respectively ms2)
+
+>     ,("select a from t union distinct corresponding \
+>       \select b from u"
+>      ,CombineQueryExpr ms1 Union Distinct Corresponding ms2)
+
+>     ,("select a from t union select a from t union select a from t"
+>      -- TODO: union should be left associative. I think the others also
+>      -- so this needs to be fixed (new optionSuffix variation which
+>      -- handles this)
+>      ,CombineQueryExpr ms1 Union All Respectively
+>        (CombineQueryExpr ms1 Union All Respectively ms1))
+>     ]
+>   where
+>     ms1 = makeSelect
+>           {qeSelectList = [(Nothing,Iden "a")]
+>           ,qeFrom = [TRSimple "t"]}
+>     ms2 = makeSelect
+>           {qeSelectList = [(Nothing,Iden "b")]
+>           ,qeFrom = [TRSimple "u"]}
+
+
+> withQueries :: TestItem
+> withQueries = Group "with queries" $ map (uncurry TestQueryExpr)
+>     [("with u as (select a from t) select a from u"
+>      ,With False [(Alias "u" Nothing, ms1)] ms2)
+
+>     ,("with u(b) as (select a from t) select a from u"
+>      ,With False [(Alias "u" (Just ["b"]), ms1)] ms2)
+
+>     ,("with x as (select a from t),\n\
+>       \     u as (select a from x)\n\
+>       \select a from u"
+>      ,With False [(Alias "x" Nothing, ms1), (Alias "u" Nothing,ms3)] ms2)
+
+>     ,("with recursive u as (select a from t) select a from u"
+>      ,With True [(Alias "u" Nothing, ms1)] ms2)
+>     ]
+>  where
+>    ms c t = makeSelect
+>             {qeSelectList = [(Nothing,Iden c)]
+>             ,qeFrom = [TRSimple t]}
+>    ms1 = ms "a" "t"
+>    ms2 = ms "a" "u"
+>    ms3 = ms "a" "x"
+
+> values :: TestItem
+> values = Group "values" $ map (uncurry TestQueryExpr)
+>     [("values (1,2),(3,4)"
+>       ,Values [[NumLit "1", NumLit "2"]
+>               ,[NumLit "3", NumLit "4"]])
+>     ]
+
+> tables :: TestItem
+> tables = Group "tables" $ map (uncurry TestQueryExpr)
+>     [("table tbl", Table "tbl")
+>     ]
diff --git a/tools/Language/SQL/SimpleSQL/QueryExprs.lhs b/tools/Language/SQL/SimpleSQL/QueryExprs.lhs
new file mode 100644
--- /dev/null
+++ b/tools/Language/SQL/SimpleSQL/QueryExprs.lhs
@@ -0,0 +1,18 @@
+
+These are the tests for the queryExprs parsing which parses multiple
+query expressions from one string.
+
+> module Language.SQL.SimpleSQL.QueryExprs (queryExprsTests) where
+
+> import Language.SQL.SimpleSQL.TestTypes
+> import Language.SQL.SimpleSQL.Syntax
+
+> queryExprsTests :: TestItem
+> queryExprsTests = Group "query exprs" $ map (uncurry TestQueryExprs)
+>     [("select 1",[ms])
+>     ,("select 1;",[ms])
+>     ,("select 1;select 1",[ms,ms])
+>     ,(" select 1;select 1; ",[ms,ms])
+>     ]
+>   where
+>     ms = makeSelect {qeSelectList = [(Nothing,NumLit "1")]}
diff --git a/tools/Language/SQL/SimpleSQL/ScalarExprs.lhs b/tools/Language/SQL/SimpleSQL/ScalarExprs.lhs
new file mode 100644
--- /dev/null
+++ b/tools/Language/SQL/SimpleSQL/ScalarExprs.lhs
@@ -0,0 +1,304 @@
+
+Tests for parsing scalar expressions
+
+> {-# LANGUAGE OverloadedStrings #-}
+> module Language.SQL.SimpleSQL.ScalarExprs (scalarExprTests) where
+
+> import Language.SQL.SimpleSQL.TestTypes
+> import Language.SQL.SimpleSQL.Syntax
+
+> scalarExprTests :: TestItem
+> scalarExprTests = Group "scalarExprTests"
+>     [literals
+>     ,identifiers
+>     ,star
+>     ,dots
+>     ,app
+>     ,caseexp
+>     ,operators
+>     ,parens
+>     ,subqueries
+>     ,aggregates
+>     ,windowFunctions
+>     ]
+
+> literals :: TestItem
+> literals = Group "literals" $ map (uncurry TestScalarExpr)
+>     [("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 "3" "day" Nothing)
+>      ,("interval '3' day (3)", IntervalLit "3" "day" $ Just 3)
+>      ,("interval '3 weeks'", TypedLit (TypeName "interval") "3 weeks")
+>     ]
+
+> identifiers :: TestItem
+> identifiers = Group "identifiers" $ map (uncurry TestScalarExpr)
+>     [("iden1", Iden "iden1")
+>     --,("t.a", Iden2 "t" "a")
+>     ,("\"quoted identifier\"", Iden $ QName "quoted identifier")
+>     ]
+
+> star :: TestItem
+> star = Group "star" $ map (uncurry TestScalarExpr)
+>     [("*", Star)
+>     --,("t.*", Star2 "t")
+>     --,("ROW(t.*,42)", App "ROW" [Star2 "t", NumLit "42"])
+>     ]
+
+> dots :: TestItem
+> dots = Group "dot" $ map (uncurry TestScalarExpr)
+>     [("t.a", BinOp (Iden "t") "." (Iden "a"))
+>     ,("t.*", BinOp (Iden "t") "." Star)
+>     ,("a.b.c", BinOp (BinOp (Iden "a") "." (Iden "b")) "." (Iden "c"))
+>     ,("ROW(t.*,42)", App "ROW" [BinOp (Iden "t") "." Star, NumLit "42"])
+>     ]
+
+> app :: TestItem
+> app = Group "app" $ map (uncurry TestScalarExpr)
+>     [("f()", App "f" [])
+>     ,("f(a)", App "f" [Iden "a"])
+>     ,("f(a,b)", App "f" [Iden "a", Iden "b"])
+>     ]
+
+> caseexp :: TestItem
+> caseexp = Group "caseexp" $ map (uncurry TestScalarExpr)
+>     [("case a when 1 then 2 end"
+>      ,Case (Just $ Iden "a") [([NumLit "1"]
+>                               ,NumLit "2")] Nothing)
+
+>     ,("case a when 1 then 2 when 3 then 4 end"
+>      ,Case (Just $ Iden "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 "a") [([NumLit "1"], NumLit "2")
+>                              ,([NumLit "3"], NumLit "4")]
+>                              (Just $ NumLit "5"))
+
+>     ,("case when a=1 then 2 when a=3 then 4 else 5 end"
+>      ,Case Nothing [([BinOp (Iden "a") "=" (NumLit "1")], NumLit "2")
+>                    ,([BinOp (Iden "a") "=" (NumLit "3")], NumLit "4")]
+>                    (Just $ NumLit "5"))
+
+>     ,("case a when 1,2 then 10 when 3,4 then 20 end"
+>      ,Case (Just $ Iden "a") [([NumLit "1",NumLit "2"]
+>                                ,NumLit "10")
+>                              ,([NumLit "3",NumLit "4"]
+>                                ,NumLit "20")]
+>                              Nothing)
+
+>     ]
+
+> operators :: TestItem
+> operators = Group "operators"
+>     [binaryOperators
+>     ,unaryOperators
+>     ,casts
+>     ,miscOps]
+
+> binaryOperators :: TestItem
+> binaryOperators = Group "binaryOperators" $ map (uncurry TestScalarExpr)
+>     [("a + b", BinOp (Iden "a") "+" (Iden "b"))
+>      -- sanity check fixities
+>      -- todo: add more fixity checking
+
+>     ,("a + b * c"
+>      ,BinOp  (Iden "a") "+"
+>              (BinOp (Iden "b") "*" (Iden "c")))
+
+>     ,("a * b + c"
+>      ,BinOp (BinOp (Iden "a") "*" (Iden "b"))
+>             "+" (Iden "c"))
+>     ]
+
+> unaryOperators :: TestItem
+> unaryOperators = Group "unaryOperators" $ map (uncurry TestScalarExpr)
+>     [("not a", PrefixOp "not" $ Iden "a")
+>     ,("not not a", PrefixOp "not" $ PrefixOp "not" $ Iden "a")
+>     ,("+a", PrefixOp "+" $ Iden "a")
+>     ,("-a", PrefixOp "-" $ Iden "a")
+>     ]
+
+
+> casts :: TestItem
+> casts = Group "operators" $ map (uncurry TestScalarExpr)
+>     [("cast('1' as int)"
+>      ,Cast (StringLit "1") $ TypeName "int")
+
+>     ,("int '3'"
+>      ,TypedLit (TypeName "int") "3")
+
+>     ,("cast('1' as double precision)"
+>      ,Cast (StringLit "1") $ TypeName "double precision")
+
+>     ,("cast('1' as float(8))"
+>      ,Cast (StringLit "1") $ PrecTypeName "float" 8)
+
+>     ,("cast('1' as decimal(15,2))"
+>      ,Cast (StringLit "1") $ PrecScaleTypeName "decimal" 15 2)
+
+
+>     ,("double precision '3'"
+>      ,TypedLit (TypeName "double precision") "3")
+>     ]
+
+> subqueries :: TestItem
+> subqueries = Group "unaryOperators" $ map (uncurry TestScalarExpr)
+>     [("exists (select a from t)", SubQueryExpr SqExists ms)
+>     ,("(select a from t)", SubQueryExpr SqSq ms)
+
+>     ,("a in (select a from t)"
+>      ,In True (Iden "a") (InQueryExpr ms))
+
+>     ,("a not in (select a from t)"
+>      ,In False (Iden "a") (InQueryExpr ms))
+
+>     ,("a > all (select a from t)"
+>      ,BinOp (Iden "a") ">" (SubQueryExpr SqAll ms))
+
+>     ,("a = some (select a from t)"
+>      ,BinOp (Iden "a") "=" (SubQueryExpr SqSome ms))
+
+>     ,("a <= any (select a from t)"
+>      ,BinOp (Iden "a") "<=" (SubQueryExpr SqAny ms))
+>     ]
+>   where
+>     ms = makeSelect
+>          {qeSelectList = [(Nothing,Iden "a")]
+>          ,qeFrom = [TRSimple "t"]
+>          }
+
+> miscOps :: TestItem
+> miscOps = Group "unaryOperators" $ map (uncurry TestScalarExpr)
+>     [("a in (1,2,3)"
+>      ,In True (Iden "a") $ InList $ map NumLit ["1","2","3"])
+
+>     ,("a between b and c", SpecialOp "between" [Iden "a"
+>                                                ,Iden "b"
+>                                                ,Iden "c"])
+
+>     ,("a not between b and c", SpecialOp "not between" [Iden "a"
+>                                                        ,Iden "b"
+>                                                        ,Iden "c"])
+
+>     ,("a is null", PostfixOp "is null" (Iden "a"))
+>     ,("a is not null", PostfixOp "is not null" (Iden "a"))
+>     ,("a is true", PostfixOp "is true" (Iden "a"))
+>     ,("a is not true", PostfixOp "is not true" (Iden "a"))
+>     ,("a is false", PostfixOp "is false" (Iden "a"))
+>     ,("a is not false", PostfixOp "is not false" (Iden "a"))
+>     ,("a is unknown", PostfixOp "is unknown" (Iden "a"))
+>     ,("a is not unknown", PostfixOp "is not unknown" (Iden "a"))
+>     ,("a is distinct from b", BinOp (Iden "a") "is distinct from"(Iden "b"))
+
+>     ,("a is not distinct from b"
+>      ,BinOp (Iden "a") "is not distinct from" (Iden "b"))
+
+>     ,("a like b", BinOp (Iden "a") "like" (Iden "b"))
+>     ,("a not like b", BinOp (Iden "a") "not like" (Iden "b"))
+>     ,("a is similar to b", BinOp (Iden "a") "is similar to" (Iden "b"))
+
+>     ,("a is not similar to b"
+>      ,BinOp (Iden "a") "is not similar to" (Iden "b"))
+
+>     ,("a overlaps b", BinOp (Iden "a") "overlaps" (Iden "b"))
+>     ,("extract(day from t)", SpecialOp "extract" [Iden "day", Iden "t"])
+
+>     ,("substring(x from 1 for 2)"
+>      ,SpecialOp "substring" [Iden "x", NumLit "1", NumLit "2"])
+
+>     ,("(1,2)"
+>      ,SpecialOp "rowctor" [NumLit "1", NumLit "2"])
+
+>     ]
+
+> aggregates :: TestItem
+> aggregates = Group "aggregates" $ map (uncurry TestScalarExpr)
+>     [("count(*)",App "count" [Star])
+
+>     ,("sum(a order by a)"
+>     ,AggregateApp "sum" Nothing [Iden "a"]
+>                   [OrderField (Iden "a") Asc NullsOrderDefault])
+
+>     ,("sum(all a)"
+>     ,AggregateApp "sum" (Just All) [Iden "a"] [])
+
+>     ,("count(distinct a)"
+>     ,AggregateApp "count" (Just Distinct) [Iden "a"] [])
+>     ]
+
+> windowFunctions :: TestItem
+> windowFunctions = Group "windowFunctions" $ map (uncurry TestScalarExpr)
+>     [("max(a) over ()", WindowApp "max" [Iden "a"] [] [] Nothing)
+>     ,("count(*) over ()", WindowApp "count" [Star] [] [] Nothing)
+
+>     ,("max(a) over (partition by b)"
+>      ,WindowApp "max" [Iden "a"] [Iden "b"] [] Nothing)
+
+>     ,("max(a) over (partition by b,c)"
+>      ,WindowApp "max" [Iden "a"] [Iden "b",Iden "c"] [] Nothing)
+
+>     ,("sum(a) over (order by b)"
+>      ,WindowApp "sum" [Iden "a"] []
+>           [OrderField (Iden "b") Asc NullsOrderDefault] Nothing)
+
+>     ,("sum(a) over (order by b desc,c)"
+>      ,WindowApp "sum" [Iden "a"] []
+>           [OrderField (Iden "b") Desc NullsOrderDefault
+>           ,OrderField (Iden "c") Asc NullsOrderDefault] Nothing)
+
+>     ,("sum(a) over (partition by b order by c)"
+>      ,WindowApp "sum" [Iden "a"] [Iden "b"]
+>           [OrderField (Iden "c") Asc NullsOrderDefault] Nothing)
+
+>     ,("sum(a) over (partition by b order by c range unbounded preceding)"
+>      ,WindowApp "sum" [Iden "a"] [Iden "b"]
+>       [OrderField (Iden "c") Asc NullsOrderDefault]
+>       $ Just $ FrameFrom FrameRange UnboundedPreceding)
+
+>     ,("sum(a) over (partition by b order by c range 5 preceding)"
+>      ,WindowApp "sum" [Iden "a"] [Iden "b"]
+>       [OrderField (Iden "c") Asc NullsOrderDefault]
+>       $ Just $ FrameFrom FrameRange $ Preceding (NumLit "5"))
+
+>     ,("sum(a) over (partition by b order by c range current row)"
+>      ,WindowApp "sum" [Iden "a"] [Iden "b"]
+>       [OrderField (Iden "c") Asc NullsOrderDefault]
+>       $ Just $ FrameFrom FrameRange Current)
+
+>     ,("sum(a) over (partition by b order by c rows 5 following)"
+>      ,WindowApp "sum" [Iden "a"] [Iden "b"]
+>       [OrderField (Iden "c") Asc NullsOrderDefault]
+>       $ Just $ FrameFrom FrameRows $ Following (NumLit "5"))
+
+>     ,("sum(a) over (partition by b order by c range unbounded following)"
+>      ,WindowApp "sum" [Iden "a"] [Iden "b"]
+>       [OrderField (Iden "c") Asc NullsOrderDefault]
+>       $ Just $ FrameFrom FrameRange UnboundedFollowing)
+
+>     ,("sum(a) over (partition by b order by c \n\
+>       \range between 5 preceding and 5 following)"
+>      ,WindowApp "sum" [Iden "a"] [Iden "b"]
+>       [OrderField (Iden "c") Asc NullsOrderDefault]
+>       $ Just $ FrameBetween FrameRange
+>                             (Preceding (NumLit "5"))
+>                             (Following (NumLit "5")))
+
+>     ]
+
+> parens :: TestItem
+> parens = Group "parens" $ map (uncurry TestScalarExpr)
+>     [("(a)", Parens (Iden "a"))
+>     ,("(a + b)", Parens (BinOp (Iden "a") "+" (Iden "b")))
+>     ]
diff --git a/tools/Language/SQL/SimpleSQL/TableRefs.lhs b/tools/Language/SQL/SimpleSQL/TableRefs.lhs
new file mode 100644
--- /dev/null
+++ b/tools/Language/SQL/SimpleSQL/TableRefs.lhs
@@ -0,0 +1,104 @@
+
+These are the tests for parsing focusing on the from part of query
+expression
+
+> {-# LANGUAGE OverloadedStrings #-}
+> module Language.SQL.SimpleSQL.TableRefs (tableRefTests) where
+
+> import Language.SQL.SimpleSQL.TestTypes
+> import Language.SQL.SimpleSQL.Syntax
+
+
+> tableRefTests :: TestItem
+> tableRefTests = Group "tableRefTests" $ map (uncurry TestQueryExpr)
+>     [("select a from t"
+>      ,ms [TRSimple "t"])
+
+>      ,("select a from f(a)"
+>       ,ms [TRFunction "f" [Iden "a"]])
+
+>     ,("select a from t,u"
+>      ,ms [TRSimple "t", TRSimple "u"])
+
+these lateral queries make no sense but the syntax is valid
+
+>     ,("select a from lateral a"
+>      ,ms [TRLateral $ TRSimple "a"])
+
+>     ,("select a from lateral a,b"
+>      ,ms [TRLateral $ TRSimple "a", TRSimple "b"])
+
+>     ,("select a from a, lateral b"
+>      ,ms [TRSimple "a", TRLateral $ TRSimple "b"])
+
+>     ,("select a from a natural join lateral b"
+>      ,ms [TRJoin (TRSimple "a") JInner
+>                  (TRLateral $ TRSimple "b")
+>                  (Just JoinNatural)])
+
+>     -- the lateral binds on the outside of the join which is incorrect
+>     ,("select a from lateral a natural join lateral b"
+>      ,ms [TRJoin (TRLateral $ TRSimple "a") JInner
+>                  (TRLateral $ TRSimple "b")
+>                  (Just JoinNatural)])
+
+
+>     ,("select a from t inner join u on expr"
+>      ,ms [TRJoin (TRSimple "t") JInner (TRSimple "u")
+>                        (Just $ JoinOn $ Iden "expr")])
+
+>     ,("select a from t join u on expr"
+>      ,ms [TRJoin (TRSimple "t") JInner (TRSimple "u")
+>                        (Just $ JoinOn $ Iden "expr")])
+
+>     ,("select a from t left join u on expr"
+>      ,ms [TRJoin (TRSimple "t") JLeft (TRSimple "u")
+>                        (Just $ JoinOn $ Iden "expr")])
+
+>     ,("select a from t right join u on expr"
+>      ,ms [TRJoin (TRSimple "t") JRight (TRSimple "u")
+>                        (Just $ JoinOn $ Iden "expr")])
+
+>     ,("select a from t full join u on expr"
+>      ,ms [TRJoin (TRSimple "t") JFull (TRSimple "u")
+>                        (Just $ JoinOn $ Iden "expr")])
+
+>     ,("select a from t cross join u"
+>      ,ms [TRJoin (TRSimple "t")
+>                        JCross (TRSimple "u") Nothing])
+
+>     ,("select a from t natural inner join u"
+>      ,ms [TRJoin (TRSimple "t") JInner (TRSimple "u")
+>                        (Just JoinNatural)])
+
+>     ,("select a from t inner join u using(a,b)"
+>      ,ms [TRJoin (TRSimple "t") JInner (TRSimple "u")
+>                        (Just $ JoinUsing ["a", "b"])])
+
+>     ,("select a from (select a from t)"
+>      ,ms [TRQueryExpr $ ms [TRSimple "t"]])
+
+>     ,("select a from t as u"
+>      ,ms [TRAlias (TRSimple "t") (Alias "u" Nothing)])
+
+>     ,("select a from t u"
+>      ,ms [TRAlias (TRSimple "t") (Alias "u" Nothing)])
+
+>     ,("select a from t u(b)"
+>      ,ms [TRAlias (TRSimple "t") (Alias "u" $ Just ["b"])])
+
+>     ,("select a from (t cross join u) as u"
+>      ,ms [TRAlias (TRParens $
+>                    TRJoin (TRSimple "t") JCross (TRSimple "u") Nothing)
+>                           (Alias "u" Nothing)])
+>      -- todo: not sure if the associativity is correct
+
+>     ,("select a from t cross join u cross join v",
+>        ms [TRJoin
+>            (TRJoin (TRSimple "t")
+>                    JCross (TRSimple "u") Nothing)
+>            JCross (TRSimple "v") Nothing])
+>     ]
+>   where
+>     ms f = makeSelect {qeSelectList = [(Nothing,Iden "a")]
+>                       ,qeFrom = f}
diff --git a/tools/Language/SQL/SimpleSQL/TestTypes.lhs b/tools/Language/SQL/SimpleSQL/TestTypes.lhs
new file mode 100644
--- /dev/null
+++ b/tools/Language/SQL/SimpleSQL/TestTypes.lhs
@@ -0,0 +1,26 @@
+
+This is the types used to define the tests as pure data. See the
+Tests.lhs module for the 'interpreter'.
+
+> module Language.SQL.SimpleSQL.TestTypes where
+
+> import Language.SQL.SimpleSQL.Syntax
+
+> import Data.String
+
+> data TestItem = Group String [TestItem]
+>               | TestScalarExpr String ScalarExpr
+>               | TestQueryExpr String QueryExpr
+>               | TestQueryExprs String [QueryExpr]
+
+this just checks the sql parses without error, mostly just a
+intermediate when I'm too lazy to write out the parsed AST. These
+should all be TODO to convert to a testqueryexpr test.
+
+>               | ParseQueryExpr String
+>                 deriving (Eq,Show)
+
+hack to make the tests a bit simpler
+
+> instance IsString Name where
+>    fromString = Name
diff --git a/tools/Language/SQL/SimpleSQL/Tests.lhs b/tools/Language/SQL/SimpleSQL/Tests.lhs
--- a/tools/Language/SQL/SimpleSQL/Tests.lhs
+++ b/tools/Language/SQL/SimpleSQL/Tests.lhs
@@ -1,483 +1,54 @@
 
+TODO:
+
+split into multiple files:
+scalar expressions
+tablerefs
+other queryexpr parts: not enough to split into multiple files
+full queries
+tpch tests
+
+
 > module Language.SQL.SimpleSQL.Tests
 >     (testData
 >     ,tests
 >     ,TestItem(..)
 >     ) where
 
-> import Language.SQL.SimpleSQL.Syntax
-> import Language.SQL.SimpleSQL.Pretty
-> import Language.SQL.SimpleSQL.Parser
-> import qualified Test.HUnit as H
-> import Tpch
 > import Test.Framework
 > import Test.Framework.Providers.HUnit
-
-> data TestItem = Group String [TestItem]
->               | TestScalarExpr String ScalarExpr
->               | TestQueryExpr String QueryExpr
->               | TestQueryExprs String [QueryExpr]
->               | ParseQueryExpr String
->                 deriving (Eq,Show)
-
-> scalarExprParserTests :: TestItem
-> scalarExprParserTests = Group "scalarExprParserTests"
->     [literals
->     ,identifiers
->     ,star
->     ,app
->     ,caseexp
->     ,operators
->     ,parens
->     ,subqueries
->     ,aggregates
->     ,windowFunctions
->     ]
-
-> literals :: TestItem
-> literals = Group "literals" $ map (uncurry TestScalarExpr)
->     [("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")
->      ,("'1'", StringLit "1")
->      ,("interval '3' day", IntervalLit "3" "day" Nothing)
->      ,("interval '3' day (3)", IntervalLit "3" "day" $ Just 3)
->     ]
-
-> identifiers :: TestItem
-> identifiers = Group "identifiers" $ map (uncurry TestScalarExpr)
->     [("iden1", Iden "iden1")
->     ,("t.a", Iden2 "t" "a")
->     ]
-
-> star :: TestItem
-> star = Group "star" $ map (uncurry TestScalarExpr)
->     [("*", Star)
->     ,("t.*", Star2 "t")
->     ]
-
-> app :: TestItem
-> app = Group "app" $ map (uncurry TestScalarExpr)
->     [("f()", App "f" [])
->     ,("f(a)", App "f" [Iden "a"])
->     ,("f(a,b)", App "f" [Iden "a", Iden "b"])
->     ]
-
-> caseexp :: TestItem
-> caseexp = Group "caseexp" $ map (uncurry TestScalarExpr)
->     [("case a when 1 then 2 end"
->      ,Case (Just $ Iden "a") [(NumLit "1"
->                                     ,NumLit "2")] Nothing)
->     ,("case a when 1 then 2 when 3 then 4 end"
->      ,Case (Just $ Iden "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 "a") [(NumLit "1", NumLit "2")
->                                    ,(NumLit "3", NumLit "4")] (Just $ NumLit "5"))
->     ,("case when a=1 then 2 when a=3 then 4 else 5 end"
->      ,Case Nothing [(BinOp (Iden "a") "=" (NumLit "1"), NumLit "2")
->                    ,(BinOp (Iden "a") "=" (NumLit "3"), NumLit "4")]
->                    (Just $ NumLit "5"))
->     ]
-
-> operators :: TestItem
-> operators = Group "operators"
->     [binaryOperators
->     ,unaryOperators
->     ,casts
->     ,miscOps]
-
-> binaryOperators :: TestItem
-> binaryOperators = Group "binaryOperators" $ map (uncurry TestScalarExpr)
->     [("a + b", BinOp (Iden "a") "+" (Iden "b"))
->      -- sanity check fixities
->      -- todo: add more fixity checking
->     ,("a + b * c"
->      ,BinOp  (Iden "a") "+"
->              (BinOp (Iden "b") "*" (Iden "c")))
->     ,("a * b + c"
->      ,BinOp (BinOp (Iden "a") "*" (Iden "b"))
->             "+" (Iden "c"))
->     ]
-
-> unaryOperators :: TestItem
-> unaryOperators = Group "unaryOperators" $ map (uncurry TestScalarExpr)
->     [("not a", PrefixOp "not" $ Iden "a")
->     ,("not not a", PrefixOp "not" $ PrefixOp "not" $ Iden "a")
->     ,("+a", PrefixOp "+" $ Iden "a")
->     ,("-a", PrefixOp "-" $ Iden "a")
->     ]
-
-
-> casts :: TestItem
-> casts = Group "operators" $ map (uncurry TestScalarExpr)
->     [("cast('1' as int)"
->      ,Cast (StringLit "1") $ TypeName "int")
->     ,("int '3'"
->      ,CastOp (TypeName "int") "3")
->     ,("cast('1' as double precision)"
->      ,Cast (StringLit "1") $ TypeName "double precision")
->     ,("double precision '3'"
->      ,CastOp (TypeName "double precision") "3")
->     ]
-
-> subqueries :: TestItem
-> subqueries = Group "unaryOperators" $ map (uncurry TestScalarExpr)
->     [("exists (select a from t)", SubQueryExpr SqExists ms)
->     ,("(select a from t)", SubQueryExpr SqSq ms)
->     ,("a in (select a from t)"
->      ,In True (Iden "a") (InQueryExpr ms))
->     ,("a not in (select a from t)"
->      ,In False (Iden "a") (InQueryExpr ms))
->     ,("a > all (select a from t)"
->      ,BinOp (Iden "a") ">" (SubQueryExpr SqAll ms))
->     ,("a = some (select a from t)"
->      ,BinOp (Iden "a") "=" (SubQueryExpr SqSome ms))
->     ,("a <= any (select a from t)"
->      ,BinOp (Iden "a") "<=" (SubQueryExpr SqAny ms))
->     ]
->   where
->     ms = makeSelect
->          {qeSelectList = [(Nothing,Iden "a")]
->          ,qeFrom = [TRSimple "t"]
->          }
-
-> miscOps :: TestItem
-> miscOps = Group "unaryOperators" $ map (uncurry TestScalarExpr)
->     [("a in (1,2,3)"
->      ,In True (Iden "a") $ InList $ map NumLit ["1","2","3"])
->     ,("a between b and c", SpecialOp "between" [Iden "a"
->                                                ,Iden "b"
->                                                ,Iden "c"])
->     ,("a not between b and c", SpecialOp "not between" [Iden "a"
->                                                        ,Iden "b"
->                                                        ,Iden "c"])
->     ,("a is null", PostfixOp "is null" (Iden "a"))
->     ,("a is not null", PostfixOp "is not null" (Iden "a"))
->     ,("a is true", PostfixOp "is true" (Iden "a"))
->     ,("a is not true", PostfixOp "is not true" (Iden "a"))
->     ,("a is false", PostfixOp "is false" (Iden "a"))
->     ,("a is not false", PostfixOp "is not false" (Iden "a"))
->     ,("a is unknown", PostfixOp "is unknown" (Iden "a"))
->     ,("a is not unknown", PostfixOp "is not unknown" (Iden "a"))
->     ,("a is distinct from b", BinOp (Iden "a") "is distinct from"(Iden "b"))
->     ,("a is not distinct from b"
->      ,BinOp (Iden "a") "is not distinct from" (Iden "b"))
->     ,("a like b", BinOp (Iden "a") "like" (Iden "b"))
->     ,("a not like b", BinOp (Iden "a") "not like" (Iden "b"))
->     ,("a is similar to b", BinOp (Iden "a") "is similar to" (Iden "b"))
->     ,("a is not similar to b"
->      ,BinOp (Iden "a") "is not similar to" (Iden "b"))
->     ,("a overlaps b", BinOp (Iden "a") "overlaps" (Iden "b"))
->     ,("extract(day from t)", SpecialOp "extract" [Iden "day", Iden "t"])
->     ,("substring(x from 1 for 2)"
->      ,SpecialOp "substring" [Iden "x", NumLit "1", NumLit "2"])
->     ]
-
-> aggregates :: TestItem
-> aggregates = Group "aggregates" $ map (uncurry TestScalarExpr)
->     [("count(*)",App "count" [Star])
->     ,("sum(a order by a)"
->     ,AggregateApp "sum" Nothing [Iden "a"] [(Iden "a", Asc)])
->     ,("sum(all a)"
->     ,AggregateApp "sum" (Just All) [Iden "a"] [])
->     ,("count(distinct a)"
->     ,AggregateApp "count" (Just Distinct) [Iden "a"] [])
->     ]
+> import qualified Test.HUnit as H
 
-> windowFunctions :: TestItem
-> windowFunctions = Group "windowFunctions" $ map (uncurry TestScalarExpr)
->     [("max(a) over ()", WindowApp "max" [Iden "a"] [] [])
->     ,("count(*) over ()", WindowApp "count" [Star] [] [])
->     ,("max(a) over (partition by b)"
->      ,WindowApp "max" [Iden "a"] [Iden "b"] [])
->     ,("max(a) over (partition by b,c)"
->      ,WindowApp "max" [Iden "a"] [Iden "b",Iden "c"] [])
->     ,("sum(a) over (order by b)"
->      ,WindowApp "sum" [Iden "a"] [] [(Iden "b", Asc)])
->     ,("sum(a) over (order by b desc,c)"
->      ,WindowApp "sum" [Iden "a"] [] [(Iden "b", Desc)
->                                     ,(Iden "c", Asc)])
->     ,("sum(a) over (partition by b order by c)"
->      ,WindowApp "sum" [Iden "a"] [Iden "b"] [(Iden "c", Asc)])
->      -- todo: check order by options, add frames
->     ]
+> --import Language.SQL.SimpleSQL.Syntax
+> import Language.SQL.SimpleSQL.Pretty
+> import Language.SQL.SimpleSQL.Parser
 
-> parens :: TestItem
-> parens = Group "parens" $ map (uncurry TestScalarExpr)
->     [("(a)", Parens (Iden "a"))
->     ,("(a + b)", Parens (BinOp (Iden "a") "+" (Iden "b")))
->     ]
+> import Language.SQL.SimpleSQL.TestTypes
 
-> queryExprParserTests :: TestItem
-> queryExprParserTests = Group "queryExprParserTests"
->     [duplicates
->     ,selectLists
->     ,from
->     ,whereClause
->     ,groupByClause
->     ,having
->     ,orderBy
->     ,limit
->     ,combos
->     ,withQueries
->     ,fullQueries
->     ]
+> import Language.SQL.SimpleSQL.FullQueries
+> import Language.SQL.SimpleSQL.GroupBy
+> import Language.SQL.SimpleSQL.Postgres
+> import Language.SQL.SimpleSQL.QueryExprComponents
+> import Language.SQL.SimpleSQL.QueryExprs
+> import Language.SQL.SimpleSQL.TableRefs
+> import Language.SQL.SimpleSQL.ScalarExprs
+> import Language.SQL.SimpleSQL.Tpch
 
 
 
-> duplicates :: TestItem
-> duplicates = Group "duplicates" $ map (uncurry TestQueryExpr)
->     [("select a from t" ,ms All)
->     ,("select all a from t" ,ms All)
->     ,("select distinct a from t", ms Distinct)
->     ]
->  where
->    ms d = makeSelect
->           {qeDuplicates = d
->           ,qeSelectList = [(Nothing,Iden "a")]
->           ,qeFrom = [TRSimple "t"]}
-
-> selectLists :: TestItem
-> selectLists = Group "selectLists" $ map (uncurry TestQueryExpr)
->     [("select 1",
->       makeSelect {qeSelectList = [(Nothing,NumLit "1")]})
->     ,("select a"
->      ,makeSelect {qeSelectList = [(Nothing,Iden "a")]})
->     ,("select a,b"
->      ,makeSelect {qeSelectList = [(Nothing,Iden "a")
->                                  ,(Nothing,Iden "b")]})
->     ,("select 1+2,3+4"
->      ,makeSelect {qeSelectList =
->                      [(Nothing,BinOp (NumLit "1") "+" (NumLit "2"))
->                      ,(Nothing,BinOp (NumLit "3") "+" (NumLit "4"))]})
->     ,("select a as a, /*comment*/ b as b"
->      ,makeSelect {qeSelectList = [(Just "a", Iden "a")
->                                  ,(Just "b", Iden "b")]})
->     ,("select a a, b b"
->      ,makeSelect {qeSelectList = [(Just "a", Iden "a")
->                                  ,(Just "b", Iden "b")]})
->     ]
-
-> from :: TestItem
-> from = Group "from" $ map (uncurry TestQueryExpr)
->     [("select a from t"
->      ,ms [TRSimple "t"])
->     ,("select a from t,u"
->      ,ms [TRSimple "t", TRSimple "u"])
->     ,("select a from t inner join u on expr"
->      ,ms [TRJoin (TRSimple "t") JInner (TRSimple "u")
->                        (Just $ JoinOn $ Iden "expr")])
->     ,("select a from t left join u on expr"
->      ,ms [TRJoin (TRSimple "t") JLeft (TRSimple "u")
->                        (Just $ JoinOn $ Iden "expr")])
->     ,("select a from t right join u on expr"
->      ,ms [TRJoin (TRSimple "t") JRight (TRSimple "u")
->                        (Just $ JoinOn $ Iden "expr")])
->     ,("select a from t full join u on expr"
->      ,ms [TRJoin (TRSimple "t") JFull (TRSimple "u")
->                        (Just $ JoinOn $ Iden "expr")])
->     ,("select a from t cross join u"
->      ,ms [TRJoin (TRSimple "t")
->                        JCross (TRSimple "u") Nothing])
->     ,("select a from t natural inner join u"
->      ,ms [TRJoin (TRSimple "t") JInner (TRSimple "u")
->                        (Just JoinNatural)])
->     ,("select a from t inner join u using(a,b)"
->      ,ms [TRJoin (TRSimple "t") JInner (TRSimple "u")
->                        (Just $ JoinUsing ["a", "b"])])
->     ,("select a from (select a from t)"
->      ,ms [TRQueryExpr $ ms [TRSimple "t"]])
->     ,("select a from t as u"
->      ,ms [TRAlias (TRSimple "t") "u" Nothing])
->     ,("select a from t u"
->      ,ms [TRAlias (TRSimple "t") "u" Nothing])
->     ,("select a from t u(b)"
->      ,ms [TRAlias (TRSimple "t") "u" $ Just ["b"]])
->     ,("select a from (t cross join u) as u"
->      ,ms [TRAlias (TRParens $
->                    TRJoin (TRSimple "t") JCross (TRSimple "u") Nothing)
->                           "u" Nothing])
->      -- todo: not sure if the associativity is correct
->     ,("select a from t cross join u cross join v",
->        ms [TRJoin
->            (TRJoin (TRSimple "t")
->                    JCross (TRSimple "u") Nothing)
->            JCross (TRSimple "v") Nothing])
->     ]
->   where
->     ms f = makeSelect {qeSelectList = [(Nothing,Iden "a")]
->                       ,qeFrom = f}
-
-> whereClause :: TestItem
-> whereClause = Group "whereClause" $ map (uncurry TestQueryExpr)
->     [("select a from t where a = 5"
->      ,makeSelect {qeSelectList = [(Nothing,Iden "a")]
->                  ,qeFrom = [TRSimple "t"]
->                  ,qeWhere = Just $ BinOp (Iden "a") "=" (NumLit "5")})
->     ]
-
-> groupByClause :: TestItem
-> groupByClause = Group "groupByClause" $ map (uncurry TestQueryExpr)
->     [("select a,sum(b) from t group by a"
->      ,makeSelect {qeSelectList = [(Nothing, Iden "a")
->                                  ,(Nothing, App "sum" [Iden "b"])]
->                  ,qeFrom = [TRSimple "t"]
->                  ,qeGroupBy = [Iden "a"]
->                  })
->     ,("select a,b,sum(c) from t group by a,b"
->      ,makeSelect {qeSelectList = [(Nothing, Iden "a")
->                                  ,(Nothing, Iden "b")
->                                  ,(Nothing, App "sum" [Iden "c"])]
->                  ,qeFrom = [TRSimple "t"]
->                  ,qeGroupBy = [Iden "a",Iden "b"]
->                  })
->     ]
-
-> having :: TestItem
-> having = Group "having" $ map (uncurry TestQueryExpr)
->     [("select a,sum(b) from t group by a having sum(b) > 5"
->      ,makeSelect {qeSelectList = [(Nothing, Iden "a")
->                                  ,(Nothing, App "sum" [Iden "b"])]
->                  ,qeFrom = [TRSimple "t"]
->                  ,qeGroupBy = [Iden "a"]
->                  ,qeHaving = Just $ BinOp (App "sum" [Iden "b"])
->                                           ">" (NumLit "5")
->                  })
->     ]
-
-> orderBy :: TestItem
-> orderBy = Group "orderBy" $ map (uncurry TestQueryExpr)
->     [("select a from t order by a"
->      ,ms [(Iden "a", Asc)])
->     ,("select a from t order by a, b"
->      ,ms [(Iden "a", Asc), (Iden "b", Asc)])
->     ,("select a from t order by a asc"
->      ,ms [(Iden "a", Asc)])
->     ,("select a from t order by a desc, b desc"
->      ,ms [(Iden "a", Desc), (Iden "b", Desc)])
->     ]
->   where
->     ms o = makeSelect {qeSelectList = [(Nothing,Iden "a")]
->                       ,qeFrom = [TRSimple "t"]
->                       ,qeOrderBy = o}
-
-> limit :: TestItem
-> limit = Group "limit" $ map (uncurry TestQueryExpr)
->     [("select a from t limit 10"
->      ,ms (Just $ NumLit "10") Nothing)
->     ,("select a from t limit 10 offset 10"
->      ,ms (Just $ NumLit "10") (Just $ NumLit "10"))
->     ]
->   where
->     ms l o = makeSelect
->              {qeSelectList = [(Nothing,Iden "a")]
->              ,qeFrom = [TRSimple "t"]
->              ,qeLimit = l
->              ,qeOffset = o}
-
-> combos :: TestItem
-> combos = Group "combos" $ map (uncurry TestQueryExpr)
->     [("select a from t union select b from u"
->      ,CombineQueryExpr ms1 Union All Respectively ms2)
->     ,("select a from t intersect select b from u"
->      ,CombineQueryExpr ms1 Intersect All Respectively ms2)
->     ,("select a from t except all select b from u"
->      ,CombineQueryExpr ms1 Except All Respectively ms2)
->     ,("select a from t union distinct corresponding \
->       \select b from u"
->      ,CombineQueryExpr ms1 Union Distinct Corresponding ms2)
->     ,("select a from t union select a from t union select a from t"
->      -- is this the correct associativity? 
->      ,CombineQueryExpr ms1 Union All Respectively
->        (CombineQueryExpr ms1 Union All Respectively ms1))
->     ]
->   where
->     ms1 = makeSelect
->           {qeSelectList = [(Nothing,Iden "a")]
->           ,qeFrom = [TRSimple "t"]}
->     ms2 = makeSelect
->           {qeSelectList = [(Nothing,Iden "b")]
->           ,qeFrom = [TRSimple "u"]}
-
-
-> withQueries :: TestItem
-> withQueries = Group "with queries" $ map (uncurry TestQueryExpr)
->     [("with u as (select a from t) select a from u"
->      ,With [("u", ms1)] ms2)
->     ,("with x as (select a from t),\n\
->       \     u as (select a from x)\n\
->       \select a from u"
->      ,With [("x", ms1), ("u",ms3)] ms2)
->     ]
->  where
->    ms c t = makeSelect
->             {qeSelectList = [(Nothing,Iden c)]
->             ,qeFrom = [TRSimple t]}
->    ms1 = ms "a" "t"
->    ms2 = ms "a" "u"
->    ms3 = ms "a" "x"
-
-
-> fullQueries :: TestItem
-> fullQueries = Group "queries" $ map (uncurry TestQueryExpr)
->     [("select count(*) from t"
->      ,makeSelect
->       {qeSelectList = [(Nothing, App "count" [Star])]
->       ,qeFrom = [TRSimple "t"]
->       }
->      )
->     ,("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"
->      ,makeSelect
->       {qeSelectList = [(Nothing, Iden "a")
->                       ,(Just "s"
->                        ,App "sum" [BinOp (Iden "c")
->                                          "+" (Iden "d")])]
->       ,qeFrom = [TRSimple "t", TRSimple "u"]
->       ,qeWhere = Just $ BinOp (Iden "a") ">" (NumLit "5")
->       ,qeGroupBy = [Iden "a"]
->       ,qeHaving = Just $ BinOp (App "count" [NumLit "1"])
->                                ">" (NumLit "5")
->       ,qeOrderBy = [(Iden "s", Asc)]
->       }
->      )
->     ]
-
-> queryExprsParserTests :: TestItem
-> queryExprsParserTests = Group "query exprs" $ map (uncurry TestQueryExprs)
->     [("select 1",[ms])
->     ,("select 1;",[ms])
->     ,("select 1;select 1",[ms,ms])
->     ,("select 1;select 1;",[ms,ms])
->     ,(" select 1;select 1; ",[ms,ms])
->     ]
->   where
->     ms = makeSelect {qeSelectList = [(Nothing,NumLit "1")]}
-
-> tpchTests :: TestItem
-> tpchTests =
->     Group "parse tpch"
->     $ map (ParseQueryExpr . snd) tpchQueries
+Order the tests to start from the simplest first. This is also the
+order on the generated documentation.
 
 > testData :: TestItem
 > testData =
 >     Group "parserTest"
->     [scalarExprParserTests
->     ,queryExprParserTests
->     ,queryExprsParserTests
+>     [scalarExprTests
+>     ,queryExprComponentTests
+>     ,queryExprsTests
+>     ,tableRefTests
+>     ,groupByTests
+>     ,fullQueriesTests
+>     ,postgresTests
 >     ,tpchTests
 >     ]
 
@@ -514,7 +85,8 @@
 >                 let str' = pp got
 >                 let egot' = parser "" Nothing str'
 >                 case egot' of
->                     Left e' -> H.assertFailure $ "pp roundtrip " ++ peFormattedError e'
+>                     Left e' -> H.assertFailure $ "pp roundtrip "
+>                                                  ++ peFormattedError e'
 >                     Right got' -> H.assertEqual "pp roundtrip" expected got'
 
 > toPTest :: (Eq a, Show a) =>
@@ -530,5 +102,6 @@
 >                 let str' = pp got
 >                 let egot' = parser "" Nothing str'
 >                 case egot' of
->                     Left e' -> H.assertFailure $ "pp roundtrip " ++ peFormattedError e'
+>                     Left e' -> H.assertFailure $ "pp roundtrip "
+>                                                  ++ peFormattedError e'
 >                     Right _got' -> return ()
diff --git a/tools/Language/SQL/SimpleSQL/Tpch.lhs b/tools/Language/SQL/SimpleSQL/Tpch.lhs
new file mode 100644
--- /dev/null
+++ b/tools/Language/SQL/SimpleSQL/Tpch.lhs
@@ -0,0 +1,683 @@
+
+Some tests for parsing the tpch queries
+
+The changes made to the official syntax are:
+1. replace the set rowcount with ansi standard fetch first n rows only
+2. replace the create view, query, drop view sequence with a query
+   using a common table expression
+
+> module Language.SQL.SimpleSQL.Tpch (tpchTests,tpchQueries) where
+
+> import Language.SQL.SimpleSQL.TestTypes
+
+> tpchTests :: TestItem
+> tpchTests =
+>     Group "parse tpch"
+>     $ map (ParseQueryExpr . snd) tpchQueries
+
+> tpchQueries :: [(String,String)]
+> tpchQueries =
+>   [("Q1","\n\
+>          \select\n\
+>          \        l_returnflag,\n\
+>          \        l_linestatus,\n\
+>          \        sum(l_quantity) as sum_qty,\n\
+>          \        sum(l_extendedprice) as sum_base_price,\n\
+>          \        sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,\n\
+>          \        sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge,\n\
+>          \        avg(l_quantity) as avg_qty,\n\
+>          \        avg(l_extendedprice) as avg_price,\n\
+>          \        avg(l_discount) as avg_disc,\n\
+>          \        count(*) as count_order\n\
+>          \from\n\
+>          \        lineitem\n\
+>          \where\n\
+>          \        l_shipdate <= date '1998-12-01' - interval '63' day (3)\n\
+>          \group by\n\
+>          \        l_returnflag,\n\
+>          \        l_linestatus\n\
+>          \order by\n\
+>          \        l_returnflag,\n\
+>          \        l_linestatus")
+>   ,("Q2","\n\
+>          \select\n\
+>          \        s_acctbal,\n\
+>          \        s_name,\n\
+>          \        n_name,\n\
+>          \        p_partkey,\n\
+>          \        p_mfgr,\n\
+>          \        s_address,\n\
+>          \        s_phone,\n\
+>          \        s_comment\n\
+>          \from\n\
+>          \        part,\n\
+>          \        supplier,\n\
+>          \        partsupp,\n\
+>          \        nation,\n\
+>          \        region\n\
+>          \where\n\
+>          \        p_partkey = ps_partkey\n\
+>          \        and s_suppkey = ps_suppkey\n\
+>          \        and p_size = 15\n\
+>          \        and p_type like '%BRASS'\n\
+>          \        and s_nationkey = n_nationkey\n\
+>          \        and n_regionkey = r_regionkey\n\
+>          \        and r_name = 'EUROPE'\n\
+>          \        and ps_supplycost = (\n\
+>          \                select\n\
+>          \                        min(ps_supplycost)\n\
+>          \                from\n\
+>          \                        partsupp,\n\
+>          \                        supplier,\n\
+>          \                        nation,\n\
+>          \                        region\n\
+>          \                where\n\
+>          \                        p_partkey = ps_partkey\n\
+>          \                        and s_suppkey = ps_suppkey\n\
+>          \                        and s_nationkey = n_nationkey\n\
+>          \                        and n_regionkey = r_regionkey\n\
+>          \                        and r_name = 'EUROPE'\n\
+>          \        )\n\
+>          \order by\n\
+>          \        s_acctbal desc,\n\
+>          \        n_name,\n\
+>          \        s_name,\n\
+>          \        p_partkey\n\
+>          \fetch first 100 rows only")
+>   ,("Q3","\n\
+>          \ select\n\
+>          \         l_orderkey,\n\
+>          \         sum(l_extendedprice * (1 - l_discount)) as revenue,\n\
+>          \         o_orderdate,\n\
+>          \         o_shippriority\n\
+>          \ from\n\
+>          \         customer,\n\
+>          \         orders,\n\
+>          \         lineitem\n\
+>          \ where\n\
+>          \         c_mktsegment = 'MACHINERY'\n\
+>          \         and c_custkey = o_custkey\n\
+>          \         and l_orderkey = o_orderkey\n\
+>          \         and o_orderdate < date '1995-03-21'\n\
+>          \         and l_shipdate > date '1995-03-21'\n\
+>          \ group by\n\
+>          \         l_orderkey,\n\
+>          \         o_orderdate,\n\
+>          \         o_shippriority\n\
+>          \ order by\n\
+>          \         revenue desc,\n\
+>          \         o_orderdate\n\
+>          \ fetch first 10 rows only")
+>   ,("Q4","\n\
+>          \ select\n\
+>          \         o_orderpriority,\n\
+>          \         count(*) as order_count\n\
+>          \ from\n\
+>          \         orders\n\
+>          \ where\n\
+>          \         o_orderdate >= date '1996-03-01'\n\
+>          \         and o_orderdate < date '1996-03-01' + interval '3' month\n\
+>          \         and exists (\n\
+>          \                 select\n\
+>          \                         *\n\
+>          \                 from\n\
+>          \                         lineitem\n\
+>          \                 where\n\
+>          \                         l_orderkey = o_orderkey\n\
+>          \                         and l_commitdate < l_receiptdate\n\
+>          \         )\n\
+>          \ group by\n\
+>          \         o_orderpriority\n\
+>          \ order by\n\
+>          \         o_orderpriority")
+>   ,("Q5","\n\
+>          \ select\n\
+>          \         n_name,\n\
+>          \         sum(l_extendedprice * (1 - l_discount)) as revenue\n\
+>          \ from\n\
+>          \         customer,\n\
+>          \         orders,\n\
+>          \         lineitem,\n\
+>          \         supplier,\n\
+>          \         nation,\n\
+>          \         region\n\
+>          \ where\n\
+>          \         c_custkey = o_custkey\n\
+>          \         and l_orderkey = o_orderkey\n\
+>          \         and l_suppkey = s_suppkey\n\
+>          \         and c_nationkey = s_nationkey\n\
+>          \         and s_nationkey = n_nationkey\n\
+>          \         and n_regionkey = r_regionkey\n\
+>          \         and r_name = 'EUROPE'\n\
+>          \         and o_orderdate >= date '1997-01-01'\n\
+>          \         and o_orderdate < date '1997-01-01' + interval '1' year\n\
+>          \ group by\n\
+>          \         n_name\n\
+>          \ order by\n\
+>          \         revenue desc")
+>   ,("Q6","\n\
+>          \ select\n\
+>          \         sum(l_extendedprice * l_discount) as revenue\n\
+>          \ from\n\
+>          \         lineitem\n\
+>          \ where\n\
+>          \         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\
+>          \ select\n\
+>          \         supp_nation,\n\
+>          \         cust_nation,\n\
+>          \         l_year,\n\
+>          \         sum(volume) as revenue\n\
+>          \ from\n\
+>          \         (\n\
+>          \                 select\n\
+>          \                         n1.n_name as supp_nation,\n\
+>          \                         n2.n_name as cust_nation,\n\
+>          \                         extract(year from l_shipdate) as l_year,\n\
+>          \                         l_extendedprice * (1 - l_discount) as volume\n\
+>          \                 from\n\
+>          \                         supplier,\n\
+>          \                         lineitem,\n\
+>          \                         orders,\n\
+>          \                         customer,\n\
+>          \                         nation n1,\n\
+>          \                         nation n2\n\
+>          \                 where\n\
+>          \                         s_suppkey = l_suppkey\n\
+>          \                         and o_orderkey = l_orderkey\n\
+>          \                         and c_custkey = o_custkey\n\
+>          \                         and s_nationkey = n1.n_nationkey\n\
+>          \                         and c_nationkey = n2.n_nationkey\n\
+>          \                         and (\n\
+>          \                                 (n1.n_name = 'PERU' and n2.n_name = 'IRAQ')\n\
+>          \                                 or (n1.n_name = 'IRAQ' and n2.n_name = 'PERU')\n\
+>          \                         )\n\
+>          \                         and l_shipdate between date '1995-01-01' and date '1996-12-31'\n\
+>          \         ) as shipping\n\
+>          \ group by\n\
+>          \         supp_nation,\n\
+>          \         cust_nation,\n\
+>          \         l_year\n\
+>          \ order by\n\
+>          \         supp_nation,\n\
+>          \         cust_nation,\n\
+>          \         l_year")
+>   ,("Q8","\n\
+>          \ select\n\
+>          \         o_year,\n\
+>          \         sum(case\n\
+>          \                 when nation = 'IRAQ' then volume\n\
+>          \                 else 0\n\
+>          \         end) / sum(volume) as mkt_share\n\
+>          \ from\n\
+>          \         (\n\
+>          \                 select\n\
+>          \                         extract(year from o_orderdate) as o_year,\n\
+>          \                         l_extendedprice * (1 - l_discount) as volume,\n\
+>          \                         n2.n_name as nation\n\
+>          \                 from\n\
+>          \                         part,\n\
+>          \                         supplier,\n\
+>          \                         lineitem,\n\
+>          \                         orders,\n\
+>          \                         customer,\n\
+>          \                         nation n1,\n\
+>          \                         nation n2,\n\
+>          \                         region\n\
+>          \                 where\n\
+>          \                         p_partkey = l_partkey\n\
+>          \                         and s_suppkey = l_suppkey\n\
+>          \                         and l_orderkey = o_orderkey\n\
+>          \                         and o_custkey = c_custkey\n\
+>          \                         and c_nationkey = n1.n_nationkey\n\
+>          \                         and n1.n_regionkey = r_regionkey\n\
+>          \                         and r_name = 'MIDDLE EAST'\n\
+>          \                         and s_nationkey = n2.n_nationkey\n\
+>          \                         and o_orderdate between date '1995-01-01' and date '1996-12-31'\n\
+>          \                         and p_type = 'STANDARD ANODIZED BRASS'\n\
+>          \         ) as all_nations\n\
+>          \ group by\n\
+>          \         o_year\n\
+>          \ order by\n\
+>          \         o_year")
+>    ,("Q9","\n\
+>          \ select\n\
+>          \         nation,\n\
+>          \         o_year,\n\
+>          \         sum(amount) as sum_profit\n\
+>          \ from\n\
+>          \         (\n\
+>          \                 select\n\
+>          \                         n_name as nation,\n\
+>          \                         extract(year from o_orderdate) as o_year,\n\
+>          \                         l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity as amount\n\
+>          \                 from\n\
+>          \                         part,\n\
+>          \                         supplier,\n\
+>          \                         lineitem,\n\
+>          \                         partsupp,\n\
+>          \                         orders,\n\
+>          \                         nation\n\
+>          \                 where\n\
+>          \                         s_suppkey = l_suppkey\n\
+>          \                         and ps_suppkey = l_suppkey\n\
+>          \                         and ps_partkey = l_partkey\n\
+>          \                         and p_partkey = l_partkey\n\
+>          \                         and o_orderkey = l_orderkey\n\
+>          \                         and s_nationkey = n_nationkey\n\
+>          \                         and p_name like '%antique%'\n\
+>          \         ) as profit\n\
+>          \ group by\n\
+>          \         nation,\n\
+>          \         o_year\n\
+>          \ order by\n\
+>          \         nation,\n\
+>          \         o_year desc")
+>    ,("Q10","\n\
+>          \ select\n\
+>          \         c_custkey,\n\
+>          \         c_name,\n\
+>          \         sum(l_extendedprice * (1 - l_discount)) as revenue,\n\
+>          \         c_acctbal,\n\
+>          \         n_name,\n\
+>          \         c_address,\n\
+>          \         c_phone,\n\
+>          \         c_comment\n\
+>          \ from\n\
+>          \         customer,\n\
+>          \         orders,\n\
+>          \         lineitem,\n\
+>          \         nation\n\
+>          \ where\n\
+>          \         c_custkey = o_custkey\n\
+>          \         and l_orderkey = o_orderkey\n\
+>          \         and o_orderdate >= date '1993-12-01'\n\
+>          \         and o_orderdate < date '1993-12-01' + interval '3' month\n\
+>          \         and l_returnflag = 'R'\n\
+>          \         and c_nationkey = n_nationkey\n\
+>          \ group by\n\
+>          \         c_custkey,\n\
+>          \         c_name,\n\
+>          \         c_acctbal,\n\
+>          \         c_phone,\n\
+>          \         n_name,\n\
+>          \         c_address,\n\
+>          \         c_comment\n\
+>          \ order by\n\
+>          \         revenue desc\n\
+>          \ fetch first 20 rows only")
+>    ,("Q11","\n\
+>          \ select\n\
+>          \         ps_partkey,\n\
+>          \         sum(ps_supplycost * ps_availqty) as value\n\
+>          \ from\n\
+>          \         partsupp,\n\
+>          \         supplier,\n\
+>          \         nation\n\
+>          \ where\n\
+>          \         ps_suppkey = s_suppkey\n\
+>          \         and s_nationkey = n_nationkey\n\
+>          \         and n_name = 'CHINA'\n\
+>          \ group by\n\
+>          \         ps_partkey having\n\
+>          \                 sum(ps_supplycost * ps_availqty) > (\n\
+>          \                         select\n\
+>          \                                 sum(ps_supplycost * ps_availqty) * 0.0001000000\n\
+>          \                         from\n\
+>          \                                 partsupp,\n\
+>          \                                 supplier,\n\
+>          \                                 nation\n\
+>          \                         where\n\
+>          \                                 ps_suppkey = s_suppkey\n\
+>          \                                 and s_nationkey = n_nationkey\n\
+>          \                                 and n_name = 'CHINA'\n\
+>          \                 )\n\
+>          \ order by\n\
+>          \         value desc")
+>    ,("Q12","\n\
+>          \ select\n\
+>          \         l_shipmode,\n\
+>          \         sum(case\n\
+>          \                 when o_orderpriority = '1-URGENT'\n\
+>          \                         or o_orderpriority = '2-HIGH'\n\
+>          \                         then 1\n\
+>          \                 else 0\n\
+>          \         end) as high_line_count,\n\
+>          \         sum(case\n\
+>          \                 when o_orderpriority <> '1-URGENT'\n\
+>          \                         and o_orderpriority <> '2-HIGH'\n\
+>          \                         then 1\n\
+>          \                 else 0\n\
+>          \         end) as low_line_count\n\
+>          \ from\n\
+>          \         orders,\n\
+>          \         lineitem\n\
+>          \ where\n\
+>          \         o_orderkey = l_orderkey\n\
+>          \         and l_shipmode in ('AIR', 'RAIL')\n\
+>          \         and l_commitdate < l_receiptdate\n\
+>          \         and l_shipdate < l_commitdate\n\
+>          \         and l_receiptdate >= date '1994-01-01'\n\
+>          \         and l_receiptdate < date '1994-01-01' + interval '1' year\n\
+>          \ group by\n\
+>          \         l_shipmode\n\
+>          \ order by\n\
+>          \         l_shipmode")
+>    ,("Q13","\n\
+>          \ select\n\
+>          \         c_count,\n\
+>          \         count(*) as custdist\n\
+>          \ from\n\
+>          \         (\n\
+>          \                 select\n\
+>          \                         c_custkey,\n\
+>          \                         count(o_orderkey)\n\
+>          \                 from\n\
+>          \                         customer left outer join orders on\n\
+>          \                                 c_custkey = o_custkey\n\
+>          \                                 and o_comment not like '%pending%requests%'\n\
+>          \                 group by\n\
+>          \                         c_custkey\n\
+>          \         ) as c_orders (c_custkey, c_count)\n\
+>          \ group by\n\
+>          \         c_count\n\
+>          \ order by\n\
+>          \         custdist desc,\n\
+>          \         c_count desc")
+>    ,("Q14","\n\
+>          \ select\n\
+>          \         100.00 * sum(case\n\
+>          \                 when p_type like 'PROMO%'\n\
+>          \                         then l_extendedprice * (1 - l_discount)\n\
+>          \                 else 0\n\
+>          \         end) / sum(l_extendedprice * (1 - l_discount)) as promo_revenue\n\
+>          \ from\n\
+>          \         lineitem,\n\
+>          \         part\n\
+>          \ 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\
+>          \ /*create view revenue0 (supplier_no, total_revenue) as\n\
+>          \         select\n\
+>          \                 l_suppkey,\n\
+>          \                 sum(l_extendedprice * (1 - l_discount))\n\
+>          \         from\n\
+>          \                 lineitem\n\
+>          \         where\n\
+>          \                 l_shipdate >= date '1995-06-01'\n\
+>          \                 and l_shipdate < date '1995-06-01' + interval '3' month\n\
+>          \         group by\n\
+>          \                 l_suppkey;*/\n\
+>          \ with\n\
+>          \ revenue0 as\n\
+>          \         (select\n\
+>          \                 l_suppkey as supplier_no,\n\
+>          \                 sum(l_extendedprice * (1 - l_discount)) as total_revenue\n\
+>          \         from\n\
+>          \                 lineitem\n\
+>          \         where\n\
+>          \                 l_shipdate >= date '1995-06-01'\n\
+>          \                 and l_shipdate < date '1995-06-01' + interval '3' month\n\
+>          \         group by\n\
+>          \                 l_suppkey)\n\
+>          \ select\n\
+>          \         s_suppkey,\n\
+>          \         s_name,\n\
+>          \         s_address,\n\
+>          \         s_phone,\n\
+>          \         total_revenue\n\
+>          \ from\n\
+>          \         supplier,\n\
+>          \         revenue0\n\
+>          \ where\n\
+>          \         s_suppkey = supplier_no\n\
+>          \         and total_revenue = (\n\
+>          \                 select\n\
+>          \                         max(total_revenue)\n\
+>          \                 from\n\
+>          \                         revenue0\n\
+>          \         )\n\
+>          \ order by\n\
+>          \         s_suppkey")
+>    ,("Q16","\n\
+>          \ select\n\
+>          \         p_brand,\n\
+>          \         p_type,\n\
+>          \         p_size,\n\
+>          \         count(distinct ps_suppkey) as supplier_cnt\n\
+>          \ from\n\
+>          \         partsupp,\n\
+>          \         part\n\
+>          \ where\n\
+>          \         p_partkey = ps_partkey\n\
+>          \         and p_brand <> 'Brand#15'\n\
+>          \         and p_type not like 'MEDIUM BURNISHED%'\n\
+>          \         and p_size in (39, 26, 18, 45, 19, 1, 3, 9)\n\
+>          \         and ps_suppkey not in (\n\
+>          \                 select\n\
+>          \                         s_suppkey\n\
+>          \                 from\n\
+>          \                         supplier\n\
+>          \                 where\n\
+>          \                         s_comment like '%Customer%Complaints%'\n\
+>          \         )\n\
+>          \ group by\n\
+>          \         p_brand,\n\
+>          \         p_type,\n\
+>          \         p_size\n\
+>          \ order by\n\
+>          \         supplier_cnt desc,\n\
+>          \         p_brand,\n\
+>          \         p_type,\n\
+>          \         p_size")
+>    ,("Q17","\n\
+>          \ select\n\
+>          \         sum(l_extendedprice) / 7.0 as avg_yearly\n\
+>          \ from\n\
+>          \         lineitem,\n\
+>          \         part\n\
+>          \ where\n\
+>          \         p_partkey = l_partkey\n\
+>          \         and p_brand = 'Brand#52'\n\
+>          \         and p_container = 'JUMBO CAN'\n\
+>          \         and l_quantity < (\n\
+>          \                 select\n\
+>          \                         0.2 * avg(l_quantity)\n\
+>          \                 from\n\
+>          \                         lineitem\n\
+>          \                 where\n\
+>          \                         l_partkey = p_partkey\n\
+>          \         )")
+>    ,("Q18","\n\
+>          \ select\n\
+>          \         c_name,\n\
+>          \         c_custkey,\n\
+>          \         o_orderkey,\n\
+>          \         o_orderdate,\n\
+>          \         o_totalprice,\n\
+>          \         sum(l_quantity)\n\
+>          \ from\n\
+>          \         customer,\n\
+>          \         orders,\n\
+>          \         lineitem\n\
+>          \ where\n\
+>          \         o_orderkey in (\n\
+>          \                 select\n\
+>          \                         l_orderkey\n\
+>          \                 from\n\
+>          \                         lineitem\n\
+>          \                 group by\n\
+>          \                         l_orderkey having\n\
+>          \                                 sum(l_quantity) > 313\n\
+>          \         )\n\
+>          \         and c_custkey = o_custkey\n\
+>          \         and o_orderkey = l_orderkey\n\
+>          \ group by\n\
+>          \         c_name,\n\
+>          \         c_custkey,\n\
+>          \         o_orderkey,\n\
+>          \         o_orderdate,\n\
+>          \         o_totalprice\n\
+>          \ order by\n\
+>          \         o_totalprice desc,\n\
+>          \         o_orderdate\n\
+>          \ fetch first 100 rows only")
+>    ,("Q19","\n\
+>          \ select\n\
+>          \         sum(l_extendedprice* (1 - l_discount)) as revenue\n\
+>          \ from\n\
+>          \         lineitem,\n\
+>          \         part\n\
+>          \ where\n\
+>          \         (\n\
+>          \                 p_partkey = l_partkey\n\
+>          \                 and p_brand = 'Brand#43'\n\
+>          \                 and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG')\n\
+>          \                 and l_quantity >= 3 and l_quantity <= 3 + 10\n\
+>          \                 and p_size between 1 and 5\n\
+>          \                 and l_shipmode in ('AIR', 'AIR REG')\n\
+>          \                 and l_shipinstruct = 'DELIVER IN PERSON'\n\
+>          \         )\n\
+>          \         or\n\
+>          \         (\n\
+>          \                 p_partkey = l_partkey\n\
+>          \                 and p_brand = 'Brand#25'\n\
+>          \                 and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK')\n\
+>          \                 and l_quantity >= 10 and l_quantity <= 10 + 10\n\
+>          \                 and p_size between 1 and 10\n\
+>          \                 and l_shipmode in ('AIR', 'AIR REG')\n\
+>          \                 and l_shipinstruct = 'DELIVER IN PERSON'\n\
+>          \         )\n\
+>          \         or\n\
+>          \         (\n\
+>          \                 p_partkey = l_partkey\n\
+>          \                 and p_brand = 'Brand#24'\n\
+>          \                 and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG')\n\
+>          \                 and l_quantity >= 22 and l_quantity <= 22 + 10\n\
+>          \                 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\
+>          \ select\n\
+>          \         s_name,\n\
+>          \         s_address\n\
+>          \ from\n\
+>          \         supplier,\n\
+>          \         nation\n\
+>          \ where\n\
+>          \         s_suppkey in (\n\
+>          \                 select\n\
+>          \                         ps_suppkey\n\
+>          \                 from\n\
+>          \                         partsupp\n\
+>          \                 where\n\
+>          \                         ps_partkey in (\n\
+>          \                                 select\n\
+>          \                                         p_partkey\n\
+>          \                                 from\n\
+>          \                                         part\n\
+>          \                                 where\n\
+>          \                                         p_name like 'lime%'\n\
+>          \                         )\n\
+>          \                         and ps_availqty > (\n\
+>          \                                 select\n\
+>          \                                         0.5 * sum(l_quantity)\n\
+>          \                                 from\n\
+>          \                                         lineitem\n\
+>          \                                 where\n\
+>          \                                         l_partkey = ps_partkey\n\
+>          \                                         and l_suppkey = ps_suppkey\n\
+>          \                                         and l_shipdate >= date '1994-01-01'\n\
+>          \                                         and l_shipdate < date '1994-01-01' + interval '1' year\n\
+>          \                         )\n\
+>          \         )\n\
+>          \         and s_nationkey = n_nationkey\n\
+>          \         and n_name = 'VIETNAM'\n\
+>          \ order by\n\
+>          \         s_name")
+>    ,("Q21","\n\
+>          \ select\n\
+>          \         s_name,\n\
+>          \         count(*) as numwait\n\
+>          \ from\n\
+>          \         supplier,\n\
+>          \         lineitem l1,\n\
+>          \         orders,\n\
+>          \         nation\n\
+>          \ where\n\
+>          \         s_suppkey = l1.l_suppkey\n\
+>          \         and o_orderkey = l1.l_orderkey\n\
+>          \         and o_orderstatus = 'F'\n\
+>          \         and l1.l_receiptdate > l1.l_commitdate\n\
+>          \         and exists (\n\
+>          \                 select\n\
+>          \                         *\n\
+>          \                 from\n\
+>          \                         lineitem l2\n\
+>          \                 where\n\
+>          \                         l2.l_orderkey = l1.l_orderkey\n\
+>          \                         and l2.l_suppkey <> l1.l_suppkey\n\
+>          \         )\n\
+>          \         and not exists (\n\
+>          \                 select\n\
+>          \                         *\n\
+>          \                 from\n\
+>          \                         lineitem l3\n\
+>          \                 where\n\
+>          \                         l3.l_orderkey = l1.l_orderkey\n\
+>          \                         and l3.l_suppkey <> l1.l_suppkey\n\
+>          \                         and l3.l_receiptdate > l3.l_commitdate\n\
+>          \         )\n\
+>          \         and s_nationkey = n_nationkey\n\
+>          \         and n_name = 'INDIA'\n\
+>          \ group by\n\
+>          \         s_name\n\
+>          \ order by\n\
+>          \         numwait desc,\n\
+>          \         s_name\n\
+>          \ fetch first 100 rows only")
+>    ,("Q22","\n\
+>          \ select\n\
+>          \         cntrycode,\n\
+>          \         count(*) as numcust,\n\
+>          \         sum(c_acctbal) as totacctbal\n\
+>          \ from\n\
+>          \         (\n\
+>          \                 select\n\
+>          \                         substring(c_phone from 1 for 2) as cntrycode,\n\
+>          \                         c_acctbal\n\
+>          \                 from\n\
+>          \                         customer\n\
+>          \                 where\n\
+>          \                         substring(c_phone from 1 for 2) in\n\
+>          \                                 ('41', '28', '39', '21', '24', '29', '44')\n\
+>          \                         and c_acctbal > (\n\
+>          \                                 select\n\
+>          \                                         avg(c_acctbal)\n\
+>          \                                 from\n\
+>          \                                         customer\n\
+>          \                                 where\n\
+>          \                                         c_acctbal > 0.00\n\
+>          \                                         and substring(c_phone from 1 for 2) in\n\
+>          \                                                 ('41', '28', '39', '21', '24', '29', '44')\n\
+>          \                         )\n\
+>          \                         and not exists (\n\
+>          \                                 select\n\
+>          \                                         *\n\
+>          \                                 from\n\
+>          \                                         orders\n\
+>          \                                 where\n\
+>          \                                         o_custkey = c_custkey\n\
+>          \                         )\n\
+>          \         ) as custsale\n\
+>          \ group by\n\
+>          \         cntrycode\n\
+>          \ order by\n\
+>          \         cntrycode")
+>   ]
diff --git a/tools/Tpch.lhs b/tools/Tpch.lhs
deleted file mode 100644
--- a/tools/Tpch.lhs
+++ /dev/null
@@ -1,675 +0,0 @@
-
-test data for tpch queries
-
-
-> {-# LANGUAGE OverloadedStrings #-}
-
-> module Tpch (tpchQueries) where
->
-
-> tpchQueries :: [(String,String)]
-> tpchQueries =
->   [("Q1","\n\
->          \select\n\
->          \        l_returnflag,\n\
->          \        l_linestatus,\n\
->          \        sum(l_quantity) as sum_qty,\n\
->          \        sum(l_extendedprice) as sum_base_price,\n\
->          \        sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,\n\
->          \        sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge,\n\
->          \        avg(l_quantity) as avg_qty,\n\
->          \        avg(l_extendedprice) as avg_price,\n\
->          \        avg(l_discount) as avg_disc,\n\
->          \        count(*) as count_order\n\
->          \from\n\
->          \        lineitem\n\
->          \where\n\
->          \        l_shipdate <= date '1998-12-01' - interval '63' day (3)\n\
->          \group by\n\
->          \        l_returnflag,\n\
->          \        l_linestatus\n\
->          \order by\n\
->          \        l_returnflag,\n\
->          \        l_linestatus")
->   ,("Q2","\n\
->          \select\n\
->          \        s_acctbal,\n\
->          \        s_name,\n\
->          \        n_name,\n\
->          \        p_partkey,\n\
->          \        p_mfgr,\n\
->          \        s_address,\n\
->          \        s_phone,\n\
->          \        s_comment\n\
->          \from\n\
->          \        part,\n\
->          \        supplier,\n\
->          \        partsupp,\n\
->          \        nation,\n\
->          \        region\n\
->          \where\n\
->          \        p_partkey = ps_partkey\n\
->          \        and s_suppkey = ps_suppkey\n\
->          \        and p_size = 15\n\
->          \        and p_type like '%BRASS'\n\
->          \        and s_nationkey = n_nationkey\n\
->          \        and n_regionkey = r_regionkey\n\
->          \        and r_name = 'EUROPE'\n\
->          \        and ps_supplycost = (\n\
->          \                select\n\
->          \                        min(ps_supplycost)\n\
->          \                from\n\
->          \                        partsupp,\n\
->          \                        supplier,\n\
->          \                        nation,\n\
->          \                        region\n\
->          \                where\n\
->          \                        p_partkey = ps_partkey\n\
->          \                        and s_suppkey = ps_suppkey\n\
->          \                        and s_nationkey = n_nationkey\n\
->          \                        and n_regionkey = r_regionkey\n\
->          \                        and r_name = 'EUROPE'\n\
->          \        )\n\
->          \order by\n\
->          \        s_acctbal desc,\n\
->          \        n_name,\n\
->          \        s_name,\n\
->          \        p_partkey\n\
->          \limit 100")
->   ,("Q3","\n\
->          \ select\n\
->          \         l_orderkey,\n\
->          \         sum(l_extendedprice * (1 - l_discount)) as revenue,\n\
->          \         o_orderdate,\n\
->          \         o_shippriority\n\
->          \ from\n\
->          \         customer,\n\
->          \         orders,\n\
->          \         lineitem\n\
->          \ where\n\
->          \         c_mktsegment = 'MACHINERY'\n\
->          \         and c_custkey = o_custkey\n\
->          \         and l_orderkey = o_orderkey\n\
->          \         and o_orderdate < date '1995-03-21'\n\
->          \         and l_shipdate > date '1995-03-21'\n\
->          \ group by\n\
->          \         l_orderkey,\n\
->          \         o_orderdate,\n\
->          \         o_shippriority\n\
->          \ order by\n\
->          \         revenue desc,\n\
->          \         o_orderdate\n\
->          \ limit 10")
->   ,("Q4","\n\
->          \ select\n\
->          \         o_orderpriority,\n\
->          \         count(*) as order_count\n\
->          \ from\n\
->          \         orders\n\
->          \ where\n\
->          \         o_orderdate >= date '1996-03-01'\n\
->          \         and o_orderdate < date '1996-03-01' + interval '3' month\n\
->          \         and exists (\n\
->          \                 select\n\
->          \                         *\n\
->          \                 from\n\
->          \                         lineitem\n\
->          \                 where\n\
->          \                         l_orderkey = o_orderkey\n\
->          \                         and l_commitdate < l_receiptdate\n\
->          \         )\n\
->          \ group by\n\
->          \         o_orderpriority\n\
->          \ order by\n\
->          \         o_orderpriority")
->   ,("Q5","\n\
->          \ select\n\
->          \         n_name,\n\
->          \         sum(l_extendedprice * (1 - l_discount)) as revenue\n\
->          \ from\n\
->          \         customer,\n\
->          \         orders,\n\
->          \         lineitem,\n\
->          \         supplier,\n\
->          \         nation,\n\
->          \         region\n\
->          \ where\n\
->          \         c_custkey = o_custkey\n\
->          \         and l_orderkey = o_orderkey\n\
->          \         and l_suppkey = s_suppkey\n\
->          \         and c_nationkey = s_nationkey\n\
->          \         and s_nationkey = n_nationkey\n\
->          \         and n_regionkey = r_regionkey\n\
->          \         and r_name = 'EUROPE'\n\
->          \         and o_orderdate >= date '1997-01-01'\n\
->          \         and o_orderdate < date '1997-01-01' + interval '1' year\n\
->          \ group by\n\
->          \         n_name\n\
->          \ order by\n\
->          \         revenue desc")
->   ,("Q6","\n\
->          \ select\n\
->          \         sum(l_extendedprice * l_discount) as revenue\n\
->          \ from\n\
->          \         lineitem\n\
->          \ where\n\
->          \         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\
->          \ select\n\
->          \         supp_nation,\n\
->          \         cust_nation,\n\
->          \         l_year,\n\
->          \         sum(volume) as revenue\n\
->          \ from\n\
->          \         (\n\
->          \                 select\n\
->          \                         n1.n_name as supp_nation,\n\
->          \                         n2.n_name as cust_nation,\n\
->          \                         extract(year from l_shipdate) as l_year,\n\
->          \                         l_extendedprice * (1 - l_discount) as volume\n\
->          \                 from\n\
->          \                         supplier,\n\
->          \                         lineitem,\n\
->          \                         orders,\n\
->          \                         customer,\n\
->          \                         nation n1,\n\
->          \                         nation n2\n\
->          \                 where\n\
->          \                         s_suppkey = l_suppkey\n\
->          \                         and o_orderkey = l_orderkey\n\
->          \                         and c_custkey = o_custkey\n\
->          \                         and s_nationkey = n1.n_nationkey\n\
->          \                         and c_nationkey = n2.n_nationkey\n\
->          \                         and (\n\
->          \                                 (n1.n_name = 'PERU' and n2.n_name = 'IRAQ')\n\
->          \                                 or (n1.n_name = 'IRAQ' and n2.n_name = 'PERU')\n\
->          \                         )\n\
->          \                         and l_shipdate between date '1995-01-01' and date '1996-12-31'\n\
->          \         ) as shipping\n\
->          \ group by\n\
->          \         supp_nation,\n\
->          \         cust_nation,\n\
->          \         l_year\n\
->          \ order by\n\
->          \         supp_nation,\n\
->          \         cust_nation,\n\
->          \         l_year")
->   ,("Q8","\n\
->          \ select\n\
->          \         o_year,\n\
->          \         sum(case\n\
->          \                 when nation = 'IRAQ' then volume\n\
->          \                 else 0\n\
->          \         end) / sum(volume) as mkt_share\n\
->          \ from\n\
->          \         (\n\
->          \                 select\n\
->          \                         extract(year from o_orderdate) as o_year,\n\
->          \                         l_extendedprice * (1 - l_discount) as volume,\n\
->          \                         n2.n_name as nation\n\
->          \                 from\n\
->          \                         part,\n\
->          \                         supplier,\n\
->          \                         lineitem,\n\
->          \                         orders,\n\
->          \                         customer,\n\
->          \                         nation n1,\n\
->          \                         nation n2,\n\
->          \                         region\n\
->          \                 where\n\
->          \                         p_partkey = l_partkey\n\
->          \                         and s_suppkey = l_suppkey\n\
->          \                         and l_orderkey = o_orderkey\n\
->          \                         and o_custkey = c_custkey\n\
->          \                         and c_nationkey = n1.n_nationkey\n\
->          \                         and n1.n_regionkey = r_regionkey\n\
->          \                         and r_name = 'MIDDLE EAST'\n\
->          \                         and s_nationkey = n2.n_nationkey\n\
->          \                         and o_orderdate between date '1995-01-01' and date '1996-12-31'\n\
->          \                         and p_type = 'STANDARD ANODIZED BRASS'\n\
->          \         ) as all_nations\n\
->          \ group by\n\
->          \         o_year\n\
->          \ order by\n\
->          \         o_year")
->    ,("Q9","\n\
->          \ select\n\
->          \         nation,\n\
->          \         o_year,\n\
->          \         sum(amount) as sum_profit\n\
->          \ from\n\
->          \         (\n\
->          \                 select\n\
->          \                         n_name as nation,\n\
->          \                         extract(year from o_orderdate) as o_year,\n\
->          \                         l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity as amount\n\
->          \                 from\n\
->          \                         part,\n\
->          \                         supplier,\n\
->          \                         lineitem,\n\
->          \                         partsupp,\n\
->          \                         orders,\n\
->          \                         nation\n\
->          \                 where\n\
->          \                         s_suppkey = l_suppkey\n\
->          \                         and ps_suppkey = l_suppkey\n\
->          \                         and ps_partkey = l_partkey\n\
->          \                         and p_partkey = l_partkey\n\
->          \                         and o_orderkey = l_orderkey\n\
->          \                         and s_nationkey = n_nationkey\n\
->          \                         and p_name like '%antique%'\n\
->          \         ) as profit\n\
->          \ group by\n\
->          \         nation,\n\
->          \         o_year\n\
->          \ order by\n\
->          \         nation,\n\
->          \         o_year desc")
->    ,("Q10","\n\
->          \ select\n\
->          \         c_custkey,\n\
->          \         c_name,\n\
->          \         sum(l_extendedprice * (1 - l_discount)) as revenue,\n\
->          \         c_acctbal,\n\
->          \         n_name,\n\
->          \         c_address,\n\
->          \         c_phone,\n\
->          \         c_comment\n\
->          \ from\n\
->          \         customer,\n\
->          \         orders,\n\
->          \         lineitem,\n\
->          \         nation\n\
->          \ where\n\
->          \         c_custkey = o_custkey\n\
->          \         and l_orderkey = o_orderkey\n\
->          \         and o_orderdate >= date '1993-12-01'\n\
->          \         and o_orderdate < date '1993-12-01' + interval '3' month\n\
->          \         and l_returnflag = 'R'\n\
->          \         and c_nationkey = n_nationkey\n\
->          \ group by\n\
->          \         c_custkey,\n\
->          \         c_name,\n\
->          \         c_acctbal,\n\
->          \         c_phone,\n\
->          \         n_name,\n\
->          \         c_address,\n\
->          \         c_comment\n\
->          \ order by\n\
->          \         revenue desc\n\
->          \ limit 20")
->    ,("Q11","\n\
->          \ select\n\
->          \         ps_partkey,\n\
->          \         sum(ps_supplycost * ps_availqty) as value\n\
->          \ from\n\
->          \         partsupp,\n\
->          \         supplier,\n\
->          \         nation\n\
->          \ where\n\
->          \         ps_suppkey = s_suppkey\n\
->          \         and s_nationkey = n_nationkey\n\
->          \         and n_name = 'CHINA'\n\
->          \ group by\n\
->          \         ps_partkey having\n\
->          \                 sum(ps_supplycost * ps_availqty) > (\n\
->          \                         select\n\
->          \                                 sum(ps_supplycost * ps_availqty) * 0.0001000000\n\
->          \                         from\n\
->          \                                 partsupp,\n\
->          \                                 supplier,\n\
->          \                                 nation\n\
->          \                         where\n\
->          \                                 ps_suppkey = s_suppkey\n\
->          \                                 and s_nationkey = n_nationkey\n\
->          \                                 and n_name = 'CHINA'\n\
->          \                 )\n\
->          \ order by\n\
->          \         value desc")
->    ,("Q12","\n\
->          \ select\n\
->          \         l_shipmode,\n\
->          \         sum(case\n\
->          \                 when o_orderpriority = '1-URGENT'\n\
->          \                         or o_orderpriority = '2-HIGH'\n\
->          \                         then 1\n\
->          \                 else 0\n\
->          \         end) as high_line_count,\n\
->          \         sum(case\n\
->          \                 when o_orderpriority <> '1-URGENT'\n\
->          \                         and o_orderpriority <> '2-HIGH'\n\
->          \                         then 1\n\
->          \                 else 0\n\
->          \         end) as low_line_count\n\
->          \ from\n\
->          \         orders,\n\
->          \         lineitem\n\
->          \ where\n\
->          \         o_orderkey = l_orderkey\n\
->          \         and l_shipmode in ('AIR', 'RAIL')\n\
->          \         and l_commitdate < l_receiptdate\n\
->          \         and l_shipdate < l_commitdate\n\
->          \         and l_receiptdate >= date '1994-01-01'\n\
->          \         and l_receiptdate < date '1994-01-01' + interval '1' year\n\
->          \ group by\n\
->          \         l_shipmode\n\
->          \ order by\n\
->          \         l_shipmode")
->    ,("Q13","\n\
->          \ select\n\
->          \         c_count,\n\
->          \         count(*) as custdist\n\
->          \ from\n\
->          \         (\n\
->          \                 select\n\
->          \                         c_custkey,\n\
->          \                         count(o_orderkey)\n\
->          \                 from\n\
->          \                         customer left outer join orders on\n\
->          \                                 c_custkey = o_custkey\n\
->          \                                 and o_comment not like '%pending%requests%'\n\
->          \                 group by\n\
->          \                         c_custkey\n\
->          \         ) as c_orders (c_custkey, c_count)\n\
->          \ group by\n\
->          \         c_count\n\
->          \ order by\n\
->          \         custdist desc,\n\
->          \         c_count desc")
->    ,("Q14","\n\
->          \ select\n\
->          \         100.00 * sum(case\n\
->          \                 when p_type like 'PROMO%'\n\
->          \                         then l_extendedprice * (1 - l_discount)\n\
->          \                 else 0\n\
->          \         end) / sum(l_extendedprice * (1 - l_discount)) as promo_revenue\n\
->          \ from\n\
->          \         lineitem,\n\
->          \         part\n\
->          \ 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\
->          \ /*create view revenue0 (supplier_no, total_revenue) as\n\
->          \         select\n\
->          \                 l_suppkey,\n\
->          \                 sum(l_extendedprice * (1 - l_discount))\n\
->          \         from\n\
->          \                 lineitem\n\
->          \         where\n\
->          \                 l_shipdate >= date '1995-06-01'\n\
->          \                 and l_shipdate < date '1995-06-01' + interval '3' month\n\
->          \         group by\n\
->          \                 l_suppkey;*/\n\
->          \ with\n\
->          \ revenue0 as\n\
->          \         (select\n\
->          \                 l_suppkey as supplier_no,\n\
->          \                 sum(l_extendedprice * (1 - l_discount)) as total_revenue\n\
->          \         from\n\
->          \                 lineitem\n\
->          \         where\n\
->          \                 l_shipdate >= date '1995-06-01'\n\
->          \                 and l_shipdate < date '1995-06-01' + interval '3' month\n\
->          \         group by\n\
->          \                 l_suppkey)\n\
->          \ select\n\
->          \         s_suppkey,\n\
->          \         s_name,\n\
->          \         s_address,\n\
->          \         s_phone,\n\
->          \         total_revenue\n\
->          \ from\n\
->          \         supplier,\n\
->          \         revenue0\n\
->          \ where\n\
->          \         s_suppkey = supplier_no\n\
->          \         and total_revenue = (\n\
->          \                 select\n\
->          \                         max(total_revenue)\n\
->          \                 from\n\
->          \                         revenue0\n\
->          \         )\n\
->          \ order by\n\
->          \         s_suppkey")
->    ,("Q16","\n\
->          \ select\n\
->          \         p_brand,\n\
->          \         p_type,\n\
->          \         p_size,\n\
->          \         count(distinct ps_suppkey) as supplier_cnt\n\
->          \ from\n\
->          \         partsupp,\n\
->          \         part\n\
->          \ where\n\
->          \         p_partkey = ps_partkey\n\
->          \         and p_brand <> 'Brand#15'\n\
->          \         and p_type not like 'MEDIUM BURNISHED%'\n\
->          \         and p_size in (39, 26, 18, 45, 19, 1, 3, 9)\n\
->          \         and ps_suppkey not in (\n\
->          \                 select\n\
->          \                         s_suppkey\n\
->          \                 from\n\
->          \                         supplier\n\
->          \                 where\n\
->          \                         s_comment like '%Customer%Complaints%'\n\
->          \         )\n\
->          \ group by\n\
->          \         p_brand,\n\
->          \         p_type,\n\
->          \         p_size\n\
->          \ order by\n\
->          \         supplier_cnt desc,\n\
->          \         p_brand,\n\
->          \         p_type,\n\
->          \         p_size")
->    ,("Q17","\n\
->          \ select\n\
->          \         sum(l_extendedprice) / 7.0 as avg_yearly\n\
->          \ from\n\
->          \         lineitem,\n\
->          \         part\n\
->          \ where\n\
->          \         p_partkey = l_partkey\n\
->          \         and p_brand = 'Brand#52'\n\
->          \         and p_container = 'JUMBO CAN'\n\
->          \         and l_quantity < (\n\
->          \                 select\n\
->          \                         0.2 * avg(l_quantity)\n\
->          \                 from\n\
->          \                         lineitem\n\
->          \                 where\n\
->          \                         l_partkey = p_partkey\n\
->          \         )")
->    ,("Q18","\n\
->          \ select\n\
->          \         c_name,\n\
->          \         c_custkey,\n\
->          \         o_orderkey,\n\
->          \         o_orderdate,\n\
->          \         o_totalprice,\n\
->          \         sum(l_quantity)\n\
->          \ from\n\
->          \         customer,\n\
->          \         orders,\n\
->          \         lineitem\n\
->          \ where\n\
->          \         o_orderkey in (\n\
->          \                 select\n\
->          \                         l_orderkey\n\
->          \                 from\n\
->          \                         lineitem\n\
->          \                 group by\n\
->          \                         l_orderkey having\n\
->          \                                 sum(l_quantity) > 313\n\
->          \         )\n\
->          \         and c_custkey = o_custkey\n\
->          \         and o_orderkey = l_orderkey\n\
->          \ group by\n\
->          \         c_name,\n\
->          \         c_custkey,\n\
->          \         o_orderkey,\n\
->          \         o_orderdate,\n\
->          \         o_totalprice\n\
->          \ order by\n\
->          \         o_totalprice desc,\n\
->          \         o_orderdate\n\
->          \ limit 100")
->    ,("Q19","\n\
->          \ select\n\
->          \         sum(l_extendedprice* (1 - l_discount)) as revenue\n\
->          \ from\n\
->          \         lineitem,\n\
->          \         part\n\
->          \ where\n\
->          \         (\n\
->          \                 p_partkey = l_partkey\n\
->          \                 and p_brand = 'Brand#43'\n\
->          \                 and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG')\n\
->          \                 and l_quantity >= 3 and l_quantity <= 3 + 10\n\
->          \                 and p_size between 1 and 5\n\
->          \                 and l_shipmode in ('AIR', 'AIR REG')\n\
->          \                 and l_shipinstruct = 'DELIVER IN PERSON'\n\
->          \         )\n\
->          \         or\n\
->          \         (\n\
->          \                 p_partkey = l_partkey\n\
->          \                 and p_brand = 'Brand#25'\n\
->          \                 and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK')\n\
->          \                 and l_quantity >= 10 and l_quantity <= 10 + 10\n\
->          \                 and p_size between 1 and 10\n\
->          \                 and l_shipmode in ('AIR', 'AIR REG')\n\
->          \                 and l_shipinstruct = 'DELIVER IN PERSON'\n\
->          \         )\n\
->          \         or\n\
->          \         (\n\
->          \                 p_partkey = l_partkey\n\
->          \                 and p_brand = 'Brand#24'\n\
->          \                 and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG')\n\
->          \                 and l_quantity >= 22 and l_quantity <= 22 + 10\n\
->          \                 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\
->          \ select\n\
->          \         s_name,\n\
->          \         s_address\n\
->          \ from\n\
->          \         supplier,\n\
->          \         nation\n\
->          \ where\n\
->          \         s_suppkey in (\n\
->          \                 select\n\
->          \                         ps_suppkey\n\
->          \                 from\n\
->          \                         partsupp\n\
->          \                 where\n\
->          \                         ps_partkey in (\n\
->          \                                 select\n\
->          \                                         p_partkey\n\
->          \                                 from\n\
->          \                                         part\n\
->          \                                 where\n\
->          \                                         p_name like 'lime%'\n\
->          \                         )\n\
->          \                         and ps_availqty > (\n\
->          \                                 select\n\
->          \                                         0.5 * sum(l_quantity)\n\
->          \                                 from\n\
->          \                                         lineitem\n\
->          \                                 where\n\
->          \                                         l_partkey = ps_partkey\n\
->          \                                         and l_suppkey = ps_suppkey\n\
->          \                                         and l_shipdate >= date '1994-01-01'\n\
->          \                                         and l_shipdate < date '1994-01-01' + interval '1' year\n\
->          \                         )\n\
->          \         )\n\
->          \         and s_nationkey = n_nationkey\n\
->          \         and n_name = 'VIETNAM'\n\
->          \ order by\n\
->          \         s_name")
->    ,("Q21","\n\
->          \ select\n\
->          \         s_name,\n\
->          \         count(*) as numwait\n\
->          \ from\n\
->          \         supplier,\n\
->          \         lineitem l1,\n\
->          \         orders,\n\
->          \         nation\n\
->          \ where\n\
->          \         s_suppkey = l1.l_suppkey\n\
->          \         and o_orderkey = l1.l_orderkey\n\
->          \         and o_orderstatus = 'F'\n\
->          \         and l1.l_receiptdate > l1.l_commitdate\n\
->          \         and exists (\n\
->          \                 select\n\
->          \                         *\n\
->          \                 from\n\
->          \                         lineitem l2\n\
->          \                 where\n\
->          \                         l2.l_orderkey = l1.l_orderkey\n\
->          \                         and l2.l_suppkey <> l1.l_suppkey\n\
->          \         )\n\
->          \         and not exists (\n\
->          \                 select\n\
->          \                         *\n\
->          \                 from\n\
->          \                         lineitem l3\n\
->          \                 where\n\
->          \                         l3.l_orderkey = l1.l_orderkey\n\
->          \                         and l3.l_suppkey <> l1.l_suppkey\n\
->          \                         and l3.l_receiptdate > l3.l_commitdate\n\
->          \         )\n\
->          \         and s_nationkey = n_nationkey\n\
->          \         and n_name = 'INDIA'\n\
->          \ group by\n\
->          \         s_name\n\
->          \ order by\n\
->          \         numwait desc,\n\
->          \         s_name\n\
->          \ limit 100")
->    ,("Q22","\n\
->          \ select\n\
->          \         cntrycode,\n\
->          \         count(*) as numcust,\n\
->          \         sum(c_acctbal) as totacctbal\n\
->          \ from\n\
->          \         (\n\
->          \                 select\n\
->          \                         substring(c_phone from 1 for 2) as cntrycode,\n\
->          \                         c_acctbal\n\
->          \                 from\n\
->          \                         customer\n\
->          \                 where\n\
->          \                         substring(c_phone from 1 for 2) in\n\
->          \                                 ('41', '28', '39', '21', '24', '29', '44')\n\
->          \                         and c_acctbal > (\n\
->          \                                 select\n\
->          \                                         avg(c_acctbal)\n\
->          \                                 from\n\
->          \                                         customer\n\
->          \                                 where\n\
->          \                                         c_acctbal > 0.00\n\
->          \                                         and substring(c_phone from 1 for 2) in\n\
->          \                                                 ('41', '28', '39', '21', '24', '29', '44')\n\
->          \                         )\n\
->          \                         and not exists (\n\
->          \                                 select\n\
->          \                                         *\n\
->          \                                 from\n\
->          \                                         orders\n\
->          \                                 where\n\
->          \                                         o_custkey = c_custkey\n\
->          \                         )\n\
->          \         ) as custsale\n\
->          \ group by\n\
->          \         cntrycode\n\
->          \ order by\n\
->          \         cntrycode")
->   ]
