diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2013, 2014, 2015, Jake Wheat
+Copyright 2013 - 2024, Jake Wheat and the simple-sql-parser contributors.
 
 All rights reserved.
 
diff --git a/Language/SQL/SimpleSQL/Combinators.lhs b/Language/SQL/SimpleSQL/Combinators.lhs
deleted file mode 100644
--- a/Language/SQL/SimpleSQL/Combinators.lhs
+++ /dev/null
@@ -1,107 +0,0 @@
-
-> -- | This module contains some generic combinators used in the
-> -- parser. None of the parsing which relies on the local lexers is
-> -- in this module. Some of these combinators have been taken from
-> -- other parser combinator libraries other than Parsec.
-
-> module Language.SQL.SimpleSQL.Combinators
->     (optionSuffix
->     ,(<??>)
->     ,(<??.>)
->     ,(<??*>)
->     ,(<$$>)
->     ,(<$$$>)
->     ,(<$$$$>)
->     ,(<$$$$$>)
->     ) where
-
-> import Control.Applicative ((<**>))
-> import Text.Parsec (option,many)
-> import Text.Parsec.String (GenParser)
-
-a possible issue with the option suffix is that it enforces left
-associativity when chaining it recursively. Have to review
-all these uses and figure out if any should be right associative
-instead, and create an alternative suffix parser
-
-This function style is not good, and should be replaced with chain and
-<??> which has a different type
-
-> optionSuffix :: (a -> GenParser t s a) -> a -> GenParser t s a
-> optionSuffix p a = option a (p a)
-
-
-parses an optional postfix element and applies its result to its left
-hand result, taken from uu-parsinglib
-
-TODO: make sure the precedence higher than <|> and lower than the
-other operators so it can be used nicely
-
-> (<??>) :: GenParser t s a -> GenParser t s (a -> a) -> GenParser t s a
-> p <??> q = p <**> option id q
-
-
-Help with left factored parsers. <$$> is like an analogy with <**>:
-
-f <$> a <*> b
-
-is like
-
-a <**> (b <$$> f)
-
-f <$> a <*> b <*> c
-
-is like
-
-a <**> (b <**> (c <$$$> f))
-
-> (<$$>) :: Applicative f =>
->       f b -> (a -> b -> c) -> f (a -> c)
-> (<$$>) pa c = pa <**> pure (flip c)
-
-> (<$$$>) :: Applicative f =>
->           f c -> (a -> b -> c -> t) -> f (b -> a -> t)
-> p <$$$> c = p <**> pure (flip3 c)
-
-> (<$$$$>) :: Applicative f =>
->           f d -> (a -> b -> c -> d -> t) -> f (c -> b -> a -> t)
-> p <$$$$> c = p <**> pure (flip4 c)
-
-> (<$$$$$>) :: Applicative f =>
->           f e -> (a -> b -> c -> d -> e -> t) -> f (d -> c -> b -> a -> t)
-> p <$$$$$> c = p <**> pure (flip5 c)
-
-Surely no-one would write code like this seriously?
-
-
-composing suffix parsers, not sure about the name. This is used to add
-a second or more suffix parser contingent on the first suffix parser
-succeeding.
-
-> (<??.>) :: GenParser t s (a -> a) -> GenParser t s (a -> a) -> GenParser t s (a -> a)
-> (<??.>) pa pb = (.) `c` pa <*> option id pb
->   -- todo: fix this mess
->   where c = (<$>) . flip
-
-
-0 to many repeated applications of suffix parser
-
-> (<??*>) :: GenParser t s a -> GenParser t s (a -> a) -> GenParser t s a
-> p <??*> q = foldr ($) <$> p <*> (reverse <$> many q)
-
-
-These are to help with left factored parsers:
-
-a <**> (b <**> (c <**> pure (flip3 ctor)))
-
-Not sure the names are correct, but they follow a pattern with flip
-a <**> (b <**> pure (flip ctor))
-
-> flip3 :: (a -> b -> c -> t) -> c -> b -> a -> t
-> flip3 f a b c = f c b a
-
-> flip4 :: (a -> b -> c -> d -> t) -> d -> c -> b -> a -> t
-> flip4 f a b c d = f d c b a
-
-> flip5 :: (a -> b -> c -> d -> e -> t) -> e -> d -> c -> b -> a -> t
-> flip5 f a b c d e = f e d c b a
diff --git a/Language/SQL/SimpleSQL/Dialect.hs b/Language/SQL/SimpleSQL/Dialect.hs
new file mode 100644
--- /dev/null
+++ b/Language/SQL/SimpleSQL/Dialect.hs
@@ -0,0 +1,555 @@
+
+
+-- Data types to represent different dialect options
+
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.Dialect
+    (Dialect(..)
+    ,ansi2011
+    ,mysql
+    ,postgres
+    ,oracle
+    ,sqlserver
+    ) where
+
+import Data.Text (Text)
+import Data.Data (Data,Typeable)
+
+-- | Used to set the dialect used for parsing and pretty printing,
+-- very unfinished at the moment.
+--
+-- The keyword handling works as follows:
+--
+-- There is a list of reserved keywords. These will never parse as
+-- anything other than as a keyword, unless they are in one of the
+-- other lists.
+--
+-- There is a list of \'identifier\' keywords. These are reserved
+-- keywords, with an exception that they will parse as an
+-- identifier in a scalar expression. They won't parse as
+-- identifiers in other places, e.g. column names or aliases.
+--
+-- There is a list of \'app\' keywords. These are reserved keywords,
+-- with an exception that they will also parse in an \'app-like\'
+-- construct - a regular function call, or any of the aggregate and
+-- window variations.
+--
+-- There is a list of special type names. This list serves two
+-- purposes - it is a list of the reserved keywords which are also
+-- type names, and it is a list of all the multi word type names.
+--
+-- Every keyword should appear in the keywords lists, and then you can
+-- add them to the other lists if you want exceptions. Most things
+-- that refer to functions, types or variables that are keywords in
+-- the ansi standard, can be removed from the keywords lists
+-- completely with little effect. With most of the actual SQL
+-- keywords, removing them from the keyword list will result in
+-- lots of valid syntax no longer parsing (and probably bad parse
+-- error messages too).
+--
+-- In the code, all special syntax which looks identical to regular
+-- identifiers or function calls (apart from the name), is treated
+-- like a regular identifier or function call.
+--
+-- It's easy to break the parser by removing the wrong words from
+-- the keywords list or adding the wrong words to the other lists.
+
+data Dialect = Dialect
+    { -- | reserved keywords
+     diKeywords :: [Text]
+      -- | keywords with identifier exception
+    ,diIdentifierKeywords :: [Text]
+      -- | keywords with app exception
+    ,diAppKeywords :: [Text]
+     -- | keywords with type exception plus all the type names which
+     -- are multiple words
+    ,diSpecialTypeNames :: [Text]
+     -- | allow ansi fetch first syntax
+    ,diFetchFirst :: Bool
+     -- | allow limit keyword (mysql, postgres,
+     -- ...)
+    ,diLimit :: Bool
+     -- | allow parsing ODBC syntax
+    ,diOdbc :: Bool
+     -- | allow quoting identifiers with \`backquotes\`
+    ,diBackquotedIden :: Bool
+     -- | allow quoting identifiers with [square brackets]
+    ,diSquareBracketQuotedIden :: Bool
+     -- | allow identifiers with a leading at @example
+    ,diAtIdentifier :: Bool
+     -- | allow identifiers with a leading \# \#example
+    ,diHashIdentifier :: Bool
+     -- | allow positional identifiers like this: $1 
+    ,diPositionalArg :: Bool
+     -- | allow postgres style dollar strings
+    ,diDollarString :: Bool
+     -- | allow strings with an e - e"example"
+    ,diEString :: Bool
+     -- | allow postgres style symbols
+    ,diPostgresSymbols :: Bool
+     -- | allow sql server style symbols
+    ,diSqlServerSymbols :: Bool
+     -- | allow sql server style for CONVERT function in format CONVERT(data_type(length), expression, style)
+    ,diConvertFunction :: Bool
+     -- | allow creating autoincrement columns
+    ,diAutoincrement :: Bool
+     -- | allow omitting the comma between constraint clauses
+    ,diNonCommaSeparatedConstraints :: Bool
+    }
+               deriving (Eq,Show,Read,Data,Typeable)
+
+-- | ansi sql 2011 dialect
+ansi2011 :: Dialect
+ansi2011 = Dialect {diKeywords = ansi2011ReservedKeywords
+                   ,diIdentifierKeywords = []
+                   ,diAppKeywords = ["set"]
+                   ,diSpecialTypeNames = ansi2011TypeNames
+                   ,diFetchFirst = True
+                   ,diLimit = False
+                   ,diOdbc = False
+                   ,diBackquotedIden = False
+                   ,diSquareBracketQuotedIden = False
+                   ,diAtIdentifier = False
+                   ,diHashIdentifier = False
+                   ,diPositionalArg = False
+                   ,diDollarString = False
+                   ,diEString = False
+                   ,diPostgresSymbols = False
+                   ,diSqlServerSymbols = False
+                   ,diConvertFunction = False                     
+                   ,diAutoincrement = False
+                   ,diNonCommaSeparatedConstraints = False
+                   }
+
+-- | mysql dialect
+mysql :: Dialect
+mysql = addLimit ansi2011 {diFetchFirst = False
+                          ,diBackquotedIden = True
+                          }
+
+-- | postgresql dialect
+postgres :: Dialect
+postgres = addLimit ansi2011 {diPositionalArg = True
+                             ,diDollarString = True
+                             ,diEString = True
+                             ,diPostgresSymbols = True}
+
+-- | oracle dialect
+oracle :: Dialect
+oracle = ansi2011 -- {}
+
+-- | microsoft sql server dialect
+sqlserver :: Dialect
+sqlserver = ansi2011 {diSquareBracketQuotedIden = True
+                     ,diAtIdentifier = True
+                     ,diHashIdentifier = True
+                     ,diOdbc = True
+                     ,diSqlServerSymbols = True
+                     ,diConvertFunction = True}
+
+addLimit :: Dialect -> Dialect
+addLimit d = d {diKeywords = "limit": diKeywords d
+               ,diLimit = True}
+
+
+{-
+The keyword handling is quite strong - an alternative way to do it
+would be to have as few keywords as possible, and only require them
+to be quoted when this is needed to resolve a parsing ambiguity.
+
+I don't think this is a good idea for genuine keywords (it probably is
+for all the 'fake' keywords in the standard - things which are
+essentially function names, or predefined variable names, or type
+names, eetc.).
+
+1. working out exactly when each keyword would need to be quoted is
+quite error prone, and might change as the parser implementation is
+maintained - which would be terrible for users
+
+2. it's not user friendly for the user to deal with a whole load of
+special cases - either something is a keyword, then you know you must
+always quote it, or it isn't, then you know you never need to quote
+it
+
+3. I think not having exceptions makes for better error messages for
+the user, and a better sql code maintenance experience.
+
+This might not match actual existing SQL products that well, some of
+which I think have idiosyncratic rules about when a keyword must be
+quoted. If you want to match one of these dialects exactly with this
+parser, I think it will be a lot of work.
+-}
+
+ansi2011ReservedKeywords :: [Text]
+ansi2011ReservedKeywords =
+    [--"abs" -- function
+     "all" -- keyword only?
+    ,"allocate" -- keyword
+    ,"alter" -- keyword
+    ,"and" -- keyword
+    --,"any" -- keyword? and function
+    ,"are" -- keyword
+    ,"array" -- keyword, and used in some special places, like array[...], and array(subquery)
+    --,"array_agg" -- function
+    -- ,"array_max_cardinality" -- function
+    ,"as" -- keyword
+    ,"asensitive" -- keyword
+    ,"asymmetric" -- keyword
+    ,"at" -- keyword
+    ,"atomic" -- keyword
+    ,"authorization" -- keyword
+    --,"avg" -- function
+    ,"begin" -- keyword
+    --,"begin_frame" -- identifier
+    --,"begin_partition" -- identifier
+    ,"between" -- keyword
+    ,"bigint" -- type
+    ,"binary" -- type
+    ,"blob" -- type
+    ,"boolean" -- type
+    ,"both" -- keyword
+    ,"by" -- keyword
+    ,"call" -- keyword
+    ,"called" -- keyword
+    -- ,"cardinality" -- function + identifier?
+    ,"cascaded" -- keyword
+    ,"case" -- keyword
+    ,"cast" -- special function
+    -- ,"ceil" -- function
+    -- ,"ceiling" -- function
+    ,"char"  -- type (+ keyword?)
+    --,"char_length" -- function
+    ,"character" -- type
+    --,"character_length" -- function
+    ,"check" -- keyword
+    ,"clob" -- type
+    ,"close" -- keyword
+    -- ,"coalesce" -- function
+    ,"collate" -- keyword
+    --,"collect" -- function
+    ,"column" -- keyword
+    ,"commit" -- keyword
+    ,"condition" -- keyword
+    ,"connect" -- keyword
+    ,"constraint" --keyword
+    --,"contains" -- keyword?
+    --,"convert" -- function?
+    --,"corr" -- function
+    ,"corresponding" --keyword
+    --,"count" --function
+    --,"covar_pop" -- function
+    --,"covar_samp" --function
+    ,"create" -- keyword
+    ,"cross" -- keyword
+    ,"cube" -- keyword
+    --,"cume_dist" -- function
+    ,"current" -- keyword
+    -- ,"current_catalog" --identifier?
+    --,"current_date" -- identifier
+    --,"current_default_transform_group"  -- identifier
+    --,"current_path"  -- identifier
+    --,"current_role"  -- identifier
+    -- ,"current_row"  -- identifier
+    -- ,"current_schema"  -- identifier
+    -- ,"current_time"  -- identifier
+    --,"current_timestamp"  -- identifier
+    --,"current_transform_group_for_type"  -- identifier, or keyword?
+    --,"current_user" -- identifier
+    ,"cursor" -- keyword
+    ,"cycle" --keyword
+    ,"date" -- type
+    --,"day" -- keyword? - the parser needs it to not be a keyword to parse extract at the moment
+    ,"deallocate" -- keyword
+    ,"dec" -- type
+    ,"decimal" -- type
+    ,"declare" -- keyword
+    --,"default" -- identifier + keyword
+    ,"delete" -- keyword
+    --,"dense_rank" -- functino
+    ,"deref" -- keyword
+    ,"describe"  -- keyword
+    ,"deterministic"
+    ,"disconnect"
+    ,"distinct"
+    ,"double"
+    ,"drop"
+    ,"dynamic"
+    ,"each"
+    --,"element"
+    ,"else"
+    ,"end"
+    -- ,"end_frame"  -- identifier
+    -- ,"end_partition"  -- identifier
+    ,"end-exec" -- no idea what this is
+    ,"equals"
+    ,"escape"
+    --,"every"
+    ,"except"
+    ,"exec"
+    ,"execute"
+    ,"exists"
+    ,"exp"
+    ,"external"
+    ,"extract"
+    --,"false"
+    ,"fetch"
+    ,"filter"
+    -- ,"first_value"
+    ,"float"
+    --,"floor"
+    ,"for"
+    ,"foreign"
+    -- ,"frame_row"  -- identifier
+    ,"free"
+    ,"from"
+    ,"full"
+    ,"function"
+    --,"fusion"
+    ,"get"
+    ,"global"
+    ,"grant"
+    ,"group"
+    --,"grouping"
+    ,"groups"
+    ,"having"
+    ,"hold"
+    --,"hour"
+    ,"identity"
+    ,"in"
+    ,"indicator"
+    ,"inner"
+    ,"inout"
+    ,"insensitive"
+    ,"insert"
+    ,"int"
+    ,"integer"
+    ,"intersect"
+    --,"intersection"
+    ,"interval"
+    ,"into"
+    ,"is"
+    ,"join"
+    --,"lag"
+    ,"language"
+    ,"large"
+    --,"last_value"
+    ,"lateral"
+    --,"lead"
+    ,"leading"
+    ,"left"
+    ,"like"
+    ,"like_regex"
+    --,"ln"
+    ,"local"
+    ,"localtime"
+    ,"localtimestamp"
+    --,"lower"
+    ,"match"
+    --,"max"
+    ,"member"
+    ,"merge"
+    ,"method"
+    --,"min"
+    --,"minute"
+    --,"mod"
+    ,"modifies"
+    --,"module"
+    --,"month"
+    ,"multiset"
+    ,"national"
+    ,"natural"
+    ,"nchar"
+    ,"nclob"
+    ,"new"
+    ,"no"
+    ,"none"
+    ,"normalize"
+    ,"not"
+    --,"nth_value"
+    ,"ntile"
+    --,"null"
+    --,"nullif"
+    ,"numeric"
+    ,"octet_length"
+    ,"occurrences_regex"
+    ,"of"
+    ,"offset"
+    ,"old"
+    ,"on"
+    ,"only"
+    ,"open"
+    ,"or"
+    ,"order"
+    ,"out"
+    ,"outer"
+    ,"over"
+    ,"overlaps"
+    ,"overlay"
+    ,"parameter"
+    ,"partition"
+    ,"percent"
+    --,"percent_rank"
+    --,"percentile_cont"
+    --,"percentile_disc"
+    ,"period"
+    ,"portion"
+    ,"position"
+    ,"position_regex"
+    --,"power"
+    ,"precedes"
+    ,"precision"
+    ,"prepare"
+    ,"primary"
+    ,"procedure"
+    ,"range"
+    --,"rank"
+    ,"reads"
+    ,"real"
+    ,"recursive"
+    ,"ref"
+    ,"references"
+    ,"referencing"
+    --,"regr_avgx"
+    --,"regr_avgy"
+    --,"regr_count"
+    --,"regr_intercept"
+    --,"regr_r2"
+    --,"regr_slope"
+    --,"regr_sxx"
+    --,"regr_sxy"
+    --,"regr_syy"
+    ,"release"
+    ,"result"
+    ,"return"
+    ,"returns"
+    ,"revoke"
+    ,"right"
+    ,"rollback"
+    ,"rollup"
+    --,"row"
+    --,"row_number"
+    ,"rows"
+    ,"savepoint"
+    ,"scope"
+    ,"scroll"
+    ,"search"
+    --,"second"
+    ,"select"
+    ,"sensitive"
+    --,"session_user"
+    ,"set"
+    ,"similar"
+    ,"smallint"
+    --,"some"
+    ,"specific"
+    ,"specifictype"
+    ,"sql"
+    ,"sqlexception"
+    ,"sqlstate"
+    ,"sqlwarning"
+    --,"sqrt"
+    --,"start"
+    ,"static"
+    --,"stddev_pop"
+    --,"stddev_samp"
+    ,"submultiset"
+    --,"substring"
+    ,"substring_regex"
+    ,"succeeds"
+    --,"sum"
+    ,"symmetric"
+    ,"system"
+    --,"system_time"
+    --,"system_user"
+    ,"table"
+    ,"tablesample"
+    ,"then"
+    ,"time"
+    ,"timestamp"
+    ,"timezone_hour"
+    ,"timezone_minute"
+    ,"to"
+    ,"trailing"
+    ,"translate"
+    ,"translate_regex"
+    ,"translation"
+    ,"treat"
+    ,"trigger"
+    ,"truncate"
+    --,"trim"
+    --,"trim_array"
+    --,"true"
+    ,"uescape"
+    ,"union"
+    ,"unique"
+    --,"unknown"
+    ,"unnest"
+    ,"update"
+    ,"upper"
+    --,"user"
+    ,"using"
+    --,"value"
+    ,"values"
+    ,"value_of"
+    --,"var_pop"
+    --,"var_samp"
+    ,"varbinary"
+    ,"varchar"
+    ,"varying"
+    ,"versioning"
+    ,"when"
+    ,"whenever"
+    ,"where"
+    --,"width_bucket"
+    ,"window"
+    ,"with"
+    ,"within"
+    ,"without"
+    --,"year"
+    ]
+
+
+ansi2011TypeNames :: [Text]
+ansi2011TypeNames =
+    ["double precision"
+    ,"character varying"
+    ,"char varying"
+    ,"character large object"
+    ,"char large object"
+    ,"national character"
+    ,"national char"
+    ,"national character varying"
+    ,"national char varying"
+    ,"national character large object"
+    ,"nchar large object"
+    ,"nchar varying"
+    ,"bit varying"
+    ,"binary large object"
+    ,"binary varying"
+        -- reserved keyword typenames:
+    ,"array"
+    ,"bigint"
+    ,"binary"
+    ,"blob"
+    ,"boolean"
+    ,"char"
+    ,"character"
+    ,"clob"
+    ,"date"
+    ,"dec"
+    ,"decimal"
+    ,"double"
+    ,"float"
+    ,"int"
+    ,"integer"
+    ,"nchar"
+    ,"nclob"
+    ,"numeric"
+    ,"real"
+    ,"smallint"
+    ,"time"
+    ,"timestamp"
+    ,"varchar"
+    ,"varbinary"
+    ]
diff --git a/Language/SQL/SimpleSQL/Dialect.lhs b/Language/SQL/SimpleSQL/Dialect.lhs
deleted file mode 100644
--- a/Language/SQL/SimpleSQL/Dialect.lhs
+++ /dev/null
@@ -1,540 +0,0 @@
-
-
-Data types to represent different dialect options
-
-> {-# LANGUAGE DeriveDataTypeable #-}
-> module Language.SQL.SimpleSQL.Dialect
->     (Dialect(..)
->     ,ansi2011
->     ,mysql
->     ,postgres
->     ,oracle
->     ,sqlserver
->     ) where
-
-> import Data.Data
-
-> -- | Used to set the dialect used for parsing and pretty printing,
-> -- very unfinished at the moment.
-> --
-> -- The keyword handling works as follows:
-> --
-> -- There is a list of reserved keywords. These will never parse as
-> -- anything other than as a keyword, unless they are in one of the
-> -- other lists.
-> --
-> -- There is a list of \'identifier\' keywords. These are reserved
-> -- keywords, with an exception that they will parse as an
-> -- identifier in a scalar expression. They won't parse as
-> -- identifiers in other places, e.g. column names or aliases.
-> --
-> -- There is a list of \'app\' keywords. These are reserved keywords,
-> -- with an exception that they will also parse in an \'app-like\'
-> -- construct - a regular function call, or any of the aggregate and
-> -- window variations.
-> --
-> -- There is a list of special type names. This list serves two
-> -- purposes - it is a list of the reserved keywords which are also
-> -- type names, and it is a list of all the multi word type names.
-> --
-> -- Every keyword should appear in the keywords lists, and then you can
-> -- add them to the other lists if you want exceptions. Most things
-> -- that refer to functions, types or variables that are keywords in
-> -- the ansi standard, can be removed from the keywords lists
-> -- completely with little effect. With most of the actual SQL
-> -- keywords, removing them from the keyword list will result in
-> -- lots of valid syntax no longer parsing (and probably bad parse
-> -- error messages too).
-> --
-> -- In the code, all special syntax which looks identical to regular
-> -- identifiers or function calls (apart from the name), is treated
-> -- like a regular identifier or function call.
-> --
-> -- It's easy to break the parser by removing the wrong words from
-> -- the keywords list or adding the wrong words to the other lists.
->
-> data Dialect = Dialect
->     { -- | reserved keywords
->      diKeywords :: [String]
->       -- | keywords with identifier exception
->     ,diIdentifierKeywords :: [String]
->       -- | keywords with app exception
->     ,diAppKeywords :: [String]
->      -- | keywords with type exception plus all the type names which
->      -- are multiple words
->     ,diSpecialTypeNames :: [String]
->      -- | allow ansi fetch first syntax
->     ,diFetchFirst :: Bool
->      -- | allow limit keyword (mysql, postgres,
->      -- ...)
->     ,diLimit :: Bool
->      -- | allow parsing ODBC syntax
->     ,diOdbc :: Bool
->      -- | allow quoting identifiers with \`backquotes\`
->     ,diBackquotedIden :: Bool
->      -- | allow quoting identifiers with [square brackets]
->     ,diSquareBracketQuotedIden :: Bool
->      -- | allow identifiers with a leading at @example
->     ,diAtIdentifier :: Bool
->      -- | allow identifiers with a leading \# \#example
->     ,diHashIdentifier :: Bool
->      -- | allow positional identifiers like this: $1 
->     ,diPositionalArg :: Bool
->      -- | allow postgres style dollar strings
->     ,diDollarString :: Bool
->      -- | allow strings with an e - e"example"
->     ,diEString :: Bool
->      -- | allow postgres style symbols
->     ,diPostgresSymbols :: Bool
->      -- | allow sql server style symbols
->     ,diSqlServerSymbols :: Bool
->     }
->                deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | ansi sql 2011 dialect
-> ansi2011 :: Dialect
-> ansi2011 = Dialect {diKeywords = ansi2011ReservedKeywords
->                    ,diIdentifierKeywords = []
->                    ,diAppKeywords = ["set"]
->                    ,diSpecialTypeNames = ansi2011TypeNames
->                    ,diFetchFirst = True
->                    ,diLimit = False
->                    ,diOdbc = False
->                    ,diBackquotedIden = False
->                    ,diSquareBracketQuotedIden = False
->                    ,diAtIdentifier = False
->                    ,diHashIdentifier = False
->                    ,diPositionalArg = False
->                    ,diDollarString = False
->                    ,diEString = False
->                    ,diPostgresSymbols = False
->                    ,diSqlServerSymbols = False
->                    }
-
-> -- | mysql dialect
-> mysql :: Dialect
-> mysql = addLimit ansi2011 {diFetchFirst = False
->                           ,diBackquotedIden = True
->                           }
-
-> -- | postgresql dialect
-> postgres :: Dialect
-> postgres = addLimit ansi2011 {diPositionalArg = True
->                              ,diDollarString = True
->                              ,diEString = True
->                              ,diPostgresSymbols = True}
-
-> -- | oracle dialect
-> oracle :: Dialect
-> oracle = ansi2011 -- {}
-
-> -- | microsoft sql server dialect
-> sqlserver :: Dialect
-> sqlserver = ansi2011 {diSquareBracketQuotedIden = True
->                      ,diAtIdentifier = True
->                      ,diHashIdentifier = True
->                      ,diSqlServerSymbols = True }
-
-> addLimit :: Dialect -> Dialect
-> addLimit d = d {diKeywords = "limit": diKeywords d
->                ,diLimit = True}
-
-
-The keyword handling is quite strong - an alternative way to do it
-would be to have as few keywords as possible, and only require them
-to be quoted when this is needed to resolve a parsing ambiguity.
-
-I don't think this is a good idea for genuine keywords (it probably is
-for all the 'fake' keywords in the standard - things which are
-essentially function names, or predefined variable names, or type
-names, eetc.).
-
-1. working out exactly when each keyword would need to be quoted is
-quite error prone, and might change as the parser implementation is
-maintained - which would be terrible for users
-
-2. it's not user friendly for the user to deal with a whole load of
-special cases - either something is a keyword, then you know you must
-always quote it, or it isn't, then you know you never need to quote
-it
-
-3. I think not having exceptions makes for better error messages for
-the user, and a better sql code maintenance experience.
-
-This might not match actual existing SQL products that well, some of
-which I think have idiosyncratic rules about when a keyword must be
-quoted. If you want to match one of these dialects exactly with this
-parser, I think it will be a lot of work.
-
-> ansi2011ReservedKeywords :: [String]
-> ansi2011ReservedKeywords =
->     [--"abs" -- function
->      "all" -- keyword only?
->     ,"allocate" -- keyword
->     ,"alter" -- keyword
->     ,"and" -- keyword
->     --,"any" -- keyword? and function
->     ,"are" -- keyword
->     ,"array" -- keyword, and used in some special places, like array[...], and array(subquery)
->     --,"array_agg" -- function
->     -- ,"array_max_cardinality" -- function
->     ,"as" -- keyword
->     ,"asensitive" -- keyword
->     ,"asymmetric" -- keyword
->     ,"at" -- keyword
->     ,"atomic" -- keyword
->     ,"authorization" -- keyword
->     --,"avg" -- function
->     ,"begin" -- keyword
->     --,"begin_frame" -- identifier
->     --,"begin_partition" -- identifier
->     ,"between" -- keyword
->     ,"bigint" -- type
->     ,"binary" -- type
->     ,"blob" -- type
->     ,"boolean" -- type
->     ,"both" -- keyword
->     ,"by" -- keyword
->     ,"call" -- keyword
->     ,"called" -- keyword
->     -- ,"cardinality" -- function + identifier?
->     ,"cascaded" -- keyword
->     ,"case" -- keyword
->     ,"cast" -- special function
->     -- ,"ceil" -- function
->     -- ,"ceiling" -- function
->     ,"char"  -- type (+ keyword?)
->     --,"char_length" -- function
->     ,"character" -- type
->     --,"character_length" -- function
->     ,"check" -- keyword
->     ,"clob" -- type
->     ,"close" -- keyword
->     -- ,"coalesce" -- function
->     ,"collate" -- keyword
->     --,"collect" -- function
->     ,"column" -- keyword
->     ,"commit" -- keyword
->     ,"condition" -- keyword
->     ,"connect" -- keyword
->     ,"constraint" --keyword
->     --,"contains" -- keyword?
->     --,"convert" -- function?
->     --,"corr" -- function
->     ,"corresponding" --keyword
->     --,"count" --function
->     --,"covar_pop" -- function
->     --,"covar_samp" --function
->     ,"create" -- keyword
->     ,"cross" -- keyword
->     ,"cube" -- keyword
->     --,"cume_dist" -- function
->     ,"current" -- keyword
->     -- ,"current_catalog" --identifier?
->     --,"current_date" -- identifier
->     --,"current_default_transform_group"  -- identifier
->     --,"current_path"  -- identifier
->     --,"current_role"  -- identifier
->     -- ,"current_row"  -- identifier
->     -- ,"current_schema"  -- identifier
->     -- ,"current_time"  -- identifier
->     --,"current_timestamp"  -- identifier
->     --,"current_transform_group_for_type"  -- identifier, or keyword?
->     --,"current_user" -- identifier
->     ,"cursor" -- keyword
->     ,"cycle" --keyword
->     ,"date" -- type
->     --,"day" -- keyword? - the parser needs it to not be a keyword to parse extract at the moment
->     ,"deallocate" -- keyword
->     ,"dec" -- type
->     ,"decimal" -- type
->     ,"declare" -- keyword
->     --,"default" -- identifier + keyword
->     ,"delete" -- keyword
->     --,"dense_rank" -- functino
->     ,"deref" -- keyword
->     ,"describe"  -- keyword
->     ,"deterministic"
->     ,"disconnect"
->     ,"distinct"
->     ,"double"
->     ,"drop"
->     ,"dynamic"
->     ,"each"
->     --,"element"
->     ,"else"
->     ,"end"
->     -- ,"end_frame"  -- identifier
->     -- ,"end_partition"  -- identifier
->     ,"end-exec" -- no idea what this is
->     ,"equals"
->     ,"escape"
->     --,"every"
->     ,"except"
->     ,"exec"
->     ,"execute"
->     ,"exists"
->     ,"exp"
->     ,"external"
->     ,"extract"
->     --,"false"
->     ,"fetch"
->     ,"filter"
->     -- ,"first_value"
->     ,"float"
->     --,"floor"
->     ,"for"
->     ,"foreign"
->     -- ,"frame_row"  -- identifier
->     ,"free"
->     ,"from"
->     ,"full"
->     ,"function"
->     --,"fusion"
->     ,"get"
->     ,"global"
->     ,"grant"
->     ,"group"
->     --,"grouping"
->     ,"groups"
->     ,"having"
->     ,"hold"
->     --,"hour"
->     ,"identity"
->     ,"in"
->     ,"indicator"
->     ,"inner"
->     ,"inout"
->     ,"insensitive"
->     ,"insert"
->     ,"int"
->     ,"integer"
->     ,"intersect"
->     --,"intersection"
->     ,"interval"
->     ,"into"
->     ,"is"
->     ,"join"
->     --,"lag"
->     ,"language"
->     ,"large"
->     --,"last_value"
->     ,"lateral"
->     --,"lead"
->     ,"leading"
->     ,"left"
->     ,"like"
->     ,"like_regex"
->     --,"ln"
->     ,"local"
->     ,"localtime"
->     ,"localtimestamp"
->     --,"lower"
->     ,"match"
->     --,"max"
->     ,"member"
->     ,"merge"
->     ,"method"
->     --,"min"
->     --,"minute"
->     --,"mod"
->     ,"modifies"
->     --,"module"
->     --,"month"
->     ,"multiset"
->     ,"national"
->     ,"natural"
->     ,"nchar"
->     ,"nclob"
->     ,"new"
->     ,"no"
->     ,"none"
->     ,"normalize"
->     ,"not"
->     --,"nth_value"
->     ,"ntile"
->     --,"null"
->     --,"nullif"
->     ,"numeric"
->     ,"octet_length"
->     ,"occurrences_regex"
->     ,"of"
->     ,"offset"
->     ,"old"
->     ,"on"
->     ,"only"
->     ,"open"
->     ,"or"
->     ,"order"
->     ,"out"
->     ,"outer"
->     ,"over"
->     ,"overlaps"
->     ,"overlay"
->     ,"parameter"
->     ,"partition"
->     ,"percent"
->     --,"percent_rank"
->     --,"percentile_cont"
->     --,"percentile_disc"
->     ,"period"
->     ,"portion"
->     ,"position"
->     ,"position_regex"
->     --,"power"
->     ,"precedes"
->     ,"precision"
->     ,"prepare"
->     ,"primary"
->     ,"procedure"
->     ,"range"
->     --,"rank"
->     ,"reads"
->     ,"real"
->     ,"recursive"
->     ,"ref"
->     ,"references"
->     ,"referencing"
->     --,"regr_avgx"
->     --,"regr_avgy"
->     --,"regr_count"
->     --,"regr_intercept"
->     --,"regr_r2"
->     --,"regr_slope"
->     --,"regr_sxx"
->     --,"regr_sxy"
->     --,"regr_syy"
->     ,"release"
->     ,"result"
->     ,"return"
->     ,"returns"
->     ,"revoke"
->     ,"right"
->     ,"rollback"
->     ,"rollup"
->     --,"row"
->     --,"row_number"
->     ,"rows"
->     ,"savepoint"
->     ,"scope"
->     ,"scroll"
->     ,"search"
->     --,"second"
->     ,"select"
->     ,"sensitive"
->     --,"session_user"
->     ,"set"
->     ,"similar"
->     ,"smallint"
->     --,"some"
->     ,"specific"
->     ,"specifictype"
->     ,"sql"
->     ,"sqlexception"
->     ,"sqlstate"
->     ,"sqlwarning"
->     --,"sqrt"
->     --,"start"
->     ,"static"
->     --,"stddev_pop"
->     --,"stddev_samp"
->     ,"submultiset"
->     --,"substring"
->     ,"substring_regex"
->     ,"succeeds"
->     --,"sum"
->     ,"symmetric"
->     ,"system"
->     --,"system_time"
->     --,"system_user"
->     ,"table"
->     ,"tablesample"
->     ,"then"
->     ,"time"
->     ,"timestamp"
->     ,"timezone_hour"
->     ,"timezone_minute"
->     ,"to"
->     ,"trailing"
->     ,"translate"
->     ,"translate_regex"
->     ,"translation"
->     ,"treat"
->     ,"trigger"
->     ,"truncate"
->     --,"trim"
->     --,"trim_array"
->     --,"true"
->     ,"uescape"
->     ,"union"
->     ,"unique"
->     --,"unknown"
->     ,"unnest"
->     ,"update"
->     ,"upper"
->     --,"user"
->     ,"using"
->     --,"value"
->     ,"values"
->     ,"value_of"
->     --,"var_pop"
->     --,"var_samp"
->     ,"varbinary"
->     ,"varchar"
->     ,"varying"
->     ,"versioning"
->     ,"when"
->     ,"whenever"
->     ,"where"
->     --,"width_bucket"
->     ,"window"
->     ,"with"
->     ,"within"
->     ,"without"
->     --,"year"
->     ]
-
-
-> ansi2011TypeNames :: [String]
-> ansi2011TypeNames =
->     ["double precision"
->     ,"character varying"
->     ,"char varying"
->     ,"character large object"
->     ,"char large object"
->     ,"national character"
->     ,"national char"
->     ,"national character varying"
->     ,"national char varying"
->     ,"national character large object"
->     ,"nchar large object"
->     ,"nchar varying"
->     ,"bit varying"
->     ,"binary large object"
->     ,"binary varying"
->         -- reserved keyword typenames:
->     ,"array"
->     ,"bigint"
->     ,"binary"
->     ,"blob"
->     ,"boolean"
->     ,"char"
->     ,"character"
->     ,"clob"
->     ,"date"
->     ,"dec"
->     ,"decimal"
->     ,"double"
->     ,"float"
->     ,"int"
->     ,"integer"
->     ,"nchar"
->     ,"nclob"
->     ,"numeric"
->     ,"real"
->     ,"smallint"
->     ,"time"
->     ,"timestamp"
->     ,"varchar"
->     ,"varbinary"
->     ]
diff --git a/Language/SQL/SimpleSQL/Errors.lhs b/Language/SQL/SimpleSQL/Errors.lhs
deleted file mode 100644
--- a/Language/SQL/SimpleSQL/Errors.lhs
+++ /dev/null
@@ -1,51 +0,0 @@
-
-> -- | helpers to work with parsec errors more nicely
-> module Language.SQL.SimpleSQL.Errors
->     (ParseError(..)
->     --,formatError
->     ,convParseError
->     ) where
-
-> import Text.Parsec (sourceColumn,sourceLine,sourceName,errorPos)
-> import qualified Text.Parsec as P (ParseError)
-
-> -- | 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
->                   } deriving (Eq,Show)
-
-> convParseError :: String -> P.ParseError -> ParseError
-> convParseError src e =
->     ParseError
->     {peErrorString = show e
->     ,peFilename = sourceName p
->     ,pePosition = (sourceLine p, sourceColumn p)
->     ,peFormattedError = formatError src e}
->   where
->     p = errorPos e
-
-format the error more nicely: emacs format for positioning, plus
-context
-
-> formatError :: String -> P.ParseError -> String
-> formatError src e =
->     sourceName p ++ ":" ++ show (sourceLine p)
->     ++ ":" ++ show (sourceColumn p) ++ ":"
->     ++ context
->     ++ show e
->   where
->     context =
->         let lns = take 1 $ drop (sourceLine p - 1) $ lines src
->         in case lns of
->              [x] -> "\n" ++ x ++ "\n"
->                     ++ replicate (sourceColumn p - 1) ' ' ++ "^\n"
->              _ -> ""
->     p = errorPos e
diff --git a/Language/SQL/SimpleSQL/Lex.hs b/Language/SQL/SimpleSQL/Lex.hs
new file mode 100644
--- /dev/null
+++ b/Language/SQL/SimpleSQL/Lex.hs
@@ -0,0 +1,940 @@
+
+{-
+The parser uses a separate lexer for two reasons:
+
+1. sql syntax is very awkward to parse, the separate lexer makes it
+easier to handle this in most places (in some places it makes it
+harder or impossible, the fix is to switch to something better than
+parsec)
+
+2. using a separate lexer gives a huge speed boost because it reduces
+backtracking. (We could get this by making the parsing code a lot more
+complex also.)
+
+3. we can test the lexer relatively exhaustively, then even when we
+don't do nearly as comprehensive testing on the syntax level, we still
+have a relatively high assurance of the low level of bugs. This is
+much more difficult to get parity with when testing the syntax parser
+directly without the separately testing lexing stage.
+
+TODO:
+
+optimisations:
+
+check for left factor opportunities
+check for places where it parses a few substrings from the source,
+  then puts them back together with a concatenate of some flavour
+  -> this is better if can find a way to parse the entire string
+  from the source and lift it in one go into the lexical token
+before this is done, a smaller optimisation is when any code matches
+  a constant string in the lexer, use that constant string instead
+  of the string from the parser, it might make a small difference in
+  a few places
+maybe every token should carry the exact source as well as any fields
+  it's been broken into - so pretty printing is trivial
+
+
+make the tokenswill print more dialect accurate. Maybe add symbol
+  chars and identifier chars to the dialect definition and use them from
+  here
+
+start adding negative / different parse dialect tests
+
+add token tables and tests for oracle, sql server
+review existing tables
+
+look for refactoring opportunities, especially the token
+generation tables in the tests
+
+do some user documentation on lexing, and lexing/dialects
+
+start thinking about a more separated design for the dialect handling
+
+lexing tests are starting to take a really long time, so split the
+tests so it is much easier to run all the tests except the lexing
+tests which only need to be run when working on the lexer (which
+should be relatively uncommon), or doing a commit or finishing off a
+series of commits,
+
+start writing the error message tests:
+  generate/write a large number of syntax errors
+  create a table with the source and the error message
+  try to compare some different versions of code to compare the
+    quality of the error messages by hand
+
+  get this checked in so improvements and regressions in the error
+    message quality can be tracked a little more easily (although it will
+    still be manual)
+
+try again to add annotation to the ast
+
+-}
+
+-- | Lexer for SQL.
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE TypeFamilies      #-}
+
+module Language.SQL.SimpleSQL.Lex
+    (Token(..)
+    ,WithPos(..)
+    ,lexSQL
+    ,lexSQLWithPositions
+    ,prettyToken
+    ,prettyTokens
+    ,ParseError
+    ,prettyError
+    ,tokenListWillPrintAndLex
+    ,ansi2011
+    ,SQLStream(..)
+    ) where
+
+import Language.SQL.SimpleSQL.Dialect
+    (Dialect(..)
+    ,ansi2011
+    )
+
+import Text.Megaparsec
+    (Parsec
+    ,runParser'
+
+    ,PosState(..)
+    ,TraversableStream(..)
+    ,VisualStream(..)
+    
+    ,ParseErrorBundle(..)
+    ,errorBundlePretty
+
+    ,SourcePos(..)
+    ,getSourcePos
+    ,getOffset
+    ,pstateSourcePos
+    ,statePosState
+    ,mkPos
+
+    ,choice
+    ,satisfy
+    ,takeWhileP
+    ,takeWhile1P
+    ,(<?>)
+    ,eof
+    ,many
+    ,try
+    ,option
+    ,(<|>)
+    ,notFollowedBy
+    ,manyTill
+    ,anySingle
+    ,lookAhead
+    )
+import qualified Text.Megaparsec as M
+import Text.Megaparsec.Char
+    (string
+    ,char
+    )
+import Text.Megaparsec.State (initialState)
+
+import qualified Data.List          as DL
+import qualified Data.List.NonEmpty as NE
+import Data.Proxy (Proxy(..))
+import Data.Void (Void)
+
+import Control.Applicative ((<**>))
+import Data.Char
+    (isAlphaNum
+    ,isAlpha
+    ,isSpace
+    ,isDigit
+    )
+import Control.Monad (void, guard)
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Maybe (fromMaybe)
+
+------------------------------------------------------------------------------
+
+-- syntax
+
+-- | Represents a lexed token
+data Token
+    -- | A symbol (in ansi dialect) is one of the following
+    --
+    -- * multi char symbols <> \<= \>= != ||
+    -- * single char symbols: * + -  < >  ^ / %  ~ & | ? ( ) [ ] , ; ( )
+    --
+    = Symbol Text
+    -- | This is an identifier or keyword. The first field is
+    -- the quotes used, or nothing if no quotes were used. The quotes
+    -- can be " or u& or something dialect specific like []
+    | Identifier (Maybe (Text,Text)) Text
+    -- | This is a prefixed variable symbol, such as :var, @var or #var
+    -- (only :var is used in ansi dialect)
+    | PrefixedVariable Char Text
+    -- | This is a positional arg identifier e.g. $1
+    | PositionalArg Int
+    -- | This is a string literal. The first two fields are the --
+    -- start and end quotes, which are usually both ', but can be
+    -- the character set (one of nNbBxX, or u&, U&), or a dialect
+    -- specific string quoting (such as $$ in postgres)
+    | SqlString Text Text Text
+    -- | A number literal (integral or otherwise), stored in original format
+    -- unchanged
+    | SqlNumber Text
+    -- | Whitespace, one or more of space, tab or newline.
+    | Whitespace Text
+    -- | A commented line using --, contains every character starting with the
+    -- \'--\' and including the terminating newline character if there is one
+    -- - this will be missing if the last line in the source is a line comment
+    -- with no trailing newline
+    | LineComment Text
+    -- | A block comment, \/* stuff *\/, includes the comment delimiters
+    | BlockComment Text
+      deriving (Eq,Show,Ord)
+
+------------------------------------------------------------------------------
+
+-- main api functions
+
+-- | Lex some SQL to a list of tokens.
+lexSQLWithPositions
+    :: Dialect
+    -- ^ dialect of SQL to use
+    -> Text
+    -- ^ filename to use in error messages
+    -> Maybe (Int,Int)
+    -- ^ line number and column number of the first character
+    -- in the source to use in error messages
+    -> Text
+    -- ^ the SQL source to lex
+    -> Either ParseError [WithPos Token]
+lexSQLWithPositions dialect fn p src = myParse fn p (many (sqlToken dialect) <* (eof <?> "")) src
+
+
+-- | Lex some SQL to a list of tokens.
+lexSQL
+    :: Dialect
+    -- ^ dialect of SQL to use
+    -> Text
+    -- ^ filename to use in error messages
+    -> Maybe (Int,Int)
+    -- ^ line number and column number of the first character
+    -- in the source to use in error messages
+    -> Text
+    -- ^ the SQL source to lex
+    -> Either ParseError [Token]
+lexSQL dialect fn p src =
+    map tokenVal <$> lexSQLWithPositions dialect fn p src
+
+myParse :: Text -> Maybe (Int,Int) -> Parser a -> Text -> Either ParseError a
+myParse name sp' p s =
+        let sp = fromMaybe (1,1) sp'
+            ps = SourcePos (T.unpack name) (mkPos $ fst sp) (mkPos $ snd sp)
+            is = initialState (T.unpack name) s
+            sps = (statePosState is) {pstateSourcePos = ps}
+            is' = is {statePosState = sps}
+        in snd $ runParser' p is'
+
+prettyError :: ParseError -> Text
+prettyError = T.pack . errorBundlePretty
+
+------------------------------------------------------------------------------
+
+-- parsing boilerplate
+
+type ParseError = ParseErrorBundle Text Void
+
+type Parser = Parsec Void Text
+
+-- | Positional information added to tokens to preserve source positions
+-- for the parser
+data WithPos a = WithPos
+  { startPos :: SourcePos
+  , endPos :: SourcePos
+  , tokenLength :: Int
+  , tokenVal :: a
+  } deriving (Eq, Ord, Show)
+
+------------------------------------------------------------------------------
+
+-- pretty print
+
+-- | Pretty printing, if you lex a bunch of tokens, then pretty
+-- print them, should should get back exactly the same string
+prettyToken :: Dialect -> Token -> Text
+prettyToken _ (Symbol s) = s
+prettyToken _ (Identifier Nothing t) = t
+prettyToken _ (Identifier (Just (q1,q2)) t) = q1 <> t <> q2
+prettyToken _ (PrefixedVariable c p) = T.cons c p
+prettyToken _ (PositionalArg p) = T.cons '$' $ T.pack $ show p
+prettyToken _ (SqlString s e t) = s <> t <> e
+prettyToken _ (SqlNumber r) = r
+prettyToken _ (Whitespace t) = t
+prettyToken _ (LineComment l) = l
+prettyToken _ (BlockComment c) = c
+
+prettyTokens :: Dialect -> [Token] -> Text
+prettyTokens d ts = T.concat $ map (prettyToken d) ts
+
+------------------------------------------------------------------------------
+
+-- token parsers
+
+-- | parser for a sql token
+sqlToken :: Dialect -> Parser (WithPos Token)
+sqlToken d = (do
+    -- possibly there's a more efficient way of doing the source positions?
+    sp <- getSourcePos
+    off <- getOffset
+    t <- choice
+         [sqlString d
+         ,identifier d
+         ,lineComment d
+         ,blockComment d
+         ,sqlNumber d
+         ,positionalArg d
+         ,dontParseEndBlockComment d
+         ,prefixedVariable d
+         ,symbol d
+         ,sqlWhitespace d]
+    off1 <- getOffset
+    ep <- getSourcePos
+    pure $ WithPos sp ep (off1 - off) t) <?> "valid lexical token"
+
+--------------------------------------
+
+{-
+Parse a SQL string. Examples:
+
+'basic string'
+'string with '' a quote'
+n'international text'
+b'binary string'
+x'hexidecimal string'
+-}
+
+sqlString :: Dialect -> Parser Token
+sqlString d = dollarString <|> csString <|> normalString
+  where
+    dollarString = do
+        guard $ diDollarString d
+        -- use try because of ambiguity with symbols and with
+        -- positional arg
+        delim <- (\x -> T.concat ["$",x,"$"])
+                 <$> try (char '$' *> option "" identifierString <* char '$')
+        SqlString delim delim . T.pack <$> manyTill anySingle (try $ string delim)
+    normalString = SqlString "'" "'" <$> (char '\'' *> normalStringSuffix False "")
+    normalStringSuffix allowBackslash t = do
+        s <- takeWhileP Nothing $ if allowBackslash
+                                  then (`notElemChar` "'\\")
+                                  else (/= '\'')
+        -- deal with '' or \' as literal quote character
+        choice [do
+                ctu <- choice ["''" <$ try (string "''")
+                              ,"\\'" <$ string "\\'"
+                              ,"\\" <$ char '\\']
+                normalStringSuffix allowBackslash $ T.concat [t,s,ctu]
+               ,T.concat [t,s] <$ char '\'']
+    -- try is used to to avoid conflicts with
+    -- identifiers which can start with n,b,x,u
+    -- once we read the quote type and the starting '
+    -- then we commit to a string
+    -- it's possible that this will reject some valid syntax
+    -- but only pathalogical stuff, and I think the improved
+    -- error messages and user predictability make it a good
+    -- pragmatic choice
+    csString
+      | diEString d =
+        choice [SqlString <$> try (string "e'" <|> string "E'")
+                          <*> pure "'" <*> normalStringSuffix True ""
+               ,csString']
+      | otherwise = csString'
+    csString' = SqlString
+                <$> try cs
+                <*> pure "'"
+                <*> normalStringSuffix False ""
+    csPrefixes = map (`T.cons` "'") "nNbBxX" ++ ["u&'", "U&'"]
+    cs :: Parser Text
+    cs = choice $ map string csPrefixes
+
+--------------------------------------
+
+{-
+Parses identifiers:
+
+simple_identifier_23
+u&"unicode quoted identifier"
+"quoted identifier"
+"quoted identifier "" with double quote char"
+`mysql quoted identifier`
+-}
+
+identifier :: Dialect -> Parser Token
+identifier d =
+    choice
+    [quotedIden
+    ,unicodeQuotedIden
+    ,regularIden
+    ,guard (diBackquotedIden d) >> mySqlQuotedIden
+    ,guard (diSquareBracketQuotedIden d) >> sqlServerQuotedIden
+    ]
+  where
+    regularIden = Identifier Nothing <$> identifierString
+    quotedIden = Identifier (Just ("\"","\"")) <$> qidenPart
+    mySqlQuotedIden = Identifier (Just ("`","`"))
+                      <$> (char '`' *> takeWhile1P Nothing (/='`') <* char '`')
+    sqlServerQuotedIden = Identifier (Just ("[","]"))
+                          <$> (char '[' *> takeWhile1P Nothing (`notElemChar` "[]") <* char ']')
+    -- try is used here to avoid a conflict with identifiers
+    -- and quoted strings which also start with a 'u'
+    unicodeQuotedIden = Identifier
+                        <$> (f <$> try (oneOf "uU" <* string "&"))
+                        <*> qidenPart
+      where f x = Just (T.cons x "&\"", "\"")
+    qidenPart = char '"' *> qidenSuffix ""
+    qidenSuffix t = do
+        s <- takeWhileP Nothing (/='"')
+        void $ char '"'
+        -- deal with "" as literal double quote character
+        choice [do
+                void $ char '"'
+                qidenSuffix $ T.concat [t,s,"\"\""]
+               ,pure $ T.concat [t,s]]
+
+identifierString :: Parser Text
+identifierString = (do
+    c <- satisfy isFirstLetter
+    choice
+        [T.cons c <$> takeWhileP (Just "identifier char") isIdentifierChar
+        ,pure $ T.singleton c]) <?> "identifier"
+  where
+     isFirstLetter c = c == '_' || isAlpha c
+
+isIdentifierChar :: Char -> Bool
+isIdentifierChar c = c == '_' || isAlphaNum c
+
+--------------------------------------
+
+lineComment :: Dialect -> Parser Token
+lineComment _ = do
+    try (string_ "--") <?> ""
+    rest <- takeWhileP (Just "non newline character") (/='\n')
+    -- can you optionally read the \n to terminate the takewhilep without reparsing it?
+    suf <- option "" ("\n" <$ char_ '\n')
+    pure $ LineComment $ T.concat ["--", rest, suf]
+
+--------------------------------------
+
+-- TODO: the parser before the switch to megaparsec parsed nested block comments
+-- I don't know any dialects that use this, but I think it's useful, if needed,
+-- add it back in under a dialect flag?
+blockComment :: Dialect -> Parser Token
+blockComment _ = (do
+    try $ string_ "/*"
+    BlockComment . T.concat . ("/*":) <$> more) <?> ""
+  where
+    more = choice
+        [["*/"] <$ try (string_ "*/")  -- comment ended
+        ,char_ '*' *> (("*":) <$> more) -- comment contains * but this isn't the comment end token
+        -- not sure if there's an easy optimisation here
+        ,(:) <$> takeWhile1P (Just "non comment terminator text") (/= '*') <*> more]
+
+{-
+This is to improve user experience: provide an error if we see */
+outside a comment. This could potentially break postgres ops with */
+in them (which is a stupid thing to do). In other cases, the user
+should write * / instead (I can't think of any cases when this would
+be valid syntax though).
+-}
+
+dontParseEndBlockComment :: Dialect -> Parser Token
+dontParseEndBlockComment _ =
+    -- don't use try, then it should commit to the error
+    try (string "*/") *> fail "comment end without comment start"
+
+--------------------------------------
+
+{-
+numbers
+
+digits
+digits.[digits][e[+-]digits]
+[digits].digits[e[+-]digits]
+digitse[+-]digits
+
+where digits is one or more decimal digits (0 through 9). At least one
+digit must be before or after the decimal point, if one is used. At
+least one digit must follow the exponent marker (e), if one is
+present. There cannot be any spaces or other characters embedded in
+the constant. Note that any leading plus or minus sign is not actually
+considered part of the constant; it is an operator applied to the
+constant.
+
+
+algorithm:
+either
+  parse 1 or more digits
+    then an optional dot which isn't two dots
+    then optional digits
+  or: parse a dot which isn't two dots
+    then digits
+followed by an optional exponent
+-}
+
+sqlNumber :: Dialect -> Parser Token
+sqlNumber d =
+    SqlNumber <$> completeNumber
+    -- this is for definitely avoiding possibly ambiguous source
+    <* choice [-- special case to allow e.g. 1..2
+               guard (diPostgresSymbols d)
+               *> void (lookAhead $ try (string ".." <?> ""))
+                  <|> void (notFollowedBy (oneOf "eE."))
+              ,notFollowedBy (oneOf "eE.")
+              ]
+  where
+    completeNumber =
+      (digits <??> (pp dot <??.> pp digits)
+      -- try is used in case we read a dot
+      -- and it isn't part of a number
+      -- if there are any following digits, then we commit
+      -- to it being a number and not something else
+      <|> try ((<>) <$> dot <*> digits))
+      <??> pp expon
+
+    -- make sure we don't parse two adjacent dots in a number
+    -- special case for postgresql, we backtrack if we see two adjacent dots
+    -- to parse 1..2, but in other dialects we commit to the failure
+    dot = let p = string "." <* notFollowedBy (char '.')
+          in if diPostgresSymbols d
+             then try p
+             else p
+    expon = T.cons <$> oneOf "eE" <*> sInt
+    sInt = (<>) <$> option "" (T.singleton <$> oneOf "+-") <*> digits
+    pp = (<$$> (<>))
+    p <??> q = p <**> option id q
+    pa <$$> c = pa <**> pure (flip c)
+    pa <??.> pb =
+       let c = (<$>) . flip
+       in (.) `c` pa <*> option id pb
+
+digits :: Parser Text
+digits = takeWhile1P (Just "digit") isDigit
+
+--------------------------------------
+
+positionalArg :: Dialect -> Parser Token
+positionalArg d =
+    guard (diPositionalArg d) >>
+    -- use try to avoid ambiguities with other syntax which starts with dollar
+    PositionalArg <$> try (char_ '$' *> (read . T.unpack <$> digits))
+
+--------------------------------------
+
+-- todo: I think the try here should read a prefix char, then a single valid
+-- identifier char, then commit
+prefixedVariable :: Dialect -> Parser Token
+prefixedVariable d = try $ choice
+    [PrefixedVariable <$> char ':' <*> identifierString
+    ,guard (diAtIdentifier d) >>
+     PrefixedVariable <$> char '@' <*> identifierString
+    ,guard (diHashIdentifier d) >>
+     PrefixedVariable <$> char '#' <*> identifierString
+    ]
+
+--------------------------------------
+
+{-
+Symbols
+
+A symbol is an operator, or one of the misc symbols which include:
+. .. := : :: ( ) ? ; , { } (for odbc)
+
+The postgresql operator syntax allows a huge range of operators
+compared with ansi and other dialects
+-}
+
+symbol :: Dialect -> Parser Token
+symbol d  = Symbol <$> choice (concat
+   [dots
+   ,if diPostgresSymbols d
+    then postgresExtraSymbols
+    else []
+   ,miscSymbol
+   ,if diOdbc d then odbcSymbol else []
+   ,if diPostgresSymbols d
+    then generalizedPostgresqlOperator
+    else basicAnsiOps
+   ])
+ where
+   dots = [takeWhile1P (Just "dot") (=='.')]
+   odbcSymbol = [string "{", string "}"]
+   postgresExtraSymbols =
+       [try (string ":=")
+        -- parse :: and : and avoid allowing ::: or more
+       ,try (string "::" <* notFollowedBy (char ':'))
+       ,try (string ":" <* notFollowedBy (char ':'))]
+   miscSymbol = map (string . T.singleton) $
+                case () of
+                    _ | diSqlServerSymbols d -> ",;():?"
+                      | diPostgresSymbols d -> "[],;()"
+                      | otherwise -> "[],;():?"
+
+{-
+try is used because most of the first characters of the two character
+symbols can also be part of a single character symbol
+-}
+
+   basicAnsiOps = map (try . string) [">=","<=","!=","<>"]
+                  ++ map (string . T.singleton) "+-^*/%~&<>="
+                  ++ pipes
+   pipes = -- what about using many1 (char '|'), then it will
+           -- fail in the parser? Not sure exactly how
+           -- standalone the lexer should be
+           [char '|' *>
+            choice ["||" <$ char '|' <* notFollowedBy (char '|')
+                   ,pure "|"]]
+
+{-
+postgresql generalized operators
+
+this includes the custom operators that postgres supports,
+plus all the standard operators which could be custom operators
+according to their grammar
+
+rules
+
+An operator name is a sequence of up to NAMEDATALEN-1 (63 by default) characters from the following list:
+
++ - * / < > = ~ ! @ # % ^ & | ` ?
+
+There are a few restrictions on operator names, however:
+-- and /* cannot appear anywhere in an operator name, since they will be taken as the start of a comment.
+
+A multiple-character operator name cannot end in + or -, unless the name also contains at least one of these characters:
+
+~ ! @ # % ^ & | ` ?
+
+which allows the last character of a multi character symbol to be + or
+-
+-}
+
+generalizedPostgresqlOperator :: [Parser Text]
+generalizedPostgresqlOperator = [singlePlusMinus,opMoreChars]
+  where
+    allOpSymbols = "+-*/<>=~!@#%^&|`?"
+    -- these are the symbols when if part of a multi character
+    -- operator permit the operator to end with a + or - symbol
+    exceptionOpSymbols = "~!@#%^&|`?"
+
+    -- special case for parsing a single + or - symbol
+    singlePlusMinus = try $ do
+      c <- oneOf "+-"
+      notFollowedBy $ oneOf allOpSymbols
+      pure $ T.singleton c
+
+    -- this is used when we are parsing a potentially multi symbol
+    -- operator and we have alread seen one of the 'exception chars'
+    -- and so we can end with a + or -
+    moreOpCharsException = do
+       c <- oneOf (filter (`notElemChar` "-/*") allOpSymbols)
+            -- make sure we don't parse a comment starting token
+            -- as part of an operator
+            <|> try (char '/' <* notFollowedBy (char '*'))
+            <|> try (char '-' <* notFollowedBy (char '-'))
+            -- and make sure we don't parse a block comment end
+            -- as part of another symbol
+            <|> try (char '*' <* notFollowedBy (char '/'))
+       T.cons c <$> option "" moreOpCharsException
+
+    opMoreChars = choice
+       [-- parse an exception char, now we can finish with a + -
+        T.cons
+        <$> oneOf exceptionOpSymbols
+        <*> option "" moreOpCharsException
+       ,T.cons
+        <$> (-- parse +, make sure it isn't the last symbol
+             try (char '+' <* lookAhead (oneOf allOpSymbols))
+             <|> -- parse -, make sure it isn't the last symbol
+                 -- or the start of a -- comment
+             try (char '-'
+                  <* notFollowedBy (char '-')
+                  <* lookAhead (oneOf allOpSymbols))
+             <|> -- parse / check it isn't the start of a /* comment
+             try (char '/' <* notFollowedBy (char '*'))
+             <|> -- make sure we don't parse */ as part of a symbol
+             try (char '*' <* notFollowedBy (char '/'))
+             <|> -- any other ansi operator symbol
+             oneOf "<>=")
+        <*> option "" opMoreChars
+       ]
+
+--------------------------------------
+
+sqlWhitespace :: Dialect -> Parser Token
+sqlWhitespace _ = Whitespace <$> takeWhile1P (Just "whitespace") isSpace <?> ""
+
+----------------------------------------------------------------------------
+
+-- parser helpers
+
+char_ :: Char -> Parser ()
+char_ = void . char
+
+string_ :: Text -> Parser ()
+string_ = void . string
+
+oneOf :: [Char] -> Parser Char
+oneOf = M.oneOf
+
+notElemChar :: Char -> [Char] -> Bool
+notElemChar a b = a `notElem` (b :: [Char])
+
+----------------------------------------------------------------------------
+
+
+{-
+This utility function will accurately report if the two tokens are
+pretty printed, if they should lex back to the same two tokens. This
+function is used in testing (and can be used in other places), and
+must not be implemented by actually trying to print both tokens and
+then lex them back from a single string (because then we would have
+the risk of thinking two tokens cannot be together when there is bug
+in the lexer, which the testing is supposed to find).
+
+maybe do some quick checking to make sure this function only gives
+true negatives: check pairs which return false actually fail to lex or
+give different symbols in return: could use quickcheck for this
+
+a good sanity test for this function is to change it to always return
+true, then check that the automated tests return the same number of
+successes. I don't think it succeeds this test at the moment
+-}
+
+-- | Utility function to tell you if a list of tokens
+-- will pretty print then lex back to the same set of tokens.
+-- Used internally, might be useful for generating SQL via lexical tokens.
+tokenListWillPrintAndLex :: Dialect -> [Token] -> Bool
+tokenListWillPrintAndLex _ [] = True
+tokenListWillPrintAndLex _ [_] = True
+tokenListWillPrintAndLex d (a:b:xs) =
+    tokensWillPrintAndLex d a b && tokenListWillPrintAndLex d (b:xs)
+
+tokensWillPrintAndLex :: Dialect -> Token -> Token -> Bool
+tokensWillPrintAndLex d a b
+
+{-
+a : followed by an identifier character will look like a host param
+followed by = or : makes a different symbol
+-}
+
+    | Symbol ":" <- a
+    , checkFirstBChar (\x -> isIdentifierChar x || x `T.elem` ":=") = False
+
+{-
+two symbols next to eachother will fail if the symbols can combine and
+(possibly just the prefix) look like a different symbol
+-}
+
+    | diPostgresSymbols d
+    , Symbol a' <- a
+    , Symbol b' <- b
+    , b' `notElem` ["+", "-"] || any (`T.elem` a') ("~!@#%^&|`?" :: [Char]) = False
+
+{-
+check two adjacent symbols in non postgres where the combination
+possibilities are much more limited. This is ansi behaviour, it might
+be different when the other dialects are done properly
+-}
+
+   | Symbol a' <- a
+   , Symbol b' <- b
+   , (a',b') `elem` [("<",">")
+                    ,("<","=")
+                    ,(">","=")
+                    ,("!","=")
+                    ,("|","|")
+                    ,("||","|")
+                    ,("|","||")
+                    ,("||","||")
+                    ,("<",">=")
+                    ] = False
+
+-- two whitespaces will be combined
+
+   | Whitespace {} <- a
+   , Whitespace {} <- b = False
+
+-- line comment without a newline at the end will eat the next token
+
+   | LineComment {} <- a
+   , checkLastAChar (/='\n') = False
+
+{-
+check the last character of the first token and the first character of
+the second token forming a comment start or end symbol
+-}
+
+   | let f '-' '-' = True
+         f '/' '*' = True
+         f '*' '/' = True
+         f _ _ = False
+     in checkBorderChars f = False
+
+{-
+a symbol will absorb a following .
+TODO: not 100% on this always being bad
+-}
+
+   | Symbol {} <- a
+   , checkFirstBChar (=='.') = False
+
+-- cannot follow a symbol ending in : with another token starting with :
+
+   | let f ':' ':' = True
+         f _ _ = False
+     in checkBorderChars f = False
+
+-- unquoted identifier followed by an identifier letter
+
+   | Identifier Nothing _ <- a
+   , checkFirstBChar isIdentifierChar = False
+
+-- a quoted identifier using ", followed by a " will fail
+
+   | Identifier (Just (_,"\"")) _ <- a
+   , checkFirstBChar (=='"') = False
+
+-- prefixed variable followed by an identifier char will be absorbed
+
+   | PrefixedVariable {} <- a
+   , checkFirstBChar isIdentifierChar = False
+
+-- a positional arg will absorb a following digit
+
+   | PositionalArg {} <- a
+   , checkFirstBChar isDigit = False
+
+-- a string ending with ' followed by a token starting with ' will be absorbed
+
+   | SqlString _ "'" _ <- a
+   , checkFirstBChar (=='\'') = False
+
+-- a number followed by a . will fail or be absorbed
+
+   | SqlNumber {} <- a
+   , checkFirstBChar (=='.') = False
+
+-- a number followed by an e or E will fail or be absorbed
+
+   | SqlNumber {} <- a
+   , checkFirstBChar (\x -> x =='e' || x == 'E') = False
+
+-- two numbers next to eachother will fail or be absorbed
+
+   | SqlNumber {} <- a
+   , SqlNumber {} <- b = False
+
+
+   | otherwise = True
+
+  where
+    prettya = prettyToken d a
+    prettyb = prettyToken d b
+    -- helper function to run a predicate on the
+    -- last character of the first token and the first
+    -- character of the second token
+    checkBorderChars f =
+        case (T.unsnoc prettya, T.uncons prettyb) of
+            (Just (_,la), Just (fb,_)) -> f la fb
+            _ -> False
+    checkFirstBChar f = case T.uncons prettyb of
+        Just (b',_) -> f b'
+        _ -> False
+    checkLastAChar f = case T.unsnoc prettya of
+        Just (_,la) -> f la
+        _ -> False
+
+------------------------------------------------------------------------------
+
+-- megaparsec stream boilerplate
+
+-- | Wrapper to allow using the lexer as input to a megaparsec parser.
+data SQLStream = SQLStream
+  { sqlStreamInput :: String
+  , unSQLStream :: [WithPos Token]
+  }
+
+instance M.Stream SQLStream where
+  type Token  SQLStream = WithPos Token
+  type Tokens SQLStream = [WithPos Token]
+
+  tokenToChunk Proxy x = [x]
+  tokensToChunk Proxy xs = xs
+  chunkToTokens Proxy = id
+  chunkLength Proxy = length
+  chunkEmpty Proxy = null
+  take1_ (SQLStream _ []) = Nothing
+  take1_ (SQLStream str (t:ts)) = Just
+    ( t
+    , SQLStream (drop (tokensLength pxy (t NE.:|[])) str) ts
+    )
+  takeN_ n (SQLStream str s)
+    | n <= 0    = Just ([], SQLStream str s)
+    | null s    = Nothing
+    | otherwise =
+        let (x, s') = splitAt n s
+        in case NE.nonEmpty x of
+          Nothing -> Just (x, SQLStream str s')
+          Just nex -> Just (x, SQLStream (drop (tokensLength pxy nex) str) s')
+  takeWhile_ f (SQLStream str s) =
+    let (x, s') = DL.span f s
+    in case NE.nonEmpty x of
+      Nothing -> (x, SQLStream str s')
+      Just nex -> (x, SQLStream (drop (tokensLength pxy nex) str) s')
+
+instance VisualStream SQLStream where
+  showTokens Proxy = DL.intercalate " "
+    . NE.toList
+    . fmap (showMyToken . tokenVal)
+  tokensLength Proxy xs = sum (tokenLength <$> xs)
+
+instance TraversableStream SQLStream where
+  reachOffset o M.PosState {..} =
+    ( Just (prefix ++ restOfLine)
+    , PosState
+        { pstateInput = SQLStream
+            { sqlStreamInput = postStr
+            , unSQLStream = post
+            }
+        , pstateOffset = max pstateOffset o
+        , pstateSourcePos = newSourcePos
+        , pstateTabWidth = pstateTabWidth
+        , pstateLinePrefix = prefix
+        }
+    )
+    where
+      prefix =
+        if sameLine
+          then pstateLinePrefix ++ preLine
+          else preLine
+      sameLine = sourceLine newSourcePos == sourceLine pstateSourcePos
+      newSourcePos =
+        case post of
+          [] -> case unSQLStream pstateInput of
+            [] -> pstateSourcePos
+            xs -> endPos (last xs)
+          (x:_) -> startPos x
+      (pre, post) = splitAt (o - pstateOffset) (unSQLStream pstateInput)
+      (preStr, postStr) = splitAt tokensConsumed (sqlStreamInput pstateInput)
+      preLine = reverse . takeWhile (/= '\n') . reverse $ preStr
+      tokensConsumed =
+        case NE.nonEmpty pre of
+          Nothing -> 0
+          Just nePre -> tokensLength pxy nePre
+      restOfLine = takeWhile (/= '\n') postStr
+
+pxy :: Proxy SQLStream
+pxy = Proxy
+
+showMyToken :: Token -> String
+-- todo: how to do this properly?
+showMyToken = T.unpack . prettyToken ansi2011
diff --git a/Language/SQL/SimpleSQL/Lex.lhs b/Language/SQL/SimpleSQL/Lex.lhs
deleted file mode 100644
--- a/Language/SQL/SimpleSQL/Lex.lhs
+++ /dev/null
@@ -1,717 +0,0 @@
-
-The parser uses a separate lexer for two reasons:
-
-1. sql syntax is very awkward to parse, the separate lexer makes it
-easier to handle this in most places (in some places it makes it
-harder or impossible, the fix is to switch to something better than
-parsec)
-
-2. using a separate lexer gives a huge speed boost because it reduces
-backtracking. (We could get this by making the parsing code a lot more
-complex also.)
-
-3. we can test the lexer relatively exhaustively, then even when we
-don't do nearly as comprehensive testing on the syntax level, we still
-have a relatively high assurance of the low level of bugs. This is
-much more difficult to get parity with when testing the syntax parser
-directly without the separately testing lexing stage.
-
-> -- | Lexer for SQL.
-> {-# LANGUAGE TupleSections #-}
-> module Language.SQL.SimpleSQL.Lex
->     (Token(..)
->     ,lexSQL
->     ,prettyToken
->     ,prettyTokens
->     ,ParseError(..)
->     ,tokenListWillPrintAndLex
->     ,ansi2011
->     ) where
-
-> import Language.SQL.SimpleSQL.Dialect
-
-> import Text.Parsec (option,string,manyTill,anyChar
->                    ,try,string,many1,oneOf,digit,(<|>),choice,char,eof
->                    ,many,runParser,lookAhead,satisfy
->                    ,setPosition,getPosition
->                    ,setSourceColumn,setSourceLine
->                    ,sourceName, setSourceName
->                    ,sourceLine, sourceColumn
->                    ,notFollowedBy)
-> import Language.SQL.SimpleSQL.Combinators
-> import Language.SQL.SimpleSQL.Errors
-> import Control.Applicative hiding ((<|>), many)
-> import Data.Char
-> import Control.Monad
-> import Prelude hiding (takeWhile)
-> import Text.Parsec.String (Parser)
-> import Data.Maybe
-
-
-> -- | Represents a lexed token
-> data Token
->     -- | A symbol (in ansi dialect) is one of the following
->     --
->     -- * multi char symbols <> \<= \>= != ||
->     -- * single char symbols: * + -  < >  ^ / %  ~ & | ? ( ) [ ] , ; ( )
->     --
->     = Symbol String
->
->     -- | This is an identifier or keyword. The first field is
->     -- the quotes used, or nothing if no quotes were used. The quotes
->     -- can be " or u& or something dialect specific like []
->     | Identifier (Maybe (String,String)) String
->
->     -- | This is a prefixed variable symbol, such as :var, @var or #var
->     -- (only :var is used in ansi dialect)
->     | PrefixedVariable Char String
->
->     -- | This is a positional arg identifier e.g. $1
->     | PositionalArg Int
->
->     -- | This is a string literal. The first two fields are the --
->     -- start and end quotes, which are usually both ', but can be
->     -- the character set (one of nNbBxX, or u&, U&), or a dialect
->     -- specific string quoting (such as $$ in postgres)
->     | SqlString String String String
->
->     -- | A number literal (integral or otherwise), stored in original format
->     -- unchanged
->     | SqlNumber String
->
->     -- | Whitespace, one or more of space, tab or newline.
->     | Whitespace String
->
->     -- | A commented line using --, contains every character starting with the
->     -- \'--\' and including the terminating newline character if there is one
->     -- - this will be missing if the last line in the source is a line comment
->     -- with no trailing newline
->     | LineComment String
->
->     -- | A block comment, \/* stuff *\/, includes the comment delimiters
->     | BlockComment String
->
->       deriving (Eq,Show)
-
-
-
-> -- | Pretty printing, if you lex a bunch of tokens, then pretty
-> -- print them, should should get back exactly the same string
-> prettyToken :: Dialect -> Token -> String
-> prettyToken _ (Symbol s) = s
-> prettyToken _ (Identifier Nothing t) = t
-> prettyToken _ (Identifier (Just (q1,q2)) t) = q1 ++ t ++ q2
-> prettyToken _ (PrefixedVariable c p) = c:p
-> prettyToken _ (PositionalArg p) = '$':show p
-> prettyToken _ (SqlString s e t) = s ++ t ++ e
-> prettyToken _ (SqlNumber r) = r
-> prettyToken _ (Whitespace t) = t
-> prettyToken _ (LineComment l) = l
-> prettyToken _ (BlockComment c) = c
-
-> prettyTokens :: Dialect -> [Token] -> String
-> prettyTokens d ts = concat $ map (prettyToken d) ts
-
-TODO: try to make all parsers applicative only
-
-> -- | Lex some SQL to a list of tokens.
-> lexSQL :: Dialect
->                   -- ^ dialect of SQL to use
->                -> FilePath
->                   -- ^ filename to use in error messages
->                -> Maybe (Int,Int)
->                   -- ^ line number and column number of the first character
->                   -- in the source to use in error messages
->                -> String
->                   -- ^ the SQL source to lex
->                -> Either ParseError [((String,Int,Int),Token)]
-> lexSQL dialect fn' p src =
->     let (l',c') = fromMaybe (1,1) p
->     in either (Left . convParseError src) Right
->        $ runParser (setPos (fn',l',c') *> many (sqlToken dialect) <* eof) () fn' src
->   where
->     setPos (fn,l,c) = do
->         fmap (flip setSourceName fn
->                . flip setSourceLine l
->                . flip setSourceColumn c) getPosition
->           >>= setPosition
-
-> -- | parser for a sql token
-> sqlToken :: Dialect -> Parser ((String,Int,Int),Token)
-> sqlToken d = do
->     p' <- getPosition
->     let p = (sourceName p',sourceLine p', sourceColumn p')
-
-The order of parsers is important: strings and quoted identifiers can
-start out looking like normal identifiers, so we try to parse these
-first and use a little bit of try. Line and block comments start like
-symbols, so we try these before symbol. Numbers can start with a . so
-this is also tried before symbol (a .1 will be parsed as a number, but
-. otherwise will be parsed as a symbol).
-
->     (p,) <$> choice [sqlString d
->                     ,identifier d
->                     ,lineComment d
->                     ,blockComment d
->                     ,sqlNumber d
->                     ,positionalArg d
->                     ,dontParseEndBlockComment d
->                     ,prefixedVariable d
->                     ,symbol d
->                     ,sqlWhitespace d]
-
-Parses identifiers:
-
-simple_identifier_23
-u&"unicode quoted identifier"
-"quoted identifier"
-"quoted identifier "" with double quote char"
-`mysql quoted identifier`
-
-> identifier :: Dialect -> Parser Token
-> identifier d =
->     choice
->     [quotedIden
->     ,unicodeQuotedIden
->     ,regularIden
->     ,guard (diBackquotedIden d) >> mySqlQuotedIden
->     ,guard (diSquareBracketQuotedIden d) >> sqlServerQuotedIden
->     ]
->   where
->     regularIden = Identifier Nothing <$> identifierString
->     quotedIden = Identifier (Just ("\"","\"")) <$> qidenPart
->     mySqlQuotedIden = Identifier (Just ("`","`"))
->                       <$> (char '`' *> takeWhile1 (/='`') <* char '`')
->     sqlServerQuotedIden = Identifier (Just ("[","]"))
->                           <$> (char '[' *> takeWhile1 (`notElem` "[]") <* char ']')
->     -- try is used here to avoid a conflict with identifiers
->     -- and quoted strings which also start with a 'u'
->     unicodeQuotedIden = Identifier
->                         <$> (f <$> try (oneOf "uU" <* string "&"))
->                         <*> qidenPart
->       where f x = Just (x: "&\"", "\"")
->     qidenPart = char '"' *> qidenSuffix ""
->     qidenSuffix t = do
->         s <- takeTill (=='"')
->         void $ char '"'
->         -- deal with "" as literal double quote character
->         choice [do
->                 void $ char '"'
->                 qidenSuffix $ concat [t,s,"\"\""]
->                ,return $ concat [t,s]]
-
-
-This parses a valid identifier without quotes.
-
-> identifierString :: Parser String
-> identifierString =
->     startsWith (\c -> c == '_' || isAlpha c) isIdentifierChar
-
-this can be moved to the dialect at some point
-
-> isIdentifierChar :: Char -> Bool
-> isIdentifierChar c = c == '_' || isAlphaNum c
-
-use try because : and @ can be part of other things also
-
-> prefixedVariable :: Dialect -> Parser Token
-> prefixedVariable  d = try $ choice
->     [PrefixedVariable <$> char ':' <*> identifierString
->     ,guard (diAtIdentifier d) >>
->      PrefixedVariable <$> char '@' <*> identifierString
->     ,guard (diHashIdentifier d) >>
->      PrefixedVariable <$> char '#' <*> identifierString
->     ]
-
-> positionalArg :: Dialect -> Parser Token
-> positionalArg d =
->   guard (diPositionalArg d) >>
->   -- use try to avoid ambiguities with other syntax which starts with dollar
->   PositionalArg <$> try (char '$' *> (read <$> many1 digit))
-
-
-Parse a SQL string. Examples:
-
-'basic string'
-'string with '' a quote'
-n'international text'
-b'binary string'
-x'hexidecimal string'
-
-
-> sqlString :: Dialect -> Parser Token
-> sqlString d = dollarString <|> csString <|> normalString
->   where
->     dollarString = do
->         guard $ diDollarString d
->         -- use try because of ambiguity with symbols and with
->         -- positional arg
->         delim <- (\x -> concat ["$",x,"$"])
->                  <$> try (char '$' *> option "" identifierString <* char '$')
->         SqlString delim delim  <$> manyTill anyChar (try $ string delim)
->     normalString = SqlString "'" "'" <$> (char '\'' *> normalStringSuffix False "")
->     normalStringSuffix allowBackslash t = do
->         s <- takeTill $ if allowBackslash
->                         then (`elem` "'\\")
->                         else (== '\'')
->         -- deal with '' or \' as literal quote character
->         choice [do
->                 ctu <- choice ["''" <$ try (string "''")
->                               ,"\\'" <$ string "\\'"
->                               ,"\\" <$ char '\\']
->                 normalStringSuffix allowBackslash $ concat [t,s,ctu]
->                ,concat [t,s] <$ char '\'']
->     -- try is used to to avoid conflicts with
->     -- identifiers which can start with n,b,x,u
->     -- once we read the quote type and the starting '
->     -- then we commit to a string
->     -- it's possible that this will reject some valid syntax
->     -- but only pathalogical stuff, and I think the improved
->     -- error messages and user predictability make it a good
->     -- pragmatic choice
->     csString
->       | diEString d =
->         choice [SqlString <$> try (string "e'" <|> string "E'")
->                           <*> return "'" <*> normalStringSuffix True ""
->                ,csString']
->       | otherwise = csString'
->     csString' = SqlString
->                 <$> try cs
->                 <*> return "'"
->                 <*> normalStringSuffix False ""
->     csPrefixes = "nNbBxX"
->     cs = choice $ (map (\x -> string ([x] ++ "'")) csPrefixes)
->                   ++ [string "u&'"
->                      ,string "U&'"]
-
-numbers
-
-digits
-digits.[digits][e[+-]digits]
-[digits].digits[e[+-]digits]
-digitse[+-]digits
-
-where digits is one or more decimal digits (0 through 9). At least one
-digit must be before or after the decimal point, if one is used. At
-least one digit must follow the exponent marker (e), if one is
-present. There cannot be any spaces or other characters embedded in
-the constant. Note that any leading plus or minus sign is not actually
-considered part of the constant; it is an operator applied to the
-constant.
-
-> sqlNumber :: Dialect -> Parser Token
-> sqlNumber d =
->     SqlNumber <$> completeNumber
->     -- this is for definitely avoiding possibly ambiguous source
->     <* choice [-- special case to allow e.g. 1..2
->                guard (diPostgresSymbols d)
->                *> (void $ lookAhead $ try $ string "..")
->                   <|> void (notFollowedBy (oneOf "eE."))
->               ,notFollowedBy (oneOf "eE.")
->               ]
->   where
->     completeNumber =
->       (int <??> (pp dot <??.> pp int)
->       -- try is used in case we read a dot
->       -- and it isn't part of a number
->       -- if there are any following digits, then we commit
->       -- to it being a number and not something else
->       <|> try ((++) <$> dot <*> int))
->       <??> pp expon
-
->     int = many1 digit
->     -- make sure we don't parse two adjacent dots in a number
->     -- special case for postgresql, we backtrack if we see two adjacent dots
->     -- to parse 1..2, but in other dialects we commit to the failure
->     dot = let p = string "." <* notFollowedBy (char '.')
->           in if diPostgresSymbols d
->              then try p
->              else p
->     expon = (:) <$> oneOf "eE" <*> sInt
->     sInt = (++) <$> option "" (string "+" <|> string "-") <*> int
->     pp = (<$$> (++))
-
-Symbols
-
-A symbol is an operator, or one of the misc symbols which include:
-. .. := : :: ( ) ? ; , { } (for odbc)
-
-The postgresql operator syntax allows a huge range of operators
-compared with ansi and other dialects
-
-> symbol :: Dialect -> Parser Token
-> symbol d  = Symbol <$> choice (concat
->    [dots
->    ,if diPostgresSymbols d
->     then postgresExtraSymbols
->     else []
->    ,miscSymbol
->    ,if diOdbc d then odbcSymbol else []
->    ,if diPostgresSymbols d
->     then generalizedPostgresqlOperator
->     else basicAnsiOps
->    ])
->  where
->    dots = [many1 (char '.')]
->    odbcSymbol = [string "{", string "}"]
->    postgresExtraSymbols =
->        [try (string ":=")
->         -- parse :: and : and avoid allowing ::: or more
->        ,try (string "::" <* notFollowedBy (char ':'))
->        ,try (string ":" <* notFollowedBy (char ':'))]
->    miscSymbol = map (string . (:[])) $
->                 case () of
->                     _ | diSqlServerSymbols d -> ",;():?"
->                       | diPostgresSymbols d -> "[],;()"
->                       | otherwise -> "[],;():?"
-
-try is used because most of the first characters of the two character
-symbols can also be part of a single character symbol
-
->    basicAnsiOps = map (try . string) [">=","<=","!=","<>"]
->                   ++ map (string . (:[])) "+-^*/%~&<>="
->                   ++ pipes
->    pipes = -- what about using many1 (char '|'), then it will
->            -- fail in the parser? Not sure exactly how
->            -- standalone the lexer should be
->            [char '|' *>
->             choice ["||" <$ char '|' <* notFollowedBy (char '|')
->                    ,return "|"]]
-
-postgresql generalized operators
-
-this includes the custom operators that postgres supports,
-plus all the standard operators which could be custom operators
-according to their grammar
-
-rules
-
-An operator name is a sequence of up to NAMEDATALEN-1 (63 by default) characters from the following list:
-
-+ - * / < > = ~ ! @ # % ^ & | ` ?
-
-There are a few restrictions on operator names, however:
--- and /* cannot appear anywhere in an operator name, since they will be taken as the start of a comment.
-
-A multiple-character operator name cannot end in + or -, unless the name also contains at least one of these characters:
-
-~ ! @ # % ^ & | ` ?
-
-which allows the last character of a multi character symbol to be + or
--
-
-> generalizedPostgresqlOperator :: [Parser String]
-> generalizedPostgresqlOperator = [singlePlusMinus,opMoreChars]
->   where
->     allOpSymbols = "+-*/<>=~!@#%^&|`?"
->     -- these are the symbols when if part of a multi character
->     -- operator permit the operator to end with a + or - symbol
->     exceptionOpSymbols = "~!@#%^&|`?"
-
->     -- special case for parsing a single + or - symbol
->     singlePlusMinus = try $ do
->       c <- oneOf "+-"
->       notFollowedBy $ oneOf allOpSymbols
->       return [c]
-
->     -- this is used when we are parsing a potentially multi symbol
->     -- operator and we have alread seen one of the 'exception chars'
->     -- and so we can end with a + or -
->     moreOpCharsException = do
->        c <- oneOf (filter (`notElem` "-/*") allOpSymbols)
->             -- make sure we don't parse a comment starting token
->             -- as part of an operator
->             <|> try (char '/' <* notFollowedBy (char '*'))
->             <|> try (char '-' <* notFollowedBy (char '-'))
->             -- and make sure we don't parse a block comment end
->             -- as part of another symbol
->             <|> try (char '*' <* notFollowedBy (char '/'))
->        (c:) <$> option [] moreOpCharsException
-
->     opMoreChars = choice
->        [-- parse an exception char, now we can finish with a + -
->         (:)
->         <$> oneOf exceptionOpSymbols
->         <*> option [] moreOpCharsException
->        ,(:)
->         <$> (-- parse +, make sure it isn't the last symbol
->              try (char '+' <* lookAhead (oneOf allOpSymbols))
->              <|> -- parse -, make sure it isn't the last symbol
->                  -- or the start of a -- comment
->              try (char '-'
->                   <* notFollowedBy (char '-')
->                   <* lookAhead (oneOf allOpSymbols))
->              <|> -- parse / check it isn't the start of a /* comment
->              try (char '/' <* notFollowedBy (char '*'))
->              <|> -- make sure we don't parse */ as part of a symbol
->              try (char '*' <* notFollowedBy (char '/'))
->              <|> -- any other ansi operator symbol
->              oneOf "<>=")
->         <*> option [] opMoreChars
->        ]
-
-> sqlWhitespace :: Dialect -> Parser Token
-> sqlWhitespace _ = Whitespace <$> many1 (satisfy isSpace)
-
-> lineComment :: Dialect -> Parser Token
-> lineComment _ =
->     (\s -> LineComment $ concat ["--",s]) <$>
->     -- try is used here in case we see a - symbol
->     -- once we read two -- then we commit to the comment token
->     (try (string "--") *> (
->         -- todo: there must be a better way to do this
->      conc <$> manyTill anyChar (lookAhead lineCommentEnd) <*> lineCommentEnd))
->   where
->     conc a Nothing = a
->     conc a (Just b) = a ++ b
->     lineCommentEnd =
->         Just "\n" <$ char '\n'
->         <|> Nothing <$ eof
-
-Try is used in the block comment for the two symbol bits because we
-want to backtrack if we read the first symbol but the second symbol
-isn't there.
-
-> blockComment :: Dialect -> Parser Token
-> blockComment _ =
->     (\s -> BlockComment $ concat ["/*",s]) <$>
->     (try (string "/*") *> commentSuffix 0)
->   where
->     commentSuffix :: Int -> Parser String
->     commentSuffix n = do
->       -- read until a possible end comment or nested comment
->       x <- takeWhile (\e -> e /= '/' && e /= '*')
->       choice [-- close comment: if the nesting is 0, done
->               -- otherwise recurse on commentSuffix
->               try (string "*/") *> let t = concat [x,"*/"]
->                                    in if n == 0
->                                       then return t
->                                       else (\s -> concat [t,s]) <$> commentSuffix (n - 1)
->               -- nested comment, recurse
->              ,try (string "/*") *> ((\s -> concat [x,"/*",s]) <$> commentSuffix (n + 1))
->               -- not an end comment or nested comment, continue
->              ,(\c s -> x ++ [c] ++ s) <$> anyChar <*> commentSuffix n]
-
-
-This is to improve user experience: provide an error if we see */
-outside a comment. This could potentially break postgres ops with */
-in them (which is a stupid thing to do). In other cases, the user
-should write * / instead (I can't think of any cases when this would
-be valid syntax though).
-
-> dontParseEndBlockComment :: Dialect -> Parser Token
-> dontParseEndBlockComment _ =
->     -- don't use try, then it should commit to the error
->     try (string "*/") *> fail "comment end without comment start"
-
-
-Some helper combinators
-
-> startsWith :: (Char -> Bool) -> (Char -> Bool) -> Parser String
-> startsWith p ps = do
->   c <- satisfy p
->   choice [(:) c <$> (takeWhile1 ps)
->          ,return [c]]
-
-> takeWhile1 :: (Char -> Bool) -> Parser String
-> takeWhile1 p = many1 (satisfy p)
-
-> takeWhile :: (Char -> Bool) -> Parser String
-> takeWhile p = many (satisfy p)
-
-> takeTill :: (Char -> Bool) -> Parser String
-> takeTill p = manyTill anyChar (peekSatisfy p)
-
-> peekSatisfy :: (Char -> Bool) -> Parser ()
-> peekSatisfy p = void $ lookAhead (satisfy p)
-
-This utility function will accurately report if the two tokens are
-pretty printed, if they should lex back to the same two tokens. This
-function is used in testing (and can be used in other places), and
-must not be implemented by actually trying to print both tokens and
-then lex them back from a single string (because then we would have
-the risk of thinking two tokens cannot be together when there is bug
-in the lexer, which the testing is supposed to find).
-
-maybe do some quick checking to make sure this function only gives
-true negatives: check pairs which return false actually fail to lex or
-give different symbols in return: could use quickcheck for this
-
-a good sanity test for this function is to change it to always return
-true, then check that the automated tests return the same number of
-successes. I don't think it succeeds this test at the moment
-
-> -- | Utility function to tell you if a list of tokens
-> -- will pretty print then lex back to the same set of tokens.
-> -- Used internally, might be useful for generating SQL via lexical tokens.
-> tokenListWillPrintAndLex :: Dialect -> [Token] -> Bool
-> tokenListWillPrintAndLex _ [] = True
-> tokenListWillPrintAndLex _ [_] = True
-> tokenListWillPrintAndLex d (a:b:xs) =
->     tokensWillPrintAndLex d a b && tokenListWillPrintAndLex d (b:xs)
-
-> tokensWillPrintAndLex :: Dialect -> Token -> Token -> Bool
-> tokensWillPrintAndLex d a b
-
-a : followed by an identifier character will look like a host param
-followed by = or : makes a different symbol
-
->     | Symbol ":" <- a
->     , checkFirstBChar (\x -> isIdentifierChar x || x `elem` ":=") = False
-
-two symbols next to eachother will fail if the symbols can combine and
-(possibly just the prefix) look like a different symbol
-
->     | diPostgresSymbols d
->     , Symbol a' <- a
->     , Symbol b' <- b
->     , b' `notElem` ["+", "-"] || or (map (`elem` a') "~!@#%^&|`?") = False
-
-check two adjacent symbols in non postgres where the combination
-possibilities are much more limited. This is ansi behaviour, it might
-be different when the other dialects are done properly
-
->    | Symbol a' <- a
->    , Symbol b' <- b
->    , (a',b') `elem` [("<",">")
->                     ,("<","=")
->                     ,(">","=")
->                     ,("!","=")
->                     ,("|","|")
->                     ,("||","|")
->                     ,("|","||")
->                     ,("||","||")
->                     ,("<",">=")
->                     ] = False
-
-two whitespaces will be combined
-
->    | Whitespace {} <- a
->    , Whitespace {} <- b = False
-
-line comment without a newline at the end will eat the next token
-
->    | LineComment {} <- a
->    , checkLastAChar (/='\n') = False
-
-check the last character of the first token and the first character of
-the second token forming a comment start or end symbol
-
->    | let f '-' '-' = True
->          f '/' '*' = True
->          f '*' '/' = True
->          f _ _ = False
->      in checkBorderChars f = False
-
-a symbol will absorb a following .
-TODO: not 100% on this always being bad
-
->    | Symbol {} <- a
->    , checkFirstBChar (=='.') = False
-
-cannot follow a symbol ending in : with another token starting with :
-
->    | let f ':' ':' = True
->          f _ _ = False
->      in checkBorderChars f = False
-
-unquoted identifier followed by an identifier letter
-
->    | Identifier Nothing _ <- a
->    , checkFirstBChar isIdentifierChar = False
-
-a quoted identifier using ", followed by a " will fail
-
->    | Identifier (Just (_,"\"")) _ <- a
->    , checkFirstBChar (=='"') = False
-
-prefixed variable followed by an identifier char will be absorbed
-
->    | PrefixedVariable {} <- a
->    , checkFirstBChar isIdentifierChar = False
-
-a positional arg will absorb a following digit
-
->    | PositionalArg {} <- a
->    , checkFirstBChar isDigit = False
-
-a string ending with ' followed by a token starting with ' will be absorbed
-
->    | SqlString _ "'" _ <- a
->    , checkFirstBChar (=='\'') = False
-
-a number followed by a . will fail or be absorbed
-
->    | SqlNumber {} <- a
->    , checkFirstBChar (=='.') = False
-
-a number followed by an e or E will fail or be absorbed
-
->    | SqlNumber {} <- a
->    , checkFirstBChar (\x -> x =='e' || x == 'E') = False
-
-two numbers next to eachother will fail or be absorbed
-
->    | SqlNumber {} <- a
->    , SqlNumber {} <- b = False
-
-
->    | otherwise = True
-
->   where
->     prettya = prettyToken d a
->     prettyb = prettyToken d b
->     -- helper function to run a predicate on the
->     -- last character of the first token and the first
->     -- character of the second token
->     checkBorderChars f
->         | (_:_) <- prettya
->         , (fb:_) <- prettyb
->         , la <- last prettya
->         = f la fb
->     checkBorderChars _ = False
->     checkFirstBChar f = case prettyb of
->                           (b':_) -> f b'
->                           _ -> False
->     checkLastAChar f = case prettya of
->                           (_:_) -> f $ last prettya
->                           _ -> False
-
-
-
-
-TODO:
-
-make the tokenswill print more dialect accurate. Maybe add symbol
-  chars and identifier chars to the dialect definition and use them from
-  here
-
-start adding negative / different parse dialect tests
-
-add token tables and tests for oracle, sql server
-review existing tables
-
-look for refactoring opportunities, especially the token
-generation tables in the tests
-
-do some user documentation on lexing, and lexing/dialects
-
-start thinking about a more separated design for the dialect handling
-
-lexing tests are starting to take a really long time, so split the
-tests so it is much easier to run all the tests except the lexing
-tests which only need to be run when working on the lexer (which
-should be relatively uncommon), or doing a commit or finishing off a
-series of commits,
-
-start writing the error message tests:
-  generate/write a large number of syntax errors
-  create a table with the source and the error message
-  try to compare some different versions of code to compare the
-    quality of the error messages by hand
-
-  get this checked in so improvements and regressions in the error
-    message quality can be tracked a little more easily (although it will
-    still be manual)
-
-try again to add annotation to the ast
diff --git a/Language/SQL/SimpleSQL/Parse.hs b/Language/SQL/SimpleSQL/Parse.hs
new file mode 100644
--- /dev/null
+++ b/Language/SQL/SimpleSQL/Parse.hs
@@ -0,0 +1,2370 @@
+
+{-
+= TOC:
+
+notes
+Public api
+Names - parsing identifiers
+Typenames
+Scalar expressions
+  simple literals
+  star, param
+  parens expression, row constructor and scalar subquery
+  case, cast, exists, unique, array/ multiset constructor
+  typed literal, app, special function, aggregate, window function
+  suffixes: in, between, quantified comparison, match predicate, array
+    subscript, escape, collate
+  operators
+  scalar expression top level
+  helpers
+query expressions
+  select lists
+  from clause
+  other table expression clauses:
+    where, group by, having, order by, offset and fetch
+  common table expressions
+  query expression
+  set operations
+lexers
+utilities
+
+= Notes about the code
+
+The lexers appear at the bottom of the file. There tries to be a clear
+separation between the lexers and the other parser which only use the
+lexers, this isn't 100% complete at the moment and needs fixing.
+
+== Left factoring
+
+The parsing code is aggressively left factored, and try is avoided as
+much as possible. Try is avoided because:
+
+ * when it is overused it makes the code hard to follow
+ * when it is overused it makes the parsing code harder to debug
+ * it makes the parser error messages much worse
+
+The code could be made a bit simpler with a few extra 'trys', but this
+isn't done because of the impact on the parser error
+messages. Apparently it can also help the speed but this hasn't been
+looked into.
+
+== Parser error messages
+
+A lot of care has been given to generating good parser error messages
+for invalid syntax. There are a few utils below which partially help
+in this area.
+
+There is a set of crafted bad expressions in ErrorMessages.hs, these
+are used to guage the quality of the error messages and monitor
+regressions by hand. The use of <?> is limited as much as possible:
+each instance should justify itself by improving an actual error
+message.
+
+There is also a plan to write a really simple expression parser which
+doesn't do precedence and associativity, and the fix these with a pass
+over the ast. I don't think there is any other way to sanely handle
+the common prefixes between many infix and postfix multiple keyword
+operators, and some other ambiguities also. This should help a lot in
+generating good error messages also.
+
+Both the left factoring and error message work are greatly complicated
+by the large number of shared prefixes of the various elements in SQL
+syntax.
+
+== Main left factoring issues
+
+There are three big areas which are tricky to left factor:
+
+ * typenames
+ * scalar expressions which can start with an identifier
+ * infix and suffix operators
+
+=== typenames
+
+There are a number of variations of typename syntax. The standard
+deals with this by switching on the name of the type which is parsed
+first. This code doesn't do this currently, but might in the
+future. Taking the approach in the standard grammar will limit the
+extensibility of the parser and might affect the ease of adapting to
+support other sql dialects.
+
+=== identifier scalar expressions
+
+There are a lot of scalar expression nodes which start with
+identifiers, and can't be distinguished the tokens after the initial
+identifier are parsed. Using try to implement these variations is very
+simple but makes the code much harder to debug and makes the parser
+error messages really bad.
+
+Here is a list of these nodes:
+
+ * identifiers
+ * function application
+ * aggregate application
+ * window application
+ * typed literal: typename 'literal string'
+ * interval literal which is like the typed literal with some extras
+
+There is further ambiguity e.g. with typed literals with precision,
+functions, aggregates, etc. - these are an identifier, followed by
+parens comma separated scalar expressions or something similar, and it
+is only later that we can find a token which tells us which flavour it
+is.
+
+There is also a set of nodes which start with an identifier/keyword
+but can commit since no other syntax can start the same way:
+
+ * case
+ * cast
+ * exists, unique subquery
+ * array constructor
+ * multiset constructor
+ * all the special syntax functions: extract, position, substring,
+  convert, translate, overlay, trim, etc.
+
+The interval literal mentioned above is treated in this group at the
+moment: if we see 'interval' we parse it either as a full interval
+literal or a typed literal only.
+
+Some items in this list might have to be fixed in the future, e.g. to
+support standard 'substring(a from 3 for 5)' as well as regular
+function substring syntax 'substring(a,3,5) at the same time.
+
+The work in left factoring all this is mostly done, but there is still
+a substantial bit to complete and this is by far the most difficult
+bit. At the moment, the work around is to use try, the downsides of
+which is the poor parsing error messages.
+
+=== infix and suffix operators
+
+== permissiveness
+
+The parser is very permissive in many ways. This departs from the
+standard which is able to eliminate a number of possibilities just in
+the grammar, which this parser allows. This is done for a number of
+reasons:
+
+ * it makes the parser simple - less variations
+ * it should allow for dialects and extensibility more easily in the
+  future (e.g. new infix binary operators with custom precedence)
+ * many things which are effectively checked in the grammar in the
+  standard, can be checked using a typechecker or other simple static
+  analysis
+
+To use this code as a front end for a sql engine, or as a sql validity
+checker, you will need to do a lot of checks on the ast. A
+typechecker/static checker plus annotation to support being a compiler
+front end is planned but not likely to happen too soon.
+
+Some of the areas this affects:
+
+typenames: the variation of the type name should switch on the actual
+name given according to the standard, but this code only does this for
+the special case of interval type names. E.g. you can write 'int
+collate C' or 'int(15,2)' and this will parse as a character type name
+or a precision scale type name instead of being rejected.
+
+scalar expressions: every variation on scalar expressions uses the same
+parser/syntax. This means we don't try to stop non boolean valued
+expressions in boolean valued contexts in the parser. Another area
+this affects is that we allow general scalar expressions in group by,
+whereas the standard only allows column names with optional collation.
+
+These are all areas which are specified (roughly speaking) in the
+syntax rather than the semantics in the standard, and we are not
+fixing them in the syntax but leaving them till the semantic checking
+(which doesn't exist in this code at this time).
+-}
+
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE OverloadedStrings #-}
+-- | This is the module with the parser functions.
+module Language.SQL.SimpleSQL.Parse
+    (parseQueryExpr
+    ,parseScalarExpr
+    ,parseStatement
+    ,parseStatements
+    ,ParseError(..)
+    ,prettyError
+    ,ansi2011
+    ) where
+
+import Text.Megaparsec
+    (ParsecT
+    ,runParserT
+
+    ,ParseErrorBundle(..)
+    ,errorBundlePretty
+
+    ,(<?>)
+    ,(<|>)
+    ,token
+    ,choice
+    ,eof
+    ,try
+    ,sepBy
+    ,sepBy1
+    ,optional
+    ,option
+    ,some
+    ,many
+    ,between
+    )
+import qualified Control.Monad.Combinators.Expr as E
+import qualified Control.Monad.Permutations as P
+
+import Control.Monad.Reader
+    (Reader
+    ,runReader
+    ,ask
+    ,asks
+    )
+
+import qualified Data.Set           as Set
+import Data.Void (Void)
+
+import Control.Monad (guard, void)
+import Control.Applicative ((<**>))
+import Data.Char (isDigit)
+import Data.List (sort,groupBy)
+import Data.Function (on)
+import Data.Maybe (catMaybes, isJust, mapMaybe)
+import Data.Text (Text)
+import qualified Data.Text as T
+
+import Language.SQL.SimpleSQL.Syntax 
+import Language.SQL.SimpleSQL.Dialect
+import qualified Language.SQL.SimpleSQL.Lex as L
+
+------------------------------------------------------------------------------
+
+-- = Public API
+
+-- | Parses a query expr, trailing semicolon optional.
+parseQueryExpr
+    :: Dialect
+    -- ^ dialect of SQL to use
+    -> Text
+    -- ^ filename to use in error messages
+    -> Maybe (Int,Int)
+    -- ^ line number and column number of the first character
+    -- in the source to use in error messages
+    -> Text
+    -- ^ the SQL source to parse
+    -> Either ParseError QueryExpr
+parseQueryExpr = wrapParse topLevelQueryExpr
+
+-- | Parses a statement, trailing semicolon optional.
+parseStatement
+    :: Dialect
+    -- ^ dialect of SQL to use
+    -> Text
+    -- ^ filename to use in error messages
+    -> Maybe (Int,Int)
+    -- ^ line number and column number of the first character
+    -- in the source to use in error messages
+    -> Text
+    -- ^ the SQL source to parse
+    -> Either ParseError Statement
+parseStatement = wrapParse topLevelStatement
+
+-- | Parses a list of statements, with semi colons between
+-- them. The final semicolon is optional.
+parseStatements
+    :: Dialect
+    -- ^ dialect of SQL to use
+    -> Text
+    -- ^ filename to use in error messages
+    -> Maybe (Int,Int)
+    -- ^ line number and column number of the first character
+    -- in the source to use in error messages
+    -> Text
+    -- ^ the SQL source to parse
+    -> Either ParseError [Statement]
+parseStatements = wrapParse statements
+
+-- | Parses a scalar expression.
+parseScalarExpr
+    :: Dialect
+    -- ^ dialect of SQL to use
+    -> Text
+    -- ^ filename to use in error messages
+    -> Maybe (Int,Int)
+    -- ^ line number and column number of the first character
+    -- in the source to use in error messages
+    -> Text
+    -- ^ the SQL source to parse
+    -> Either ParseError ScalarExpr
+parseScalarExpr = wrapParse scalarExpr
+
+-- Megaparsec is too clever, so have to create a new type to represent
+-- either a lex error or a parse error
+
+data ParseError
+    = LexError L.ParseError
+    | ParseError (ParseErrorBundle L.SQLStream Void)
+
+prettyError :: ParseError -> Text
+prettyError (LexError e) = T.pack $ errorBundlePretty e
+prettyError (ParseError e) = T.pack $ errorBundlePretty e
+
+{-
+This helper function takes the parser given and:
+
+sets the position when parsing
+automatically skips leading whitespace
+checks the parser parses all the input using eof
+converts the error return to the nice wrapper
+-}
+
+wrapParse :: Parser a
+          -> Dialect
+          -> Text
+          -> Maybe (Int,Int)
+          -> Text
+          -> Either ParseError a
+wrapParse parser d f p src = do
+    lx <- either (Left . LexError) Right $ L.lexSQLWithPositions d f p src
+    either (Left . ParseError) Right $ 
+        runReader (runParserT (parser <* (eof <?> "")) (T.unpack f)
+                   $ L.SQLStream (T.unpack src) $ filter notSpace lx) d
+  where
+    notSpace = notSpace' . L.tokenVal
+    notSpace' (L.Whitespace {}) = False
+    notSpace' (L.LineComment {}) = False
+    notSpace' (L.BlockComment {}) = False
+    notSpace' _ = True
+
+------------------------------------------------------------------------------
+
+-- parsing code
+
+type Parser = ParsecT Void L.SQLStream (Reader Dialect)
+
+{-
+------------------------------------------------
+
+= Names
+
+Names represent identifiers and a few other things. The parser here
+handles regular identifiers, dotten chain identifiers, quoted
+identifiers and unicode quoted identifiers.
+
+Dots: dots in identifier chains are parsed here and represented in the
+Iden constructor usually. If parts of the chains are non identifier
+scalar expressions, then this is represented by a BinOp "."
+instead. Dotten chain identifiers which appear in other contexts (such
+as function names, table names, are represented as [Name] only.
+
+Identifier grammar:
+
+unquoted:
+underscore <|> letter : many (underscore <|> alphanum
+
+example
+_example123
+
+quoted:
+
+double quote, many (non quote character or two double quotes
+together), double quote
+
+"example quoted"
+"example with "" quote"
+
+unicode quoted is the same as quoted in this parser, except it starts
+with U& or u&
+
+u&"example quoted"
+-}
+
+name :: Parser Name
+name = do
+    bl <- askDialect diKeywords
+    uncurry Name <$> identifierTok bl
+
+-- todo: replace (:[]) with a named function all over
+
+names :: Parser [Name]
+names = reverse <$> (((:[]) <$> name) <??*> anotherName)
+  -- can't use a simple chain here since we
+  -- want to wrap the . + name in a try
+  -- this will change when this is left factored
+  where
+    anotherName :: Parser ([Name] -> [Name])
+    anotherName = try ((:) <$> ((symbol "." *> name) <?> ""))
+
+{-
+= Type Names
+
+Typenames are used in casts, and also in the typed literal syntax,
+which is a typename followed by a string literal.
+
+Here are the grammar notes:
+
+== simple type name
+
+just an identifier chain or a multi word identifier (this is a fixed
+list of possibilities, e.g. as 'character varying', see below in the
+parser code for the exact list).
+
+<simple-type-name> ::= <identifier-chain>
+     | multiword-type-identifier
+
+== Precision type name
+
+<precision-type-name> ::= <simple-type-name> <left paren> <unsigned-int> <right paren>
+
+e.g. char(5)
+
+note: above and below every where a simple type name can appear, this
+means a single identifier/quoted or a dotted chain, or a multi word
+identifier
+
+== Precision scale type name
+
+<precision-type-name> ::= <simple-type-name> <left paren> <unsigned-int> <comma> <unsigned-int> <right paren>
+
+e.g. decimal(15,2)
+
+== Lob type name
+
+this is a variation on the precision type name with some extra info on
+the units:
+
+<lob-type-name> ::=
+   <simple-type-name> <left paren> <unsigned integer> [ <multiplier> ] [ <char length units> ] <right paren>
+
+<multiplier>    ::=   K | M | G
+<char length units>    ::=   CHARACTERS | CODE_UNITS | OCTETS
+
+(if both multiplier and char length units are missing, then this will
+parse as a precision type name)
+
+e.g.
+clob(5M octets)
+
+== char type name
+
+this is a simple type with optional precision which allows the
+character set or the collation to appear as a suffix:
+
+<char type name> ::=
+    <simple type name>
+    [ <left paren> <unsigned-int> <right paren> ]
+    [ CHARACTER SET <identifier chain> ]
+    [ COLLATE <identifier chain> ]
+
+e.g.
+
+char(5) character set my_charset collate my_collation
+
+= Time typename
+
+this is typename with optional precision and either 'with time zone'
+or 'without time zone' suffix, e.g.:
+
+<datetime type> ::=
+    [ <left paren> <unsigned-int> <right paren> ]
+    <with or without time zone>
+<with or without time zone> ::= WITH TIME ZONE | WITHOUT TIME ZONE
+    WITH TIME ZONE | WITHOUT TIME ZONE
+
+= row type name
+
+<row type> ::=
+    ROW <left paren> <field definition> [ { <comma> <field definition> }... ] <right paren>
+
+<field definition> ::= <identifier> <type name>
+
+e.g.
+row(a int, b char(5))
+
+= interval type name
+
+<interval type> ::= INTERVAL <interval datetime field> [TO <interval datetime field>]
+
+<interval datetime field> ::=
+  <datetime field> [ <left paren> <unsigned int> [ <comma> <unsigned int> ] <right paren> ]
+
+= array type name
+
+<array type> ::= <data type> ARRAY [ <left bracket> <unsigned integer> <right bracket> ]
+
+= multiset type name
+
+<multiset type>    ::=   <data type> MULTISET
+
+A type name will parse into the 'smallest' constructor it will fit in
+syntactically, e.g. a clob(5) will parse to a precision type name, not
+a lob type name.
+
+Unfortunately, to improve the error messages, there is a lot of (left)
+factoring in this function, and it is a little dense.
+-}
+
+typeName :: Parser TypeName
+typeName =
+    (rowTypeName <|> intervalTypeName <|> otherTypeName)
+    <??*> tnSuffix
+  where
+    rowTypeName =
+        RowTypeName <$> (keyword_ "row" *> parens (commaSep1 rowField))
+    rowField = (,) <$> name <*> typeName
+    ----------------------------
+    intervalTypeName =
+        keyword_ "interval" *>
+        (uncurry IntervalTypeName <$> intervalQualifier)
+    ----------------------------
+    otherTypeName =
+        nameOfType <**>
+            (typeNameWithParens
+             <|> pure Nothing <**> (timeTypeName <|> charTypeName)
+             <|> pure TypeName)
+    nameOfType = reservedTypeNames <|> names
+    charTypeName = charSet <**> (option [] tcollate <$$$$> CharTypeName)
+                   <|> pure [] <**> (tcollate <$$$$> CharTypeName)
+    typeNameWithParens =
+        (openParen *> unsignedInteger)
+        <**> (closeParen *> precMaybeSuffix
+              <|> (precScaleTypeName <|> precLengthTypeName) <* closeParen)
+    precMaybeSuffix = (. Just) <$> (timeTypeName <|> charTypeName)
+                      <|> pure (flip PrecTypeName)
+    precScaleTypeName = (comma *> unsignedInteger) <$$$> PrecScaleTypeName
+    precLengthTypeName =
+        Just <$> lobPrecSuffix
+        <**> (optional lobUnits <$$$$> PrecLengthTypeName)
+        <|> pure Nothing <**> ((Just <$> lobUnits) <$$$$> PrecLengthTypeName)
+    timeTypeName = tz <$$$> TimeTypeName
+    ----------------------------
+    lobPrecSuffix = PrecK <$ keyword_ "k"
+                    <|> PrecM <$ keyword_ "m"
+                    <|> PrecG <$ keyword_ "g"
+                    <|> PrecT <$ keyword_ "t"
+                    <|> PrecP <$ keyword_ "p"
+    lobUnits = PrecCharacters <$ keyword_ "characters"
+               -- char and byte are the oracle spelling
+               -- todo: move these to oracle dialect
+               <|> PrecCharacters <$ keyword_ "char"
+               <|> PrecOctets <$ keyword_ "octets"
+               <|> PrecOctets <$ keyword_ "byte"
+    tz = True <$ keywords_ ["with", "time","zone"]
+         <|> False <$ keywords_ ["without", "time","zone"]
+    charSet = keywords_ ["character", "set"] *> names
+    tcollate = keyword_ "collate" *> names
+    ----------------------------
+    tnSuffix = multiset <|> array
+    multiset = MultisetTypeName <$ keyword_ "multiset"
+    array = keyword_ "array" *>
+        (optional (brackets unsignedInteger) <$$> ArrayTypeName)
+    ----------------------------
+    -- this parser handles the fixed set of multi word
+    -- type names, plus all the type names which are
+    -- reserved words
+    reservedTypeNames = do
+        stn <- askDialect diSpecialTypeNames
+        (:[]) . Name Nothing . T.unwords <$> makeKeywordTree stn
+        
+
+{-
+= Scalar expressions
+
+== simple literals
+
+See the stringToken lexer below for notes on string literal syntax.
+-}
+
+stringLit :: Parser ScalarExpr
+stringLit = (\(s,e,t) -> StringLit s e t) <$> stringTokExtend
+
+numberLit :: Parser ScalarExpr
+numberLit = NumLit <$> sqlNumberTok False
+
+simpleLiteral :: Parser ScalarExpr
+simpleLiteral = numberLit <|> stringLit
+
+{-
+== star, param, host param
+
+=== star
+
+used in select *, select x.*, and agg(*) variations, and some other
+places as well. The parser doesn't attempt to check that the star is
+in a valid context, it parses it OK in any scalar expression context.
+-}
+
+star :: Parser ScalarExpr
+star = Star <$ symbol "*"
+
+{-
+== parameter
+
+unnamed parameter or named parameter
+use in e.g. select * from t where a = ?
+select x from t where x > :param
+-}
+
+parameter :: Parser ScalarExpr
+parameter = choice
+    [Parameter <$ questionMark
+    ,HostParameter
+     <$> hostParamTok
+     <*> optional (keyword "indicator" *> hostParamTok)]
+
+-- == positional arg
+
+positionalArg :: Parser ScalarExpr
+positionalArg = PositionalArg <$> positionalArgTok
+
+{-
+== parens
+
+scalar expression parens, row ctor and scalar subquery
+-}
+
+parensExpr :: Parser ScalarExpr
+parensExpr = parens $ choice
+    [SubQueryExpr SqSq <$> queryExpr
+    ,ctor <$> commaSep1 scalarExpr]
+  where
+    ctor [a] = Parens a
+    ctor as = SpecialOp [Name Nothing "rowctor"] as
+
+{-
+== case, cast, exists, unique, array/multiset constructor, interval
+
+All of these start with a fixed keyword which is reserved, so no other
+syntax can start with the same keyword.
+
+=== case expression
+-}
+
+caseExpr :: Parser ScalarExpr
+caseExpr =
+    Case <$> (keyword_ "case" *> optional scalarExpr)
+         <*> some whenClause
+         <*> optional elseClause
+         <* keyword_ "end"
+  where
+   whenClause = (,) <$> (keyword_ "when" *> commaSep1 scalarExpr)
+                    <*> (keyword_ "then" *> scalarExpr)
+   elseClause = keyword_ "else" *> scalarExpr
+
+{-
+=== cast
+
+cast: cast(expr as type)
+-}
+
+cast :: Parser ScalarExpr
+cast = keyword_ "cast" *>
+       parens (Cast <$> scalarExpr
+                    <*> (keyword_ "as" *> typeName))
+
+{-
+=== convert
+
+convertSqlServer: SqlServer dialect CONVERT(data_type(length), expression, style)
+-}
+
+convertSqlServer :: Parser ScalarExpr
+convertSqlServer = guardDialect diConvertFunction
+                   *> keyword_ "convert" *>
+                   parens (Convert <$> typeName <*> (comma *> scalarExpr)
+                      <*> optional (comma *> unsignedInteger))
+
+{-
+=== exists, unique
+
+subquery expression:
+[exists|unique] (queryexpr)
+-}
+
+subquery :: Parser ScalarExpr
+subquery = SubQueryExpr <$> sqkw <*> parens queryExpr
+  where
+    sqkw = SqExists <$ keyword_ "exists" <|> SqUnique <$ keyword_ "unique"
+
+-- === array/multiset constructor
+
+arrayCtor :: Parser ScalarExpr
+arrayCtor = keyword_ "array" >>
+    choice
+    [ArrayCtor <$> parens queryExpr
+    ,Array (Iden [Name Nothing "array"]) <$> brackets (commaSep scalarExpr)]
+
+{-
+As far as I can tell, table(query expr) is just syntax sugar for
+multiset(query expr). It must be there for compatibility or something.
+-}
+
+multisetCtor :: Parser ScalarExpr
+multisetCtor =
+    choice
+    [keyword_ "multiset" >>
+     choice
+     [MultisetQueryCtor <$> parens queryExpr
+     ,MultisetCtor <$> brackets (commaSep scalarExpr)]
+    ,keyword_ "table" >>
+     MultisetQueryCtor <$> parens queryExpr]
+
+nextValueFor :: Parser ScalarExpr
+nextValueFor = keywords_ ["next","value","for"] >>
+    NextValueFor <$> names
+
+{-
+=== interval
+
+interval literals are a special case and we follow the grammar less
+permissively here
+
+parse SQL interval literals, something like
+interval '5' day (3)
+or
+interval '5' month
+
+if the literal looks like this:
+interval 'something'
+
+then it is parsed as a regular typed literal. It must have a
+interval-datetime-field suffix to parse as an intervallit
+
+It uses try because of a conflict with interval type names: todo, fix
+this. also fix the monad -> applicative
+-}
+
+intervalLit :: Parser ScalarExpr
+intervalLit = try (keyword_ "interval" >> do
+    s <- optional $ choice [Plus <$ symbol_ "+"
+                              ,Minus <$ symbol_ "-"]
+    lit <- singleQuotesOnlyStringTok
+    q <- optional intervalQualifier
+    mkIt s lit q)
+  where
+    mkIt Nothing val Nothing = pure $ TypedLit (TypeName [Name Nothing "interval"]) val
+    mkIt s val (Just (a,b)) = pure $ IntervalLit s val a b
+    mkIt (Just {}) _val Nothing = fail "cannot use sign without interval qualifier"
+
+{-
+== typed literal, app, special, aggregate, window, iden
+
+All of these start with identifiers (some of the special functions
+start with reserved keywords).
+
+they are all variations on suffixes on the basic identifier parser
+
+The windows is a suffix on the app parser
+
+=== iden prefix term
+
+all the scalar expressions which start with an identifier
+
+(todo: really put all of them here instead of just some of them)
+-}
+
+idenExpr :: Parser ScalarExpr
+idenExpr =
+    -- todo: work out how to left factor this
+    try (TypedLit <$> typeName <*> singleQuotesOnlyStringTok)
+    <|> (names <**> option Iden app)
+    <|> keywordFunctionOrIden
+  where
+    -- special cases for keywords that can be parsed as an iden or app
+    keywordFunctionOrIden = try $ do
+        x <- unquotedIdentifierTok [] Nothing
+        d <- askDialect id
+        let i = T.toLower x `elem` diIdentifierKeywords d
+            a = T.toLower x `elem` diAppKeywords d
+        case () of
+            _  | i && a -> pure [Name Nothing x] <**> option Iden app
+               | i -> pure (Iden [Name Nothing x])
+               | a -> pure [Name Nothing x] <**> app
+               | otherwise -> fail ""
+
+
+{-
+=== special
+
+These are keyword operators which don't look like normal prefix,
+postfix or infix binary operators. They mostly look like function
+application but with keywords in the argument list instead of commas
+to separate the arguments.
+
+the special op keywords
+parse an operator which is
+operatorname(firstArg keyword0 arg0 keyword1 arg1 etc.)
+-}
+
+data SpecialOpKFirstArg = SOKNone
+                        | SOKOptional
+                        | SOKMandatory
+
+specialOpK :: Text -- name of the operator
+           -> SpecialOpKFirstArg -- has a first arg without a keyword
+           -> [(Text,Bool)] -- the other args with their keywords
+                              -- and whether they are optional
+           -> Parser ScalarExpr
+specialOpK opName firstArg kws =
+    keyword_ opName >> do
+    void openParen
+    let pfa = do
+              e <- scalarExpr
+              -- check we haven't parsed the first
+              -- keyword as an identifier
+              case (e,kws) of
+                  (Iden [Name Nothing i], (k,_):_)
+                      | T.toLower i == k ->
+                          fail $ "cannot use keyword here: " ++ T.unpack i
+                  _ -> pure ()
+              pure e
+    fa <- case firstArg of
+         SOKNone -> pure Nothing
+         SOKOptional -> optional (try pfa)
+         SOKMandatory -> Just <$> pfa
+    as <- mapM parseArg kws
+    void closeParen
+    pure $ SpecialOpK [Name Nothing opName] fa $ catMaybes as
+  where
+    parseArg (nm,mand) =
+        let p = keyword_ nm >> scalarExpr
+        in fmap (nm,) <$> if mand
+                          then Just <$> p
+                          else optional (try p)
+
+{-
+The actual operators:
+
+EXTRACT( date_part FROM expression )
+
+POSITION( string1 IN string2 )
+
+SUBSTRING(extraction_string FROM starting_position [FOR length]
+[COLLATE collation_name])
+
+CONVERT(char_value USING conversion_char_name)
+
+TRANSLATE(char_value USING translation_name)
+
+OVERLAY(string PLACING embedded_string FROM start
+[FOR length])
+
+TRIM( [ [{LEADING | TRAILING | BOTH}] [removal_char] FROM ]
+target_string
+[COLLATE collation_name] )
+-}
+
+specialOpKs :: Parser ScalarExpr
+specialOpKs = choice $ map try
+    [extract, position, substring, convert, translate, overlay, trim]
+
+extract :: Parser ScalarExpr
+extract = specialOpK "extract" SOKMandatory [("from", True)]
+
+position :: Parser ScalarExpr
+position = specialOpK "position" SOKMandatory [("in", True)]
+
+{-
+strictly speaking, the substring must have at least one of from and
+for, but the parser doens't enforce this
+-}
+
+substring :: Parser ScalarExpr
+substring = specialOpK "substring" SOKMandatory
+                [("from", False),("for", False)]
+
+convert :: Parser ScalarExpr
+convert = specialOpK "convert" SOKMandatory [("using", True)]
+
+
+translate :: Parser ScalarExpr
+translate = specialOpK "translate" SOKMandatory [("using", True)]
+
+overlay :: Parser ScalarExpr
+overlay = specialOpK "overlay" SOKMandatory
+                [("placing", True),("from", True),("for", False)]
+
+{-
+trim is too different because of the optional char, so a custom parser
+the both ' ' is filled in as the default if either parts are missing
+in the source
+-}
+
+trim :: Parser ScalarExpr
+trim =
+    keyword "trim" >>
+    parens (mkTrim
+            <$> option "both" sides
+            <*> option " " singleQuotesOnlyStringTok
+            <*> (keyword_ "from" *> scalarExpr))
+  where
+    sides = choice ["leading" <$ keyword_ "leading"
+                   ,"trailing" <$ keyword_ "trailing"
+                   ,"both" <$ keyword_ "both"]
+    mkTrim fa ch fr =
+      SpecialOpK [Name Nothing "trim"] Nothing
+          $ catMaybes [Just (fa,StringLit "'" "'" ch)
+                      ,Just ("from", fr)]
+
+{-
+=== app, aggregate, window
+
+This parses all these variations:
+normal function application with just a csv of scalar exprs
+aggregate variations (distinct, order by in parens, filter and where
+  suffixes)
+window apps (fn/agg followed by over)
+
+This code is also a little dense like the typename code because of
+left factoring, later they will even have to be partially combined
+together.
+-}
+
+app :: Parser ([Name] -> ScalarExpr)
+app =
+    openParen *> choice
+    [duplicates
+     <**> (commaSep1 scalarExpr
+           <**> ((option [] orderBy <* closeParen)
+                 <**> (optional afilter <$$$$$> AggregateApp)))
+     -- separate cases with no all or distinct which must have at
+     -- least one scalar expr
+    ,commaSep1 scalarExpr
+     <**> choice
+          [closeParen *> choice
+                         [window
+                         ,withinGroup
+                         ,(Just <$> afilter) <$$$> aggAppWithoutDupeOrd
+                         ,pure (flip App)]
+          ,orderBy <* closeParen
+           <**> (optional afilter <$$$$> aggAppWithoutDupe)]
+     -- no scalarExprs: duplicates and order by not allowed
+    ,([] <$ closeParen) <**> option (flip App) (window <|> withinGroup)
+    ]
+  where
+    aggAppWithoutDupeOrd n es f = AggregateApp n SQDefault es [] f
+    aggAppWithoutDupe n = AggregateApp n SQDefault
+
+afilter :: Parser ScalarExpr
+afilter = keyword_ "filter" *> parens (keyword_ "where" *> scalarExpr)
+
+withinGroup :: Parser ([ScalarExpr] -> [Name] -> ScalarExpr)
+withinGroup =
+    (keywords_ ["within", "group"] *> parens orderBy) <$$$> AggregateAppGroup
+
+{-
+==== window
+
+parse a window call as a suffix of a regular function call
+this looks like this:
+functionname(args) over ([partition by ids] [order by orderitems])
+
+No support for explicit frames yet.
+
+TODO: add window support for other aggregate variations, needs some
+changes to the syntax also
+-}
+
+window :: Parser ([ScalarExpr] -> [Name] -> ScalarExpr)
+window =
+  keyword_ "over" *> openParen *> option [] partitionBy
+  <**> (option [] orderBy
+        <**> ((optional frameClause <* closeParen) <$$$$$> WindowApp))
+  where
+    partitionBy = keywords_ ["partition","by"] *> commaSep1 scalarExpr
+    frameClause =
+        frameRowsRange -- TODO: this 'and' could be an issue
+        <**> choice [(keyword_ "between" *> frameLimit True)
+                      <**> ((keyword_ "and" *> frameLimit True)
+                            <$$$> FrameBetween)
+                      -- maybe this should still use a b expression
+                      -- for consistency
+                     ,frameLimit False <**> pure (flip FrameFrom)]
+    frameRowsRange = FrameRows <$ keyword_ "rows"
+                     <|> FrameRange <$ keyword_ "range"
+    frameLimit useB =
+        choice
+        [Current <$ keywords_ ["current", "row"]
+         -- todo: create an automatic left factor for stuff like this
+        ,keyword_ "unbounded" *>
+         choice [UnboundedPreceding <$ keyword_ "preceding"
+                ,UnboundedFollowing <$ keyword_ "following"]
+        ,(if useB then scalarExprB else scalarExpr)
+         <**> (Preceding <$ keyword_ "preceding"
+               <|> Following <$ keyword_ "following")
+        ]
+
+{-
+== suffixes
+
+These are all generic suffixes on any scalar expr
+
+=== in
+
+in: two variations:
+a in (expr0, expr1, ...)
+a in (queryexpr)
+-}
+
+inSuffix :: Parser (ScalarExpr -> ScalarExpr)
+inSuffix =
+    mkIn <$> inty
+         <*> parens (choice
+                     [InQueryExpr <$> queryExpr
+                     ,InList <$> commaSep1 scalarExpr])
+  where
+    inty = choice [True <$ keyword_ "in"
+                  ,False <$ keywords_ ["not","in"]]
+    mkIn i v e = In i e v
+
+{-
+=== between
+
+between:
+expr between expr and expr
+
+There is a complication when parsing between - when parsing the second
+expression it is ambiguous when you hit an 'and' whether it is a
+binary operator or part of the between. This code follows what
+postgres does, which might be standard across SQL implementations,
+which is that you can't have a binary and operator in the middle
+expression in a between unless it is wrapped in parens. The 'bExpr
+parsing' is used to create alternative scalar expression parser which
+is identical to the normal one expect it doesn't recognise the binary
+and operator. This is the call to scalarExprB.
+-}
+
+betweenSuffix :: Parser (ScalarExpr -> ScalarExpr)
+betweenSuffix =
+    makeOp . Name Nothing
+           <$> opName
+           <*> scalarExprB
+           <*> (keyword_ "and" *> scalarExprB)
+  where
+    opName = choice
+             ["between" <$ keyword_ "between"
+             ,"not between" <$ try (keywords_ ["not","between"])]
+    makeOp n b c a = SpecialOp [n] [a,b,c]
+
+{-
+=== quantified comparison
+
+a = any (select * from t)
+-}
+
+quantifiedComparisonSuffix :: Parser (ScalarExpr -> ScalarExpr)
+quantifiedComparisonSuffix = do
+    c <- comp
+    cq <- compQuan
+    q <- parens queryExpr
+    pure $ \v -> QuantifiedComparison v [c] cq q
+  where
+    comp = Name Nothing <$> choice (map symbol
+           ["=", "<>", "<=", "<", ">", ">="])
+    compQuan = choice
+               [CPAny <$ keyword_ "any"
+               ,CPSome <$ keyword_ "some"
+               ,CPAll <$ keyword_ "all"]
+
+{-
+=== match
+
+a match (select a from t)
+-}
+
+matchPredicateSuffix :: Parser (ScalarExpr -> ScalarExpr)
+matchPredicateSuffix = do
+    keyword_ "match"
+    u <- option False (True <$ keyword_ "unique")
+    q <- parens queryExpr
+    pure $ \v -> Match v u q
+
+-- === array subscript
+
+arraySuffix :: Parser (ScalarExpr -> ScalarExpr)
+arraySuffix = do
+    es <- brackets (commaSep scalarExpr)
+    pure $ \v -> Array v es
+
+{-
+=== escape
+
+It is going to be really difficult to support an arbitrary character
+for the escape now there is a separate lexer ...
+
+TODO: this needs fixing. Escape is only part of other nodes, and not a
+separate suffix.
+-}
+
+{-escapeSuffix :: Parser (ScalarExpr -> ScalarExpr)
+escapeSuffix = do
+    ctor <- choice
+            [Escape <$ keyword_ "escape"
+            ,UEscape <$ keyword_ "uescape"]
+    c <- escapeChar
+    pure $ \v -> ctor v c
+  where
+    escapeChar :: Parser Char
+    escapeChar = (identifierTok [] Nothing <|> symbolTok Nothing) >>= oneOnly
+    oneOnly :: String -> Parser Char
+    oneOnly c = case c of
+                   [c'] -> pure c'
+                   _ -> fail "escape char must be single char"
+-}
+
+-- === collate
+
+collateSuffix:: Parser (ScalarExpr -> ScalarExpr)
+collateSuffix = do
+    keyword_ "collate"
+    i <- names
+    pure $ \v -> Collate v i
+
+{-
+== odbc syntax
+
+the parser supports three kinds of odbc syntax, two of which are
+scalar expressions (the other is a variation on joins)
+-}
+
+
+odbcExpr :: Parser ScalarExpr
+odbcExpr = between (symbol "{") (symbol "}")
+           (odbcTimeLit <|> odbcFunc)
+  where
+    odbcTimeLit =
+        OdbcLiteral <$> choice [OLDate <$ keyword "d"
+                               ,OLTime <$ keyword "t"
+                               ,OLTimestamp <$ keyword "ts"]
+                    <*> singleQuotesOnlyStringTok
+    -- todo: this parser is too general, the expr part
+    -- should be only a function call (from a whitelist of functions)
+    -- or the extract operator
+    odbcFunc = OdbcFunc <$> (keyword "fn" *> scalarExpr)
+
+{-
+==  operators
+
+The 'regular' operators in this parsing and in the abstract syntax are
+unary prefix, unary postfix and binary infix operators. The operators
+can be symbols (a + b), single keywords (a and b) or multiple keywords
+(a is similar to b).
+
+TODO: carefully review the precedences and associativities.
+
+TODO: to fix the parsing completely, I think will need to parse
+without precedence and associativity and fix up afterwards, since SQL
+syntax is way too messy. It might be possible to avoid this if we
+wanted to avoid extensibility and to not be concerned with parse error
+messages, but both of these are too important.
+-}
+
+opTable :: Bool -> [[E.Operator Parser ScalarExpr]]
+opTable bExpr =
+        [-- parse match and quantified comparisons as postfix ops
+          -- todo: left factor the quantified comparison with regular
+          -- binary comparison, somehow
+         [postfix $ try quantifiedComparisonSuffix
+         ,postfix matchPredicateSuffix]
+
+        ,[binarySymL "."]
+
+        ,[postfix arraySuffix
+         ,postfix collateSuffix]
+
+        ,[prefixSym "+", prefixSym "-"]
+
+        ,[binarySymL "^"]
+
+        ,[binarySymL "*"
+         ,binarySymL "/"
+         ,binarySymL "%"]
+
+        ,[binarySymL "+"
+         ,binarySymL "-"]
+
+        ,[binarySymR "||"
+         ,prefixSym "~"
+         ,binarySymR "&"
+         ,binarySymR "|"]
+
+        ,[binaryKeywordN "overlaps"]
+
+        ,[binaryKeywordN "like"
+         -- have to use try with inSuffix because of a conflict
+         -- with 'in' in position function, and not between
+         -- between also has a try in it to deal with 'not'
+         -- ambiguity
+         ,postfix $ try inSuffix
+         ,postfix betweenSuffix]
+         -- todo: figure out where to put the try?
+         ++ [binaryKeywordsN $ makeKeywordTree
+             ["not like"
+             ,"is similar to"
+             ,"is not similar to"]]
+          ++ [multisetBinOp]
+
+        ,[binarySymN "<"
+         ,binarySymN ">"
+         ,binarySymN ">="
+         ,binarySymN "<="
+         ,binarySymR "!="
+         ,binarySymR "<>"
+         ,binarySymR "="]
+
+        ,[postfixKeywords $ makeKeywordTree
+             ["is null"
+             ,"is not null"
+             ,"is true"
+             ,"is not true"
+             ,"is false"
+             ,"is not false"
+             ,"is unknown"
+             ,"is not unknown"]]
+         ++ [binaryKeywordsN $ makeKeywordTree
+             ["is distinct from"
+             ,"is not distinct from"]]
+
+        ,[prefixKeyword "not"]
+
+        ,[binaryKeywordL "and" | not bExpr]
+
+        ,[binaryKeywordL "or"]
+
+       ]
+  where
+    binarySymL nm = E.InfixL (mkBinOp nm <$ symbol_ nm)
+    binarySymR nm = E.InfixR (mkBinOp nm <$ symbol_ nm)
+    binarySymN nm = E.InfixN (mkBinOp nm <$ symbol_ nm)
+    binaryKeywordN nm = E.InfixN (mkBinOp nm <$ keyword_ nm)
+    binaryKeywordL nm = E.InfixL (mkBinOp nm <$ keyword_ nm)
+    mkBinOp nm a b = BinOp a (mkNm nm) b
+    prefixSym  nm = prefix (PrefixOp (mkNm nm) <$ symbol_ nm)
+    prefixKeyword  nm = prefix (PrefixOp (mkNm nm) <$ keyword_ nm)
+    mkNm nm = [Name Nothing nm]
+    binaryKeywordsN p =
+        E.InfixN (do
+                 o <- try p
+                 pure (\a b -> BinOp a [Name Nothing $ T.unwords o] b))
+    multisetBinOp = E.InfixL (do
+        keyword_ "multiset"
+        o <- choice [Union <$ keyword_ "union"
+                    ,Intersect <$ keyword_ "intersect"
+                    ,Except <$ keyword_ "except"]
+        d <- option SQDefault duplicates
+        pure (\a b -> MultisetBinOp a o d b))
+    postfixKeywords p =
+      postfix $ do
+          o <- try p
+          pure $ PostfixOp [Name Nothing $ T.unwords o]
+    -- parse repeated prefix or postfix operators
+    postfix p = E.Postfix $ foldr1 (flip (.)) <$> some p
+    prefix p = E.Prefix $ foldr1 (.) <$> some p
+
+{-
+== scalar expression top level
+
+This parses most of the scalar exprs.The order of the parsers and use
+of try is carefully done to make everything work. It is a little
+fragile and could at least do with some heavy explanation. Update: the
+'try's have migrated into the individual parsers, they still need
+documenting/fixing.
+-}
+
+scalarExpr :: Parser ScalarExpr
+scalarExpr = E.makeExprParser term (opTable False)
+
+term :: Parser ScalarExpr
+term = choice [simpleLiteral
+              ,parameter
+              ,positionalArg
+              ,star
+              ,parensExpr
+              ,caseExpr
+              ,cast
+              ,convertSqlServer
+              ,arrayCtor
+              ,multisetCtor
+              ,nextValueFor
+              ,subquery
+              ,intervalLit
+              ,specialOpKs
+              ,idenExpr
+              ,odbcExpr]
+       <?> "scalar expression"
+
+-- expose the b expression for window frame clause range between
+
+scalarExprB :: Parser ScalarExpr
+scalarExprB = E.makeExprParser term (opTable True)
+
+{-
+== helper parsers
+
+This is used in interval literals and in interval type names.
+-}
+
+intervalQualifier :: Parser (IntervalTypeField,Maybe IntervalTypeField)
+intervalQualifier =
+    (,) <$> intervalField
+        <*> optional (keyword_ "to" *> intervalField)
+  where
+    intervalField =
+        Itf
+        <$> datetimeField
+        <*> optional
+            (parens ((,) <$> unsignedInteger
+                         <*> optional (comma *> unsignedInteger)))
+
+{-
+TODO: use datetime field in extract also
+use a data type for the datetime field?
+-}
+
+datetimeField :: Parser Text
+datetimeField = choice (map keyword ["year","month","day"
+                                    ,"hour","minute","second"])
+                <?> "datetime field"
+
+{-
+This is used in multiset operations (scalar expr), selects (query expr)
+and set operations (query expr).
+-}
+
+duplicates :: Parser SetQuantifier
+duplicates =
+    choice [All <$ keyword_ "all"
+           ,Distinct <$ keyword "distinct"]
+
+{-
+-------------------------------------------------
+
+= query expressions
+
+== select lists
+-}
+
+selectItem :: Parser (ScalarExpr,Maybe Name)
+selectItem = (,) <$> scalarExpr <*> optional als
+  where als = optional (keyword_ "as") *> name
+
+selectList :: Parser [(ScalarExpr,Maybe Name)]
+selectList = commaSep1 selectItem
+
+{-
+== from
+
+Here is the rough grammar for joins
+
+tref
+(cross | [natural] ([inner] | (left | right | full) [outer])) join
+tref
+[on expr | using (...)]
+
+TODO: either use explicit 'operator precedence' parsers or build
+expression parser for the 'tref operators' such as joins, lateral,
+aliases.
+-}
+
+from :: Parser [TableRef]
+from = keyword_ "from" *> commaSep1 tref
+  where
+    -- TODO: use P (a->) for the join tref suffix
+    -- chainl or buildexpressionparser
+    tref = (nonJoinTref <?> "table ref") >>= optionSuffix joinTrefSuffix
+    nonJoinTref = choice
+        [parens $ choice
+             [TRQueryExpr <$> queryExpr
+             ,TRParens <$> tref]
+        ,TRLateral <$> (keyword_ "lateral"
+                        *> nonJoinTref)
+        ,do
+         n <- names
+         choice [TRFunction n
+                 <$> parens (commaSep scalarExpr)
+                ,pure $ TRSimple n]
+         -- todo: I think you can only have outer joins inside the oj,
+         -- not sure.
+        ,TROdbc <$> (symbol "{" *> keyword_ "oj" *> tref <* symbol "}")
+        ] <??> aliasSuffix
+    aliasSuffix = fromAlias <$$> TRAlias
+    joinTrefSuffix t =
+        ((TRJoin t <$> option False (True <$ keyword_ "natural")
+                  <*> joinType
+                  <*> nonJoinTref
+                  <*> optional joinCondition)
+        >>= optionSuffix joinTrefSuffix) <?> ""
+
+{-
+TODO: factor the join stuff to produce better error messages (and make
+it more readable)
+-}
+
+joinType :: Parser JoinType
+joinType = choice
+    [JCross <$ keyword_ "cross" <* keyword_ "join"
+    ,JInner <$ keyword_ "inner" <* keyword_ "join"
+    ,JLeft <$ keyword_ "left"
+           <* optional (keyword_ "outer")
+           <* keyword_ "join"
+    ,JRight <$ keyword_ "right"
+            <* optional (keyword_ "outer")
+            <* keyword_ "join"
+    ,JFull <$ keyword_ "full"
+           <* optional (keyword_ "outer")
+           <* keyword_ "join"
+    ,JInner <$ keyword_ "join"]
+
+joinCondition :: Parser JoinCondition
+joinCondition = choice
+    [keyword_ "on" >> JoinOn <$> scalarExpr
+    ,keyword_ "using" >> JoinUsing <$> parens (commaSep1 name)]
+
+fromAlias :: Parser Alias
+fromAlias = Alias <$> tableAlias <*> columnAliases
+  where
+    tableAlias = optional (keyword_ "as") *> name
+    columnAliases = optional $ parens $ commaSep1 name
+
+{-
+== simple other parts
+
+Parsers for where, group by, having, order by and limit, which are
+pretty trivial.
+-}
+
+whereClause :: Parser ScalarExpr
+whereClause = keyword_ "where" *> scalarExpr
+
+groupByClause :: Parser [GroupingExpr]
+groupByClause = keywords_ ["group","by"] *> commaSep1 groupingExpression
+  where
+    groupingExpression = choice
+      [keyword_ "cube" >>
+       Cube <$> parens (commaSep groupingExpression)
+      ,keyword_ "rollup" >>
+       Rollup <$> parens (commaSep groupingExpression)
+      ,GroupingParens <$> parens (commaSep groupingExpression)
+      ,keywords_ ["grouping", "sets"] >>
+       GroupingSets <$> parens (commaSep groupingExpression)
+      ,SimpleGroup <$> scalarExpr
+      ]
+
+having :: Parser ScalarExpr
+having = keyword_ "having" *> scalarExpr
+
+orderBy :: Parser [SortSpec]
+orderBy = keywords_ ["order","by"] *> commaSep1 ob
+  where
+    ob = SortSpec
+         <$> scalarExpr
+         <*> option DirDefault (choice [Asc <$ keyword_ "asc"
+                                       ,Desc <$ keyword_ "desc"])
+         <*> option NullsOrderDefault
+             -- todo: left factor better
+             (keyword_ "nulls" >>
+                    choice [NullsFirst <$ keyword "first"
+                           ,NullsLast <$ keyword "last"])
+
+{-
+allows offset and fetch in either order
++ postgresql offset without row(s) and limit instead of fetch also
+-}
+
+offsetFetch :: Parser (Maybe ScalarExpr, Maybe ScalarExpr)
+offsetFetch =
+    P.runPermutation $ (,) <$> maybePermutation offset <*> maybePermutation fetch
+  where
+    maybePermutation p = P.toPermutationWithDefault Nothing (Just <$> p)
+
+offset :: Parser ScalarExpr
+offset = keyword_ "offset" *> scalarExpr
+         <* option () (choice [keyword_ "rows"
+                              ,keyword_ "row"])
+
+fetch :: Parser ScalarExpr
+fetch = fetchFirst <|> limit
+  where
+    fetchFirst = guardDialect diFetchFirst
+                 *> fs *> scalarExpr <* ro
+    fs = makeKeywordTree ["fetch first", "fetch next"]
+    ro = makeKeywordTree ["rows only", "row only"]
+    -- todo: not in ansi sql dialect
+    limit = guardDialect diLimit *>
+            keyword_ "limit" *> scalarExpr
+
+-- == common table expressions
+
+with :: Parser QueryExpr
+with = keyword_ "with" >>
+    With <$> option False (True <$ keyword_ "recursive")
+         <*> commaSep1 withQuery <*> queryExpr
+  where
+    withQuery = (,) <$> (withAlias <* keyword_ "as")
+                    <*> parens queryExpr
+    withAlias = Alias <$> name <*> columnAliases
+    columnAliases = optional $ parens $ commaSep1 name
+
+
+{-
+== query expression
+
+This parser parses any query expression variant: normal select, cte,
+and union, etc..
+-}
+
+queryExpr :: Parser QueryExpr
+queryExpr = E.makeExprParser qeterm qeOpTable
+  where
+    qeterm = with <|> select <|> table <|> values
+    
+    select = keyword_ "select" >>
+        mkSelect
+        <$> option SQDefault duplicates
+        <*> selectList
+        <*> optional tableExpression <?> "table expression"
+    mkSelect d sl Nothing =
+        toQueryExpr $ makeSelect {msSetQuantifier = d, msSelectList = sl}
+    mkSelect d sl (Just (TableExpression f w g h od ofs fe)) =
+        Select d sl f w g h od ofs fe
+    values = keyword_ "values"
+             >> Values <$> commaSep (parens (commaSep scalarExpr))
+    table = keyword_ "table" >> Table <$> names
+
+    qeOpTable =
+        [[E.InfixL $ setOp Intersect "intersect"]
+        ,[E.InfixL $ setOp Except "except"
+         ,E.InfixL $ setOp Union "union"]]
+    setOp :: SetOperatorName -> Text -> Parser (QueryExpr -> QueryExpr -> QueryExpr)
+    setOp ctor opName = (cq
+        <$> (ctor <$ keyword_ opName)
+        <*> option SQDefault duplicates
+        <*> corr) <?> ""
+    cq o d c q0 q1 = QueryExprSetOp q0 o d c q1
+    corr = option Respectively (Corresponding <$ keyword_ "corresponding")
+
+    
+{-
+local data type to help with parsing the bit after the select list,
+called 'table expression' in the ansi sql grammar. Maybe this should
+be in the public syntax?
+-}
+
+data TableExpression
+    = TableExpression
+      {_teFrom :: [TableRef]
+      ,_teWhere :: Maybe ScalarExpr
+      ,_teGroupBy :: [GroupingExpr]
+      ,_teHaving :: Maybe ScalarExpr
+      ,_teOrderBy :: [SortSpec]
+      ,_teOffset :: Maybe ScalarExpr
+      ,_teFetchFirst :: Maybe ScalarExpr}
+
+tableExpression :: Parser TableExpression
+tableExpression = mkTe <$> (from <?> "from clause")
+                       <*> (optional whereClause <?> "where clause")
+                       <*> (option [] groupByClause <?> "group by clause")
+                       <*> (optional having <?> "having clause")
+                       <*> (option [] orderBy <?> "order by clause")
+                       <*> (offsetFetch <?> "")
+ where
+    mkTe f w g h od (ofs,fe) =
+        TableExpression f w g h od ofs fe
+
+{-
+wrapper for query expr which ignores optional trailing semicolon.
+
+TODO: change style
+-}
+
+topLevelQueryExpr :: Parser QueryExpr
+topLevelQueryExpr = queryExpr <??> (id <$ semi)
+
+topLevelStatement :: Parser Statement
+topLevelStatement = statement
+
+{-
+-------------------------
+
+= Statements
+-}
+
+statementWithoutSemicolon :: Parser Statement
+statementWithoutSemicolon = choice
+    [keyword_ "create" *> choice [createSchema
+                                 ,createTable
+                                 ,createIndex
+                                 ,createView
+                                 ,createDomain
+                                 ,createSequence
+                                 ,createRole
+                                 ,createAssertion]
+    ,keyword_ "alter" *> choice [alterTable
+                                ,alterDomain
+                                ,alterSequence]
+    ,keyword_ "drop" *> choice [dropSchema
+                               ,dropTable
+                               ,dropView
+                               ,dropDomain
+                               ,dropSequence
+                               ,dropRole
+                               ,dropAssertion]
+    ,delete
+    ,truncateSt
+    ,insert
+    ,update
+    ,startTransaction
+    ,savepoint
+    ,releaseSavepoint
+    ,commit
+    ,rollback
+    ,grant
+    ,revoke
+    ,SelectStatement <$> queryExpr
+    ]
+
+statement :: Parser Statement
+statement = statementWithoutSemicolon <* optional semi <|> EmptyStatement <$ semi
+
+createSchema :: Parser Statement
+createSchema = keyword_ "schema" >>
+    CreateSchema <$> names
+
+createTable :: Parser Statement
+createTable = do 
+  d <- askDialect id
+  let 
+    parseColumnDef = TableColumnDef <$> columnDef 
+    parseConstraintDef = uncurry TableConstraintDef <$> tableConstraintDef
+    separator = if diNonCommaSeparatedConstraints d
+      then optional comma
+      else Just <$> comma
+    constraints = sepBy parseConstraintDef separator
+    entries = ((:) <$> parseColumnDef <*> ((comma >> entries) <|> pure [])) <|> constraints
+
+  keyword_ "table" >>
+    CreateTable
+    <$> names 
+    <*> parens entries
+
+createIndex :: Parser Statement
+createIndex =
+    CreateIndex
+    <$> ((keyword_ "index" >> pure False) <|>
+         (keywords_ ["unique", "index"] >> pure True))
+    <*> names
+    <*> (keyword_ "on" >> names)
+    <*> parens (commaSep1 name)
+
+columnDef :: Parser ColumnDef
+columnDef = ColumnDef <$> name <*> typeName
+            <*> optional defaultClause
+            <*> option [] (some colConstraintDef)
+  where
+    defaultClause = choice [
+        keyword_ "default" >>
+        DefaultClause <$> scalarExpr
+        -- todo: left factor
+       ,try (keywords_ ["generated","always","as"] >>
+             GenerationClause <$> parens scalarExpr)
+       ,keyword_ "generated" >>
+        IdentityColumnSpec
+        <$> (GeneratedAlways <$ keyword_ "always"
+             <|> GeneratedByDefault <$ keywords_ ["by", "default"])
+        <*> (keywords_ ["as", "identity"] *>
+             option [] (parens sequenceGeneratorOptions))
+       ]
+
+tableConstraintDef :: Parser (Maybe [Name], TableConstraint)
+tableConstraintDef =
+    (,)
+    <$> optional (keyword_ "constraint" *> names)
+    <*> (unique <|> primaryKey <|> check <|> references)
+  where
+    unique = keyword_ "unique" >>
+        TableUniqueConstraint <$> parens (commaSep1 name)
+    primaryKey = keywords_ ["primary", "key"] >>
+        TablePrimaryKeyConstraint <$> parens (commaSep1 name)
+    check = keyword_ "check" >> TableCheckConstraint <$> parens scalarExpr
+    references = keywords_ ["foreign", "key"] >>
+        (\cs ft ftcs m (u,d) -> TableReferencesConstraint cs ft ftcs m u d)
+        <$> parens (commaSep1 name)
+        <*> (keyword_ "references" *> names)
+        <*> optional (parens $ commaSep1 name)
+        <*> refMatch
+        <*> refActions
+
+refMatch :: Parser ReferenceMatch
+refMatch = option DefaultReferenceMatch
+            (keyword_ "match" *>
+             choice [MatchFull <$ keyword_ "full"
+                    ,MatchPartial <$ keyword_ "partial"
+                    ,MatchSimple <$ keyword_ "simple"])
+refActions :: Parser (ReferentialAction,ReferentialAction)
+refActions =
+    P.runPermutation $ (,)
+    <$> P.toPermutationWithDefault DefaultReferentialAction onUpdate
+    <*> P.toPermutationWithDefault DefaultReferentialAction onDelete
+  where
+    -- todo: left factor?
+    onUpdate = try (keywords_ ["on", "update"]) *> referentialAction
+    onDelete = try (keywords_ ["on", "delete"]) *> referentialAction
+    referentialAction = choice [
+         RefCascade <$ keyword_ "cascade"
+         -- todo: left factor?
+        ,RefSetNull <$ try (keywords_ ["set", "null"])
+        ,RefSetDefault <$ try (keywords_ ["set", "default"])
+        ,RefRestrict <$ keyword_ "restrict"
+        ,RefNoAction <$ keywords_ ["no", "action"]]
+
+colConstraintDef :: Parser ColConstraintDef
+colConstraintDef =
+    ColConstraintDef
+    <$> optional (keyword_ "constraint" *> names)
+    <*> (nullable <|> notNull <|> unique <|> primaryKey <|> check <|> references)
+  where
+    nullable = ColNullableConstraint <$ keyword "null"
+    notNull = ColNotNullConstraint <$ keywords_ ["not", "null"]
+    unique = ColUniqueConstraint <$ keyword_ "unique"
+    primaryKey = do
+      keywords_ ["primary", "key"] 
+      d <- askDialect id
+      autoincrement <- if diAutoincrement d 
+        then optional (keyword_ "autoincrement")
+        else pure Nothing
+      pure $ ColPrimaryKeyConstraint $ isJust autoincrement
+    check = keyword_ "check" >> ColCheckConstraint <$> parens scalarExpr
+    references = keyword_ "references" >>
+        (\t c m (ou,od) -> ColReferencesConstraint t c m ou od)
+        <$> names
+        <*> optional (parens name)
+        <*> refMatch
+        <*> refActions
+
+-- slightly hacky parser for signed integers
+
+signedInteger :: Parser Integer
+signedInteger =
+    (*) <$> option 1 (1 <$ symbol "+" <|> (-1) <$ symbol "-")
+    <*> unsignedInteger
+
+sequenceGeneratorOptions :: Parser [SequenceGeneratorOption]
+sequenceGeneratorOptions =
+         -- todo: could try to combine exclusive options
+         -- such as cycle and nocycle
+         -- sort out options which are sometimes not allowed
+         -- as datatype, and restart with
+    P.runPermutation ((\a b c d e f g h j k -> catMaybes [a,b,c,d,e,f,g,h,j,k])
+                  <$> maybePermutation startWith
+                  <*> maybePermutation dataType
+                  <*> maybePermutation restart
+                  <*> maybePermutation incrementBy
+                  <*> maybePermutation maxValue
+                  <*> maybePermutation noMaxValue
+                  <*> maybePermutation minValue
+                  <*> maybePermutation noMinValue
+                  <*> maybePermutation scycle
+                  <*> maybePermutation noCycle
+                 )
+  where
+    maybePermutation p = P.toPermutationWithDefault Nothing (Just <$> p)
+    startWith = keywords_ ["start", "with"] >>
+                SGOStartWith <$> signedInteger
+    dataType = keyword_ "as" >>
+               SGODataType <$> typeName
+    restart = keyword_ "restart" >>
+              SGORestart <$> optional (keyword_ "with" *> signedInteger)
+    incrementBy = keywords_ ["increment", "by"] >>
+                SGOIncrementBy <$> signedInteger
+    maxValue = keyword_ "maxvalue" >>
+                SGOMaxValue <$> signedInteger
+    noMaxValue = SGONoMaxValue <$ try (keywords_ ["no","maxvalue"])
+    minValue = keyword_ "minvalue" >>
+                SGOMinValue <$> signedInteger
+    noMinValue = SGONoMinValue <$ try (keywords_ ["no","minvalue"])
+    scycle = SGOCycle <$ keyword_ "cycle"
+    noCycle = SGONoCycle <$ try (keywords_ ["no","cycle"])
+
+
+alterTable :: Parser Statement
+alterTable = keyword_ "table" >>
+    -- the choices have been ordered so that it works
+    AlterTable <$> names <*> choice [addConstraint
+                                    ,dropConstraint
+                                    ,addColumnDef
+                                    ,alterColumn
+                                    ,dropColumn
+                                    ]
+  where
+    addColumnDef = try (keyword_ "add"
+                        *> optional (keyword_ "column")) >>
+                   AddColumnDef <$> columnDef
+    alterColumn = keyword_ "alter" >> optional (keyword_ "column") >>
+                  name <**> choice [setDefault
+                                   ,dropDefault
+                                   ,setNotNull
+                                   ,dropNotNull
+                                   ,setDataType]
+    setDefault :: Parser (Name -> AlterTableAction)
+    -- todo: left factor
+    setDefault = try (keywords_ ["set","default"]) >>
+                 scalarExpr <$$> AlterColumnSetDefault
+    dropDefault = AlterColumnDropDefault <$ try (keywords_ ["drop","default"])
+    setNotNull = AlterColumnSetNotNull <$ try (keywords_ ["set","not","null"])
+    dropNotNull = AlterColumnDropNotNull <$ try (keywords_ ["drop","not","null"])
+    setDataType = try (keywords_ ["set","data","type"]) >>
+                  typeName <$$> AlterColumnSetDataType
+    dropColumn = try (keyword_ "drop" *> optional (keyword_ "column")) >>
+                 DropColumn <$> name <*> dropBehaviour
+    -- todo: left factor, this try is especially bad
+    addConstraint = try (keyword_ "add" >>
+        uncurry AddTableConstraintDef <$> tableConstraintDef)
+    dropConstraint = try (keywords_ ["drop","constraint"]) >>
+        DropTableConstraintDef <$> names <*> dropBehaviour
+
+
+dropSchema :: Parser Statement
+dropSchema = keyword_ "schema" >>
+    DropSchema <$> names <*> dropBehaviour
+
+dropTable :: Parser Statement
+dropTable = keyword_ "table" >>
+    DropTable <$> names <*> dropBehaviour
+
+createView :: Parser Statement
+createView =
+    CreateView
+    <$> (option False (True <$ keyword_ "recursive") <* keyword_ "view")
+    <*> names
+    <*> optional (parens (commaSep1 name))
+    <*> (keyword_ "as" *> queryExpr)
+    <*> optional (choice [
+            -- todo: left factor
+            DefaultCheckOption <$ try (keywords_ ["with", "check", "option"])
+           ,CascadedCheckOption <$ try (keywords_ ["with", "cascaded", "check", "option"])
+           ,LocalCheckOption <$ try (keywords_ ["with", "local", "check", "option"])
+            ])
+
+dropView :: Parser Statement
+dropView = keyword_ "view" >>
+    DropView <$> names <*> dropBehaviour
+
+createDomain :: Parser Statement
+createDomain = keyword_ "domain" >>
+    CreateDomain
+    <$> names
+    <*> (optional (keyword_ "as") *> typeName)
+    <*> optional (keyword_ "default" *> scalarExpr)
+    <*> many con
+  where
+    con = (,) <$> optional (keyword_ "constraint" *> names)
+          <*> (keyword_ "check" *> parens scalarExpr)
+
+alterDomain :: Parser Statement
+alterDomain = keyword_ "domain" >>
+    AlterDomain
+    <$> names
+    <*> (setDefault <|> constraint
+         <|> (keyword_ "drop" *> (dropDefault <|> dropConstraint)))
+  where
+    setDefault = keywords_ ["set", "default"] >> ADSetDefault <$> scalarExpr
+    constraint = keyword_ "add" >>
+       ADAddConstraint
+       <$> optional (keyword_ "constraint" *> names)
+       <*> (keyword_ "check" *> parens scalarExpr)
+    dropDefault = ADDropDefault <$ keyword_ "default"
+    dropConstraint = keyword_ "constraint" >> ADDropConstraint <$> names
+
+dropDomain :: Parser Statement
+dropDomain = keyword_ "domain" >>
+    DropDomain <$> names <*> dropBehaviour
+
+createSequence :: Parser Statement
+createSequence = keyword_ "sequence" >>
+    CreateSequence
+    <$> names
+    <*> sequenceGeneratorOptions
+
+alterSequence :: Parser Statement
+alterSequence = keyword_ "sequence" >>
+    AlterSequence
+    <$> names
+    <*> sequenceGeneratorOptions
+
+dropSequence :: Parser Statement
+dropSequence = keyword_ "sequence" >>
+    DropSequence <$> names <*> dropBehaviour
+
+createAssertion :: Parser Statement
+createAssertion = keyword_ "assertion" >>
+    CreateAssertion
+    <$> names
+    <*> (keyword_ "check" *> parens scalarExpr)
+
+
+dropAssertion :: Parser Statement
+dropAssertion = keyword_ "assertion" >>
+    DropAssertion <$> names <*> dropBehaviour
+
+{-
+-----------------
+
+= dml
+-}
+
+delete :: Parser Statement
+delete = keywords_ ["delete","from"] >>
+    Delete
+    <$> names
+    <*> optional (optional (keyword_ "as") *> name)
+    <*> optional (keyword_ "where" *> scalarExpr)
+
+truncateSt :: Parser Statement
+truncateSt = keywords_ ["truncate", "table"] >>
+    Truncate
+    <$> names
+    <*> option DefaultIdentityRestart
+        (ContinueIdentity <$ keywords_ ["continue","identity"]
+         <|> RestartIdentity <$ keywords_ ["restart","identity"])
+
+insert :: Parser Statement
+insert = keywords_ ["insert", "into"] >>
+    Insert
+    <$> names
+    <*> optional (parens $ commaSep1 name)
+    <*> (DefaultInsertValues <$ keywords_ ["default", "values"]
+         <|> InsertQuery <$> queryExpr)
+
+update :: Parser Statement
+update = keywords_ ["update"] >>
+    Update
+    <$> names
+    <*> optional (optional (keyword_ "as") *> name)
+    <*> (keyword_ "set" *> commaSep1 setClause)
+    <*> optional (keyword_ "where" *> scalarExpr)
+  where
+    setClause = multipleSet <|> singleSet
+    multipleSet = SetMultiple
+                  <$> parens (commaSep1 names)
+                  <*> (symbol "=" *> parens (commaSep1 scalarExpr))
+    singleSet = Set
+                <$> names
+                <*> (symbol "=" *> scalarExpr)
+
+dropBehaviour :: Parser DropBehaviour
+dropBehaviour =
+    option DefaultDropBehaviour
+    (Restrict <$ keyword_ "restrict"
+    <|> Cascade <$ keyword_ "cascade")
+
+{-
+-----------------------------
+
+= transaction management
+-}
+
+startTransaction :: Parser Statement
+startTransaction = StartTransaction <$ keywords_ ["start","transaction"]
+
+savepoint :: Parser Statement
+savepoint = keyword_ "savepoint" >>
+    Savepoint <$> name
+
+releaseSavepoint :: Parser Statement
+releaseSavepoint = keywords_ ["release","savepoint"] >>
+    ReleaseSavepoint <$> name
+
+commit :: Parser Statement
+commit = Commit <$ keyword_ "commit" <* optional (keyword_ "work")
+
+rollback :: Parser Statement
+rollback = keyword_ "rollback" >> optional (keyword_ "work") >>
+    Rollback <$> optional (keywords_ ["to", "savepoint"] *> name)
+
+
+{-
+------------------------------
+
+= Access control
+
+TODO: fix try at the 'on'
+-}
+
+grant :: Parser Statement
+grant = keyword_ "grant" >> (try priv <|> role)
+  where
+    priv = GrantPrivilege
+           <$> commaSep privilegeAction
+           <*> (keyword_ "on" *> privilegeObject)
+           <*> (keyword_ "to" *> commaSep name)
+           <*> option WithoutGrantOption
+               (WithGrantOption <$ keywords_ ["with","grant","option"])
+    role = GrantRole
+           <$> commaSep name
+           <*> (keyword_ "to" *> commaSep name)
+           <*> option WithoutAdminOption
+               (WithAdminOption <$ keywords_ ["with","admin","option"])
+
+createRole :: Parser Statement
+createRole = keyword_ "role" >>
+    CreateRole <$> name
+
+dropRole :: Parser Statement
+dropRole = keyword_ "role" >>
+    DropRole <$> name
+
+-- TODO: fix try at the 'on'
+
+revoke :: Parser Statement
+revoke = keyword_ "revoke" >> (try priv <|> role)
+  where
+    priv = RevokePrivilege
+           <$> option NoGrantOptionFor
+               (GrantOptionFor <$ keywords_ ["grant","option","for"])
+           <*> commaSep privilegeAction
+           <*> (keyword_ "on" *> privilegeObject)
+           <*> (keyword_ "from" *> commaSep name)
+           <*> dropBehaviour
+    role = RevokeRole
+           <$> option NoAdminOptionFor
+               (AdminOptionFor <$ keywords_ ["admin","option", "for"])
+           <*> commaSep name
+           <*> (keyword_ "from" *> commaSep name)
+           <*> dropBehaviour
+
+privilegeAction :: Parser PrivilegeAction
+privilegeAction = choice
+    [PrivAll <$ keywords_ ["all","privileges"]
+    ,keyword_ "select" >>
+     PrivSelect <$> option [] (parens $ commaSep name)
+    ,PrivDelete <$ keyword_ "delete"
+    ,PrivUsage <$ keyword_ "usage"
+    ,PrivTrigger <$ keyword_ "trigger"
+    ,PrivExecute <$ keyword_ "execute"
+    ,keyword_ "insert" >>
+     PrivInsert <$> option [] (parens $ commaSep name)
+    ,keyword_ "update" >>
+     PrivUpdate <$> option [] (parens $ commaSep name)
+    ,keyword_ "references" >>
+     PrivReferences <$> option [] (parens $ commaSep name)
+    ]
+
+privilegeObject :: Parser PrivilegeObject
+privilegeObject = choice
+    [keyword_ "domain" >> PrivDomain <$> names
+    ,keyword_ "type" >> PrivType <$> names
+    ,keyword_ "sequence" >> PrivSequence <$> names
+    ,keywords_ ["specific","function"] >> PrivFunction <$> names
+    ,optional (keyword_ "table") >> PrivTable <$> names
+    ]
+
+
+{-
+----------------------------
+
+wrapper to parse a series of statements. They must be separated by
+semicolon, but for the last statement, the trailing semicolon is
+optional.
+-}
+
+statements :: Parser [Statement]
+statements = many statement
+
+{-
+----------------------------------------------
+
+= multi keyword helper
+
+This helper is to help parsing multiple options of multiple keywords
+with similar prefixes, e.g. parsing 'is null' and 'is not null'.
+
+use to left factor/ improve:
+typed literal and general identifiers
+not like, not in, not between operators
+help with factoring keyword functions and other app-likes
+the join keyword sequences
+fetch first/next
+row/rows only
+
+There is probably a simpler way of doing this but I am a bit
+thick.
+-}
+
+makeKeywordTree :: [Text] -> Parser [Text]
+makeKeywordTree sets =
+    parseTrees (sort $ map T.words sets)
+  where
+    parseTrees :: [[Text]] -> Parser [Text]
+    parseTrees ws = do
+      let gs :: [[[Text]]]
+          gs = groupBy ((==) `on` safeHead) ws
+      choice $ map parseGroup gs
+    parseGroup :: [[Text]] -> Parser [Text]
+    parseGroup l@((k:_):_) = do
+        keyword_ k
+        let tls = mapMaybe safeTail l
+            pr = (k:) <$> parseTrees tls
+        if any null tls
+          then pr <|> pure [k]
+          else pr
+    parseGroup _ = guard False >> fail "impossible"
+    safeHead (x:_) = Just x
+    safeHead [] = Nothing
+    safeTail (_:x) = Just x
+    safeTail [] = Nothing
+
+------------------------------------------------------------------------------
+
+-- parser helpers
+
+(<$$>) :: Applicative f =>
+      f b -> (a -> b -> c) -> f (a -> c)
+(<$$>) pa c = pa <**> pure (flip c)
+
+(<$$$>) :: Applicative f =>
+          f c -> (a -> b -> c -> t) -> f (b -> a -> t)
+p <$$$> c = p <**> pure (flip3 c)
+
+(<$$$$>) :: Applicative f =>
+          f d -> (a -> b -> c -> d -> t) -> f (c -> b -> a -> t)
+p <$$$$> c = p <**> pure (flip4 c)
+
+(<$$$$$>) :: Applicative f =>
+          f e -> (a -> b -> c -> d -> e -> t) -> f (d -> c -> b -> a -> t)
+p <$$$$$> c = p <**> pure (flip5 c)
+
+optionSuffix :: (a -> Parser a) -> a -> Parser a
+optionSuffix p a = option a (p a)
+
+{-
+parses an optional postfix element and applies its result to its left
+hand result, taken from uu-parsinglib
+
+TODO: make sure the precedence higher than <|> and lower than the
+other operators so it can be used nicely
+-}
+
+(<??>) :: Parser a -> Parser (a -> a) -> Parser a
+p <??> q = p <**> option id q
+
+-- 0 to many repeated applications of suffix parser
+
+(<??*>) :: Parser a -> Parser (a -> a) -> Parser a
+p <??*> q = foldr ($) <$> p <*> (reverse <$> many q)
+
+{-
+These are to help with left factored parsers:
+
+a <**> (b <**> (c <**> pure (flip3 ctor)))
+
+Not sure the names are correct, but they follow a pattern with flip
+a <**> (b <**> pure (flip ctor))
+-}
+
+flip3 :: (a -> b -> c -> t) -> c -> b -> a -> t
+flip3 f a b c = f c b a
+
+flip4 :: (a -> b -> c -> d -> t) -> d -> c -> b -> a -> t
+flip4 f a b c d = f d c b a
+
+flip5 :: (a -> b -> c -> d -> e -> t) -> e -> d -> c -> b -> a -> t
+flip5 f a b c d e = f e d c b a
+
+--------------------------------------
+
+unsignedInteger :: Parser Integer
+unsignedInteger = read . T.unpack <$> sqlNumberTok True <?> "natural number"
+
+-- todo: work out the symbol parsing better
+
+symbol :: Text -> Parser Text
+symbol s = symbolTok (Just s) <?> T.unpack s
+
+singleCharSymbol :: Char -> Parser Char
+singleCharSymbol c = c <$ symbol (T.singleton c)
+
+questionMark :: Parser Char
+questionMark = singleCharSymbol '?' <?> "question mark"
+
+openParen :: Parser Char
+openParen = singleCharSymbol '('
+
+closeParen :: Parser Char
+closeParen = singleCharSymbol ')'
+
+openBracket :: Parser Char
+openBracket = singleCharSymbol '['
+
+closeBracket :: Parser Char
+closeBracket = singleCharSymbol ']'
+
+
+comma :: Parser Char
+comma = singleCharSymbol ',' <?> ""
+
+semi :: Parser Char
+semi = singleCharSymbol ';' <?> ""
+
+-- = helper functions
+
+keyword :: Text -> Parser Text
+keyword k = unquotedIdentifierTok [] (Just k) <?> T.unpack k
+
+-- helper function to improve error messages
+
+keywords_ :: [Text] -> Parser ()
+keywords_ ks = mapM_ keyword_ ks <?> T.unpack (T.unwords ks)
+
+
+parens :: Parser a -> Parser a
+parens = between openParen closeParen
+
+brackets :: Parser a -> Parser a
+brackets = between openBracket closeBracket
+
+commaSep :: Parser a -> Parser [a]
+commaSep = (`sepBy` comma)
+
+keyword_ :: Text -> Parser ()
+keyword_ = void . keyword
+
+symbol_ :: Text -> Parser ()
+symbol_ = void . symbol
+
+commaSep1 :: Parser a -> Parser [a]
+commaSep1 = (`sepBy1` comma)
+
+------------------------------------------------------------------------------
+
+-- interfacing with the lexing
+{-
+TODO: push checks into here:
+keyword blacklists
+unsigned integer match
+symbol matching
+keyword matching
+
+-}
+stringTok :: Parser (Text,Text,Text)
+stringTok = token test Set.empty <?> "string literal"
+  where
+    test (L.WithPos _ _ _ (L.SqlString s e t)) = Just (s,e,t)
+    test _ = Nothing
+
+singleQuotesOnlyStringTok :: Parser Text
+singleQuotesOnlyStringTok = token test Set.empty <?> "string literal"
+  where
+    test (L.WithPos _ _ _ (L.SqlString "'" "'" t)) = Just t
+    test _ = Nothing
+
+{-
+This is to support SQL strings where you can write
+'part of a string' ' another part'
+and it will parse as a single string
+
+It is only allowed when all the strings are quoted with ' atm.
+-}
+
+stringTokExtend :: Parser (Text,Text,Text)
+stringTokExtend = do
+    (s,e,x) <- stringTok
+    choice [
+         do
+         guard (s == "'" && e == "'")
+         (s',e',y) <- stringTokExtend
+         guard (s' == "'" && e' == "'")
+         pure (s,e,x <> y)
+        ,pure (s,e,x)
+        ]
+
+hostParamTok :: Parser Text
+hostParamTok = token test Set.empty <?> ""
+  where
+    test (L.WithPos _ _ _ (L.PrefixedVariable c p)) = Just $ T.cons c p
+    test _ = Nothing
+
+positionalArgTok :: Parser Int
+positionalArgTok = token test Set.empty <?> ""
+  where
+    test (L.WithPos _ _ _ (L.PositionalArg p)) = Just p
+    test _ = Nothing
+
+sqlNumberTok :: Bool -> Parser Text
+sqlNumberTok intOnly = token test Set.empty <?> ""
+  where
+    test (L.WithPos _ _ _ (L.SqlNumber p)) | not intOnly || T.all isDigit p = Just p
+    test _ = Nothing
+
+symbolTok :: Maybe Text -> Parser Text
+symbolTok sym = token test Set.empty <?> ""
+  where
+    test (L.WithPos _ _ _ (L.Symbol p)) =
+        case sym of
+            Nothing -> Just p
+            Just sym' | sym' == p -> Just p
+            _ -> Nothing
+    test _ = Nothing
+
+{-
+The blacklisted names are mostly needed when we parse something with
+an optional alias, e.g. select a a from t. If we write select a from
+t, we have to make sure the from isn't parsed as an alias. I'm not
+sure what other places strictly need the blacklist, and in theory it
+could be tuned differently for each place the identifierString/
+identifier parsers are used to only blacklist the bare
+minimum. Something like this might be needed for dialect support, even
+if it is pretty silly to use a keyword as an unquoted identifier when
+there is a quoting syntax as well.
+
+The standard has a weird mix of reserved keywords and unreserved
+keywords (I'm not sure what exactly being an unreserved keyword
+means).
+
+The current approach tries to have everything which is a keyword only
+in the keyword list - so it can only be used in some other context if
+quoted. If something is a 'ansi keyword', but appears only as an
+identifier or function name for instance in the syntax (or something
+that looks identical to this), then it isn't treated as a keyword at
+all. When there is some overlap (e.g. 'set'), then there is either
+special case parsing code to handle this (in the case of set), or it
+is not treated as a keyword (not perfect, but if it more or less
+works, ok for now).
+
+An exception to this is the standard type names are considered as
+keywords at the moment, with a special case in the type parser to
+make this work. Maybe this isn't necessary or is a bad idea.
+
+It is possible to have a problem if you remove something which is a
+keyword from this list, and still want to parse statements using it
+as a keyword - for instance, removing things like 'from' or 'as',
+will likely mean many things don't parse anymore.
+
+-}
+
+identifierTok :: [Text] -> Parser (Maybe (Text,Text), Text)
+identifierTok blackList = token test Set.empty <?> ""
+  where
+    test (L.WithPos _ _ _ (L.Identifier q@(Just {}) p)) = Just (q,p)
+    test (L.WithPos _ _ _ (L.Identifier q p))
+        | T.toLower p `notElem` blackList = Just (q,p)
+    test _ = Nothing
+
+unquotedIdentifierTok :: [Text] -> Maybe Text -> Parser Text
+unquotedIdentifierTok blackList kw = token test Set.empty <?> ""
+  where
+    test (L.WithPos _ _ _ (L.Identifier Nothing p)) =
+        case kw of
+            Nothing | T.toLower p `notElem` blackList -> Just p
+            Just k | k == T.toLower p -> Just p
+            _ -> Nothing
+    test _ = Nothing
+
+------------------------------------------------------------------------------
+
+-- dialect
+
+guardDialect :: (Dialect -> Bool) -> Parser ()
+guardDialect p = guard . p =<< ask
+
+askDialect :: (Dialect -> a) -> Parser a
+askDialect = asks
+
diff --git a/Language/SQL/SimpleSQL/Parse.lhs b/Language/SQL/SimpleSQL/Parse.lhs
deleted file mode 100644
--- a/Language/SQL/SimpleSQL/Parse.lhs
+++ /dev/null
@@ -1,2152 +0,0 @@
-
-= TOC:
-
-notes
-Public api
-Names - parsing identifiers
-Typenames
-Scalar expressions
-  simple literals
-  star, param
-  parens expression, row constructor and scalar subquery
-  case, cast, exists, unique, array/ multiset constructor
-  typed literal, app, special function, aggregate, window function
-  suffixes: in, between, quantified comparison, match predicate, array
-    subscript, escape, collate
-  operators
-  scalar expression top level
-  helpers
-query expressions
-  select lists
-  from clause
-  other table expression clauses:
-    where, group by, having, order by, offset and fetch
-  common table expressions
-  query expression
-  set operations
-lexers
-utilities
-
-= Notes about the code
-
-The lexers appear at the bottom of the file. There tries to be a clear
-separation between the lexers and the other parser which only use the
-lexers, this isn't 100% complete at the moment and needs fixing.
-
-== Left factoring
-
-The parsing code is aggressively left factored, and try is avoided as
-much as possible. Try is avoided because:
-
- * when it is overused it makes the code hard to follow
- * when it is overused it makes the parsing code harder to debug
- * it makes the parser error messages much worse
-
-The code could be made a bit simpler with a few extra 'trys', but this
-isn't done because of the impact on the parser error
-messages. Apparently it can also help the speed but this hasn't been
-looked into.
-
-== Parser error messages
-
-A lot of care has been given to generating good parser error messages
-for invalid syntax. There are a few utils below which partially help
-in this area.
-
-There is a set of crafted bad expressions in ErrorMessages.lhs, these
-are used to guage the quality of the error messages and monitor
-regressions by hand. The use of <?> is limited as much as possible:
-each instance should justify itself by improving an actual error
-message.
-
-There is also a plan to write a really simple expression parser which
-doesn't do precedence and associativity, and the fix these with a pass
-over the ast. I don't think there is any other way to sanely handle
-the common prefixes between many infix and postfix multiple keyword
-operators, and some other ambiguities also. This should help a lot in
-generating good error messages also.
-
-Both the left factoring and error message work are greatly complicated
-by the large number of shared prefixes of the various elements in SQL
-syntax.
-
-== Main left factoring issues
-
-There are three big areas which are tricky to left factor:
-
- * typenames
- * scalar expressions which can start with an identifier
- * infix and suffix operators
-
-=== typenames
-
-There are a number of variations of typename syntax. The standard
-deals with this by switching on the name of the type which is parsed
-first. This code doesn't do this currently, but might in the
-future. Taking the approach in the standard grammar will limit the
-extensibility of the parser and might affect the ease of adapting to
-support other sql dialects.
-
-=== identifier scalar expressions
-
-There are a lot of scalar expression nodes which start with
-identifiers, and can't be distinguished the tokens after the initial
-identifier are parsed. Using try to implement these variations is very
-simple but makes the code much harder to debug and makes the parser
-error messages really bad.
-
-Here is a list of these nodes:
-
- * identifiers
- * function application
- * aggregate application
- * window application
- * typed literal: typename 'literal string'
- * interval literal which is like the typed literal with some extras
-
-There is further ambiguity e.g. with typed literals with precision,
-functions, aggregates, etc. - these are an identifier, followed by
-parens comma separated scalar expressions or something similar, and it
-is only later that we can find a token which tells us which flavour it
-is.
-
-There is also a set of nodes which start with an identifier/keyword
-but can commit since no other syntax can start the same way:
-
- * case
- * cast
- * exists, unique subquery
- * array constructor
- * multiset constructor
- * all the special syntax functions: extract, position, substring,
-  convert, translate, overlay, trim, etc.
-
-The interval literal mentioned above is treated in this group at the
-moment: if we see 'interval' we parse it either as a full interval
-literal or a typed literal only.
-
-Some items in this list might have to be fixed in the future, e.g. to
-support standard 'substring(a from 3 for 5)' as well as regular
-function substring syntax 'substring(a,3,5) at the same time.
-
-The work in left factoring all this is mostly done, but there is still
-a substantial bit to complete and this is by far the most difficult
-bit. At the moment, the work around is to use try, the downsides of
-which is the poor parsing error messages.
-
-=== infix and suffix operators
-
-== permissiveness
-
-The parser is very permissive in many ways. This departs from the
-standard which is able to eliminate a number of possibilities just in
-the grammar, which this parser allows. This is done for a number of
-reasons:
-
- * it makes the parser simple - less variations
- * it should allow for dialects and extensibility more easily in the
-  future (e.g. new infix binary operators with custom precedence)
- * many things which are effectively checked in the grammar in the
-  standard, can be checked using a typechecker or other simple static
-  analysis
-
-To use this code as a front end for a sql engine, or as a sql validity
-checker, you will need to do a lot of checks on the ast. A
-typechecker/static checker plus annotation to support being a compiler
-front end is planned but not likely to happen too soon.
-
-Some of the areas this affects:
-
-typenames: the variation of the type name should switch on the actual
-name given according to the standard, but this code only does this for
-the special case of interval type names. E.g. you can write 'int
-collate C' or 'int(15,2)' and this will parse as a character type name
-or a precision scale type name instead of being rejected.
-
-scalar expressions: every variation on scalar expressions uses the same
-parser/syntax. This means we don't try to stop non boolean valued
-expressions in boolean valued contexts in the parser. Another area
-this affects is that we allow general scalar expressions in group by,
-whereas the standard only allows column names with optional collation.
-
-These are all areas which are specified (roughly speaking) in the
-syntax rather than the semantics in the standard, and we are not
-fixing them in the syntax but leaving them till the semantic checking
-(which doesn't exist in this code at this time).
-
-> {-# LANGUAGE TupleSections #-}
-> -- | This is the module with the parser functions.
-> module Language.SQL.SimpleSQL.Parse
->     (parseQueryExpr
->     ,parseScalarExpr
->     ,parseStatement
->     ,parseStatements
->     ,ParseError(..)) where
-
-> import Control.Monad.Identity (Identity)
-> import Control.Monad (guard, void)
-> import Control.Applicative ((<**>))
-> import Data.Char (toLower, isDigit)
-> import Text.Parsec (setPosition,setSourceColumn,setSourceLine,getPosition
->                    ,option,between,sepBy,sepBy1
->                    ,try,many,many1,(<|>),choice,eof
->                    ,optionMaybe,optional,runParser
->                    ,chainl1, chainr1,(<?>))
-> import Text.Parsec.Perm (permute,(<$?>), (<|?>))
-> import Text.Parsec.Prim (getState, token)
-> import Text.Parsec.Pos (newPos)
-> import qualified Text.Parsec.Expr as E
-> import Data.List (intercalate,sort,groupBy)
-> import Data.Function (on)
-> import Data.Maybe
-> import Text.Parsec.String (GenParser)
-
-> import Language.SQL.SimpleSQL.Syntax
-> import Language.SQL.SimpleSQL.Combinators
-> import Language.SQL.SimpleSQL.Errors
-> import Language.SQL.SimpleSQL.Dialect
-> import qualified Language.SQL.SimpleSQL.Lex as L
-
-
-= Public API
-
-> -- | Parses a query expr, trailing semicolon optional.
-> parseQueryExpr :: Dialect
->                   -- ^ dialect of SQL to use
->                -> FilePath
->                   -- ^ filename to use in error messages
->                -> Maybe (Int,Int)
->                   -- ^ line number and column number of the first character
->                   -- in the source to use in error messages
->                -> String
->                   -- ^ the SQL source to parse
->                -> Either ParseError QueryExpr
-> parseQueryExpr = wrapParse topLevelQueryExpr
-
-> -- | Parses a statement, trailing semicolon optional.
-> parseStatement :: Dialect
->                   -- ^ dialect of SQL to use
->                -> FilePath
->                   -- ^ filename to use in error messages
->                -> Maybe (Int,Int)
->                   -- ^ line number and column number of the first character
->                   -- in the source to use in error messages
->                -> String
->                   -- ^ the SQL source to parse
->                -> Either ParseError Statement
-> parseStatement = wrapParse topLevelStatement
-
-
-> -- | Parses a list of statements, with semi colons between
-> -- them. The final semicolon is optional.
-> parseStatements :: Dialect
->                   -- ^ dialect of SQL to use
->                 -> FilePath
->                    -- ^ filename to use in error messages
->                 -> Maybe (Int,Int)
->                    -- ^ line number and column number of the first character
->                    -- in the source to use in error messages
->                 -> String
->                    -- ^ the SQL source to parse
->                 -> Either ParseError [Statement]
-> parseStatements = wrapParse statements
-
-> -- | Parses a scalar expression.
-> parseScalarExpr :: Dialect
->                    -- ^ dialect of SQL to use
->                 -> FilePath
->                    -- ^ filename to use in error messages
->                 -> Maybe (Int,Int)
->                    -- ^ line number and column number of the first character
->                    -- in the source to use in error messages
->                 -> String
->                    -- ^ the SQL source to parse
->                 -> Either ParseError ScalarExpr
-> parseScalarExpr = wrapParse scalarExpr
-
-This helper function takes the parser given and:
-
-sets the position when parsing
-automatically skips leading whitespace
-checks the parser parses all the input using eof
-converts the error return to the nice wrapper
-
-> wrapParse :: Parser a
->           -> Dialect
->           -> FilePath
->           -> Maybe (Int,Int)
->           -> String
->           -> Either ParseError a
-> wrapParse parser d f p src = do
->     let (l,c) = fromMaybe (1,1) p
->     lx <- L.lexSQL d f (Just (l,c)) src
->     either (Left . convParseError src) Right
->       $ runParser (setPos p *> parser <* eof)
->                   d f $ filter keep lx
->   where
->     setPos Nothing = pure ()
->     setPos (Just (l,c)) = fmap up getPosition >>= setPosition
->       where up = flip setSourceColumn c . flip setSourceLine l
->     keep (_,L.Whitespace {}) = False
->     keep (_,L.LineComment {}) = False
->     keep (_,L.BlockComment {}) = False
->     keep _ = True
-
-
-------------------------------------------------
-
-= Names
-
-Names represent identifiers and a few other things. The parser here
-handles regular identifiers, dotten chain identifiers, quoted
-identifiers and unicode quoted identifiers.
-
-Dots: dots in identifier chains are parsed here and represented in the
-Iden constructor usually. If parts of the chains are non identifier
-scalar expressions, then this is represented by a BinOp "."
-instead. Dotten chain identifiers which appear in other contexts (such
-as function names, table names, are represented as [Name] only.
-
-Identifier grammar:
-
-unquoted:
-underscore <|> letter : many (underscore <|> alphanum
-
-example
-_example123
-
-quoted:
-
-double quote, many (non quote character or two double quotes
-together), double quote
-
-"example quoted"
-"example with "" quote"
-
-unicode quoted is the same as quoted in this parser, except it starts
-with U& or u&
-
-u&"example quoted"
-
-> name :: Parser Name
-> name = do
->     d <- getState
->     uncurry Name <$> identifierTok (blacklist d)
-
-todo: replace (:[]) with a named function all over
-
-> names :: Parser [Name]
-> names = reverse <$> (((:[]) <$> name) <??*> anotherName)
->   -- can't use a simple chain here since we
->   -- want to wrap the . + name in a try
->   -- this will change when this is left factored
->   where
->     anotherName :: Parser ([Name] -> [Name])
->     anotherName = try ((:) <$> (symbol "." *> name))
-
-= Type Names
-
-Typenames are used in casts, and also in the typed literal syntax,
-which is a typename followed by a string literal.
-
-Here are the grammar notes:
-
-== simple type name
-
-just an identifier chain or a multi word identifier (this is a fixed
-list of possibilities, e.g. as 'character varying', see below in the
-parser code for the exact list).
-
-<simple-type-name> ::= <identifier-chain>
-     | multiword-type-identifier
-
-== Precision type name
-
-<precision-type-name> ::= <simple-type-name> <left paren> <unsigned-int> <right paren>
-
-e.g. char(5)
-
-note: above and below every where a simple type name can appear, this
-means a single identifier/quoted or a dotted chain, or a multi word
-identifier
-
-== Precision scale type name
-
-<precision-type-name> ::= <simple-type-name> <left paren> <unsigned-int> <comma> <unsigned-int> <right paren>
-
-e.g. decimal(15,2)
-
-== Lob type name
-
-this is a variation on the precision type name with some extra info on
-the units:
-
-<lob-type-name> ::=
-   <simple-type-name> <left paren> <unsigned integer> [ <multiplier> ] [ <char length units> ] <right paren>
-
-<multiplier>    ::=   K | M | G
-<char length units>    ::=   CHARACTERS | CODE_UNITS | OCTETS
-
-(if both multiplier and char length units are missing, then this will
-parse as a precision type name)
-
-e.g.
-clob(5M octets)
-
-== char type name
-
-this is a simple type with optional precision which allows the
-character set or the collation to appear as a suffix:
-
-<char type name> ::=
-    <simple type name>
-    [ <left paren> <unsigned-int> <right paren> ]
-    [ CHARACTER SET <identifier chain> ]
-    [ COLLATE <identifier chain> ]
-
-e.g.
-
-char(5) character set my_charset collate my_collation
-
-= Time typename
-
-this is typename with optional precision and either 'with time zone'
-or 'without time zone' suffix, e.g.:
-
-<datetime type> ::=
-    [ <left paren> <unsigned-int> <right paren> ]
-    <with or without time zone>
-<with or without time zone> ::= WITH TIME ZONE | WITHOUT TIME ZONE
-    WITH TIME ZONE | WITHOUT TIME ZONE
-
-= row type name
-
-<row type> ::=
-    ROW <left paren> <field definition> [ { <comma> <field definition> }... ] <right paren>
-
-<field definition> ::= <identifier> <type name>
-
-e.g.
-row(a int, b char(5))
-
-= interval type name
-
-<interval type> ::= INTERVAL <interval datetime field> [TO <interval datetime field>]
-
-<interval datetime field> ::=
-  <datetime field> [ <left paren> <unsigned int> [ <comma> <unsigned int> ] <right paren> ]
-
-= array type name
-
-<array type> ::= <data type> ARRAY [ <left bracket> <unsigned integer> <right bracket> ]
-
-= multiset type name
-
-<multiset type>    ::=   <data type> MULTISET
-
-A type name will parse into the 'smallest' constructor it will fit in
-syntactically, e.g. a clob(5) will parse to a precision type name, not
-a lob type name.
-
-Unfortunately, to improve the error messages, there is a lot of (left)
-factoring in this function, and it is a little dense.
-
-> typeName :: Parser TypeName
-> typeName =
->     (rowTypeName <|> intervalTypeName <|> otherTypeName)
->     <??*> tnSuffix
->   where
->     rowTypeName =
->         RowTypeName <$> (keyword_ "row" *> parens (commaSep1 rowField))
->     rowField = (,) <$> name <*> typeName
->     ----------------------------
->     intervalTypeName =
->         keyword_ "interval" *>
->         (uncurry IntervalTypeName <$> intervalQualifier)
->     ----------------------------
->     otherTypeName =
->         nameOfType <**>
->             (typeNameWithParens
->              <|> pure Nothing <**> (timeTypeName <|> charTypeName)
->              <|> pure TypeName)
->     nameOfType = reservedTypeNames <|> names
->     charTypeName = charSet <**> (option [] tcollate <$$$$> CharTypeName)
->                    <|> pure [] <**> (tcollate <$$$$> CharTypeName)
->     typeNameWithParens =
->         (openParen *> unsignedInteger)
->         <**> (closeParen *> precMaybeSuffix
->               <|> (precScaleTypeName <|> precLengthTypeName) <* closeParen)
->     precMaybeSuffix = (. Just) <$> (timeTypeName <|> charTypeName)
->                       <|> pure (flip PrecTypeName)
->     precScaleTypeName = (comma *> unsignedInteger) <$$$> PrecScaleTypeName
->     precLengthTypeName =
->         Just <$> lobPrecSuffix
->         <**> (optionMaybe lobUnits <$$$$> PrecLengthTypeName)
->         <|> pure Nothing <**> ((Just <$> lobUnits) <$$$$> PrecLengthTypeName)
->     timeTypeName = tz <$$$> TimeTypeName
->     ----------------------------
->     lobPrecSuffix = PrecK <$ keyword_ "k"
->                     <|> PrecM <$ keyword_ "m"
->                     <|> PrecG <$ keyword_ "g"
->                     <|> PrecT <$ keyword_ "t"
->                     <|> PrecP <$ keyword_ "p"
->     lobUnits = PrecCharacters <$ keyword_ "characters"
->                -- char and byte are the oracle spelling
->                -- todo: move these to oracle dialect
->                <|> PrecCharacters <$ keyword_ "char"
->                <|> PrecOctets <$ keyword_ "octets"
->                <|> PrecOctets <$ keyword_ "byte"
->     tz = True <$ keywords_ ["with", "time","zone"]
->          <|> False <$ keywords_ ["without", "time","zone"]
->     charSet = keywords_ ["character", "set"] *> names
->     tcollate = keyword_ "collate" *> names
->     ----------------------------
->     tnSuffix = multiset <|> array
->     multiset = MultisetTypeName <$ keyword_ "multiset"
->     array = keyword_ "array" *>
->         (optionMaybe (brackets unsignedInteger) <$$> ArrayTypeName)
->     ----------------------------
->     -- this parser handles the fixed set of multi word
->     -- type names, plus all the type names which are
->     -- reserved words
->     reservedTypeNames = do
->         d <- getState
->         (:[]) . Name Nothing . unwords <$> makeKeywordTree (diSpecialTypeNames d)
->         
-
-= Scalar expressions
-
-== simple literals
-
-See the stringToken lexer below for notes on string literal syntax.
-
-> stringLit :: Parser ScalarExpr
-> stringLit = (\(s,e,t) -> StringLit s e t) <$> stringTokExtend
-
-> numberLit :: Parser ScalarExpr
-> numberLit = NumLit <$> sqlNumberTok False
-
-> simpleLiteral :: Parser ScalarExpr
-> simpleLiteral = numberLit <|> stringLit
-
-== star, param, host param
-
-=== star
-
-used in select *, select x.*, and agg(*) variations, and some other
-places as well. The parser doesn't attempt to check that the star is
-in a valid context, it parses it OK in any scalar expression context.
-
-> star :: Parser ScalarExpr
-> star = Star <$ symbol "*"
-
-== parameter
-
-unnamed parameter or named parameter
-use in e.g. select * from t where a = ?
-select x from t where x > :param
-
-> parameter :: Parser ScalarExpr
-> parameter = choice
->     [Parameter <$ questionMark
->     ,HostParameter
->      <$> hostParamTok
->      <*> optionMaybe (keyword "indicator" *> hostParamTok)]
-
-== positional arg
-
-> positionalArg :: Parser ScalarExpr
-> positionalArg = PositionalArg <$> positionalArgTok
-
-== parens
-
-scalar expression parens, row ctor and scalar subquery
-
-> parensExpr :: Parser ScalarExpr
-> parensExpr = parens $ choice
->     [SubQueryExpr SqSq <$> queryExpr
->     ,ctor <$> commaSep1 scalarExpr]
->   where
->     ctor [a] = Parens a
->     ctor as = SpecialOp [Name Nothing "rowctor"] as
-
-== case, cast, exists, unique, array/multiset constructor, interval
-
-All of these start with a fixed keyword which is reserved, so no other
-syntax can start with the same keyword.
-
-=== case expression
-
-> caseExpr :: Parser ScalarExpr
-> caseExpr =
->     Case <$> (keyword_ "case" *> optionMaybe scalarExpr)
->          <*> many1 whenClause
->          <*> optionMaybe elseClause
->          <* keyword_ "end"
->   where
->    whenClause = (,) <$> (keyword_ "when" *> commaSep1 scalarExpr)
->                     <*> (keyword_ "then" *> scalarExpr)
->    elseClause = keyword_ "else" *> scalarExpr
-
-=== cast
-
-cast: cast(expr as type)
-
-> cast :: Parser ScalarExpr
-> cast = keyword_ "cast" *>
->        parens (Cast <$> scalarExpr
->                     <*> (keyword_ "as" *> typeName))
-
-=== exists, unique
-
-subquery expression:
-[exists|unique] (queryexpr)
-
-> subquery :: Parser ScalarExpr
-> subquery = SubQueryExpr <$> sqkw <*> parens queryExpr
->   where
->     sqkw = SqExists <$ keyword_ "exists" <|> SqUnique <$ keyword_ "unique"
-
-=== array/multiset constructor
-
-> arrayCtor :: Parser ScalarExpr
-> arrayCtor = keyword_ "array" >>
->     choice
->     [ArrayCtor <$> parens queryExpr
->     ,Array (Iden [Name Nothing "array"]) <$> brackets (commaSep scalarExpr)]
-
-As far as I can tell, table(query expr) is just syntax sugar for
-multiset(query expr). It must be there for compatibility or something.
-
-> multisetCtor :: Parser ScalarExpr
-> multisetCtor =
->     choice
->     [keyword_ "multiset" >>
->      choice
->      [MultisetQueryCtor <$> parens queryExpr
->      ,MultisetCtor <$> brackets (commaSep scalarExpr)]
->     ,keyword_ "table" >>
->      MultisetQueryCtor <$> parens queryExpr]
-
-> nextValueFor :: Parser ScalarExpr
-> nextValueFor = keywords_ ["next","value","for"] >>
->     NextValueFor <$> names
-
-=== interval
-
-interval literals are a special case and we follow the grammar less
-permissively here
-
-parse SQL interval literals, something like
-interval '5' day (3)
-or
-interval '5' month
-
-if the literal looks like this:
-interval 'something'
-
-then it is parsed as a regular typed literal. It must have a
-interval-datetime-field suffix to parse as an intervallit
-
-It uses try because of a conflict with interval type names: todo, fix
-this. also fix the monad -> applicative
-
-> intervalLit :: Parser ScalarExpr
-> intervalLit = try (keyword_ "interval" >> do
->     s <- optionMaybe $ choice [Plus <$ symbol_ "+"
->                               ,Minus <$ symbol_ "-"]
->     lit <- singleQuotesOnlyStringTok
->     q <- optionMaybe intervalQualifier
->     mkIt s lit q)
->   where
->     mkIt Nothing val Nothing = pure $ TypedLit (TypeName [Name Nothing "interval"]) val
->     mkIt s val (Just (a,b)) = pure $ IntervalLit s val a b
->     mkIt (Just {}) _val Nothing = fail "cannot use sign without interval qualifier"
-
-== typed literal, app, special, aggregate, window, iden
-
-All of these start with identifiers (some of the special functions
-start with reserved keywords).
-
-they are all variations on suffixes on the basic identifier parser
-
-The windows is a suffix on the app parser
-
-=== iden prefix term
-
-all the scalar expressions which start with an identifier
-
-(todo: really put all of them here instead of just some of them)
-
-> idenExpr :: Parser ScalarExpr
-> idenExpr =
->     -- todo: work out how to left factor this
->     try (TypedLit <$> typeName <*> singleQuotesOnlyStringTok)
->     <|> (names <**> option Iden app)
->     <|> keywordFunctionOrIden
->   where
->     -- special cases for keywords that can be parsed as an iden or app
->     keywordFunctionOrIden = try $ do
->         x <- unquotedIdentifierTok [] Nothing
->         d <- getState
->         let i = map toLower x `elem` diIdentifierKeywords d
->             a = map toLower x `elem` diAppKeywords d
->         case () of
->             _  | i && a -> pure [Name Nothing x] <**> option Iden app
->                | i -> pure (Iden [Name Nothing x])
->                | a -> pure [Name Nothing x] <**> app
->                | otherwise -> fail ""
-
-
-=== special
-
-These are keyword operators which don't look like normal prefix,
-postfix or infix binary operators. They mostly look like function
-application but with keywords in the argument list instead of commas
-to separate the arguments.
-
-the special op keywords
-parse an operator which is
-operatorname(firstArg keyword0 arg0 keyword1 arg1 etc.)
-
-> data SpecialOpKFirstArg = SOKNone
->                         | SOKOptional
->                         | SOKMandatory
-
-> specialOpK :: String -- name of the operator
->            -> SpecialOpKFirstArg -- has a first arg without a keyword
->            -> [(String,Bool)] -- the other args with their keywords
->                               -- and whether they are optional
->            -> Parser ScalarExpr
-> specialOpK opName firstArg kws =
->     keyword_ opName >> do
->     void openParen
->     let pfa = do
->               e <- scalarExpr
->               -- check we haven't parsed the first
->               -- keyword as an identifier
->               case (e,kws) of
->                   (Iden [Name Nothing i], (k,_):_)
->                       | map toLower i == k ->
->                           fail $ "cannot use keyword here: " ++ i
->                   _ -> return ()
->               pure e
->     fa <- case firstArg of
->          SOKNone -> pure Nothing
->          SOKOptional -> optionMaybe (try pfa)
->          SOKMandatory -> Just <$> pfa
->     as <- mapM parseArg kws
->     void closeParen
->     pure $ SpecialOpK [Name Nothing opName] fa $ catMaybes as
->   where
->     parseArg (nm,mand) =
->         let p = keyword_ nm >> scalarExpr
->         in fmap (nm,) <$> if mand
->                           then Just <$> p
->                           else optionMaybe (try p)
-
-The actual operators:
-
-EXTRACT( date_part FROM expression )
-
-POSITION( string1 IN string2 )
-
-SUBSTRING(extraction_string FROM starting_position [FOR length]
-[COLLATE collation_name])
-
-CONVERT(char_value USING conversion_char_name)
-
-TRANSLATE(char_value USING translation_name)
-
-OVERLAY(string PLACING embedded_string FROM start
-[FOR length])
-
-TRIM( [ [{LEADING | TRAILING | BOTH}] [removal_char] FROM ]
-target_string
-[COLLATE collation_name] )
-
-> specialOpKs :: Parser ScalarExpr
-> specialOpKs = choice $ map try
->     [extract, position, substring, convert, translate, overlay, trim]
-
-> extract :: Parser ScalarExpr
-> extract = specialOpK "extract" SOKMandatory [("from", True)]
-
-> position :: Parser ScalarExpr
-> position = specialOpK "position" SOKMandatory [("in", True)]
-
-strictly speaking, the substring must have at least one of from and
-for, but the parser doens't enforce this
-
-> substring :: Parser ScalarExpr
-> substring = specialOpK "substring" SOKMandatory
->                 [("from", False),("for", False)]
-
-> convert :: Parser ScalarExpr
-> convert = specialOpK "convert" SOKMandatory [("using", True)]
-
-
-> translate :: Parser ScalarExpr
-> translate = specialOpK "translate" SOKMandatory [("using", True)]
-
-> overlay :: Parser ScalarExpr
-> overlay = specialOpK "overlay" SOKMandatory
->                 [("placing", True),("from", True),("for", False)]
-
-trim is too different because of the optional char, so a custom parser
-the both ' ' is filled in as the default if either parts are missing
-in the source
-
-> trim :: Parser ScalarExpr
-> trim =
->     keyword "trim" >>
->     parens (mkTrim
->             <$> option "both" sides
->             <*> option " " singleQuotesOnlyStringTok
->             <*> (keyword_ "from" *> scalarExpr))
->   where
->     sides = choice ["leading" <$ keyword_ "leading"
->                    ,"trailing" <$ keyword_ "trailing"
->                    ,"both" <$ keyword_ "both"]
->     mkTrim fa ch fr =
->       SpecialOpK [Name Nothing "trim"] Nothing
->           $ catMaybes [Just (fa,StringLit "'" "'" ch)
->                       ,Just ("from", fr)]
-
-=== app, aggregate, window
-
-This parses all these variations:
-normal function application with just a csv of scalar exprs
-aggregate variations (distinct, order by in parens, filter and where
-  suffixes)
-window apps (fn/agg followed by over)
-
-This code is also a little dense like the typename code because of
-left factoring, later they will even have to be partially combined
-together.
-
-> app :: Parser ([Name] -> ScalarExpr)
-> app =
->     openParen *> choice
->     [duplicates
->      <**> (commaSep1 scalarExpr
->            <**> (((option [] orderBy) <* closeParen)
->                  <**> (optionMaybe afilter <$$$$$> AggregateApp)))
->      -- separate cases with no all or distinct which must have at
->      -- least one scalar expr
->     ,commaSep1 scalarExpr
->      <**> choice
->           [closeParen *> choice
->                          [window
->                          ,withinGroup
->                          ,(Just <$> afilter) <$$$> aggAppWithoutDupeOrd
->                          ,pure (flip App)]
->           ,orderBy <* closeParen
->            <**> (optionMaybe afilter <$$$$> aggAppWithoutDupe)]
->      -- no scalarExprs: duplicates and order by not allowed
->     ,([] <$ closeParen) <**> option (flip App) (window <|> withinGroup)
->     ]
->   where
->     aggAppWithoutDupeOrd n es f = AggregateApp n SQDefault es [] f
->     aggAppWithoutDupe n = AggregateApp n SQDefault
-
-> afilter :: Parser ScalarExpr
-> afilter = keyword_ "filter" *> parens (keyword_ "where" *> scalarExpr)
-
-> withinGroup :: Parser ([ScalarExpr] -> [Name] -> ScalarExpr)
-> withinGroup =
->     (keywords_ ["within", "group"] *> parens orderBy) <$$$> AggregateAppGroup
-
-==== window
-
-parse a window call as a suffix of a regular function call
-this looks like this:
-functionname(args) over ([partition by ids] [order by orderitems])
-
-No support for explicit frames yet.
-
-TODO: add window support for other aggregate variations, needs some
-changes to the syntax also
-
-> window :: Parser ([ScalarExpr] -> [Name] -> ScalarExpr)
-> window =
->   keyword_ "over" *> openParen *> option [] partitionBy
->   <**> (option [] orderBy
->         <**> (((optionMaybe frameClause) <* closeParen) <$$$$$> WindowApp))
->   where
->     partitionBy = keywords_ ["partition","by"] *> commaSep1 scalarExpr
->     frameClause =
->         frameRowsRange -- TODO: this 'and' could be an issue
->         <**> (choice [(keyword_ "between" *> frameLimit True)
->                       <**> ((keyword_ "and" *> frameLimit True)
->                             <$$$> FrameBetween)
->                       -- maybe this should still use a b expression
->                       -- for consistency
->                      ,frameLimit False <**> pure (flip FrameFrom)])
->     frameRowsRange = FrameRows <$ keyword_ "rows"
->                      <|> FrameRange <$ keyword_ "range"
->     frameLimit useB =
->         choice
->         [Current <$ keywords_ ["current", "row"]
->          -- todo: create an automatic left factor for stuff like this
->         ,keyword_ "unbounded" *>
->          choice [UnboundedPreceding <$ keyword_ "preceding"
->                 ,UnboundedFollowing <$ keyword_ "following"]
->         ,(if useB then scalarExprB else scalarExpr)
->          <**> (Preceding <$ keyword_ "preceding"
->                <|> Following <$ keyword_ "following")
->         ]
-
-== suffixes
-
-These are all generic suffixes on any scalar expr
-
-=== in
-
-in: two variations:
-a in (expr0, expr1, ...)
-a in (queryexpr)
-
-> inSuffix :: Parser (ScalarExpr -> ScalarExpr)
-> inSuffix =
->     mkIn <$> inty
->          <*> parens (choice
->                      [InQueryExpr <$> queryExpr
->                      ,InList <$> commaSep1 scalarExpr])
->   where
->     inty = choice [True <$ keyword_ "in"
->                   ,False <$ keywords_ ["not","in"]]
->     mkIn i v = \e -> In i e v
-
-=== between
-
-between:
-expr between expr and expr
-
-There is a complication when parsing between - when parsing the second
-expression it is ambiguous when you hit an 'and' whether it is a
-binary operator or part of the between. This code follows what
-postgres does, which might be standard across SQL implementations,
-which is that you can't have a binary and operator in the middle
-expression in a between unless it is wrapped in parens. The 'bExpr
-parsing' is used to create alternative scalar expression parser which
-is identical to the normal one expect it doesn't recognise the binary
-and operator. This is the call to scalarExprB.
-
-> betweenSuffix :: Parser (ScalarExpr -> ScalarExpr)
-> betweenSuffix =
->     makeOp <$> Name Nothing <$> opName
->            <*> scalarExprB
->            <*> (keyword_ "and" *> scalarExprB)
->   where
->     opName = choice
->              ["between" <$ keyword_ "between"
->              ,"not between" <$ try (keywords_ ["not","between"])]
->     makeOp n b c = \a -> SpecialOp [n] [a,b,c]
-
-=== quantified comparison
-
-a = any (select * from t)
-
-> quantifiedComparisonSuffix :: Parser (ScalarExpr -> ScalarExpr)
-> quantifiedComparisonSuffix = do
->     c <- comp
->     cq <- compQuan
->     q <- parens queryExpr
->     pure $ \v -> QuantifiedComparison v [c] cq q
->   where
->     comp = Name Nothing <$> choice (map symbol
->            ["=", "<>", "<=", "<", ">", ">="])
->     compQuan = choice
->                [CPAny <$ keyword_ "any"
->                ,CPSome <$ keyword_ "some"
->                ,CPAll <$ keyword_ "all"]
-
-=== match
-
-a match (select a from t)
-
-> matchPredicateSuffix :: Parser (ScalarExpr -> ScalarExpr)
-> matchPredicateSuffix = do
->     keyword_ "match"
->     u <- option False (True <$ keyword_ "unique")
->     q <- parens queryExpr
->     pure $ \v -> Match v u q
-
-=== array subscript
-
-> arraySuffix :: Parser (ScalarExpr -> ScalarExpr)
-> arraySuffix = do
->     es <- brackets (commaSep scalarExpr)
->     pure $ \v -> Array v es
-
-=== escape
-
-It is going to be really difficult to support an arbitrary character
-for the escape now there is a separate lexer ...
-
-TODO: this needs fixing. Escape is only part of other nodes, and not a
-separate suffix.
-
-> {-escapeSuffix :: Parser (ScalarExpr -> ScalarExpr)
-> escapeSuffix = do
->     ctor <- choice
->             [Escape <$ keyword_ "escape"
->             ,UEscape <$ keyword_ "uescape"]
->     c <- escapeChar
->     pure $ \v -> ctor v c
->   where
->     escapeChar :: Parser Char
->     escapeChar = (identifierTok [] Nothing <|> symbolTok Nothing) >>= oneOnly
->     oneOnly :: String -> Parser Char
->     oneOnly c = case c of
->                    [c'] -> return c'
->                    _ -> fail "escape char must be single char"
-> -}
-
-=== collate
-
-> collateSuffix:: Parser (ScalarExpr -> ScalarExpr)
-> collateSuffix = do
->     keyword_ "collate"
->     i <- names
->     pure $ \v -> Collate v i
-
-== odbc syntax
-
-the parser supports three kinds of odbc syntax, two of which are
-scalar expressions (the other is a variation on joins)
-
-
-> odbcExpr :: Parser ScalarExpr
-> odbcExpr = between (symbol "{") (symbol "}")
->            (odbcTimeLit <|> odbcFunc)
->   where
->     odbcTimeLit =
->         OdbcLiteral <$> choice [OLDate <$ keyword "d"
->                                ,OLTime <$ keyword "t"
->                                ,OLTimestamp <$ keyword "ts"]
->                     <*> singleQuotesOnlyStringTok
->     -- todo: this parser is too general, the expr part
->     -- should be only a function call (from a whitelist of functions)
->     -- or the extract operator
->     odbcFunc = OdbcFunc <$> (keyword "fn" *> scalarExpr)
-
-==  operators
-
-The 'regular' operators in this parsing and in the abstract syntax are
-unary prefix, unary postfix and binary infix operators. The operators
-can be symbols (a + b), single keywords (a and b) or multiple keywords
-(a is similar to b).
-
-TODO: carefully review the precedences and associativities.
-
-TODO: to fix the parsing completely, I think will need to parse
-without precedence and associativity and fix up afterwards, since SQL
-syntax is way too messy. It might be possible to avoid this if we
-wanted to avoid extensibility and to not be concerned with parse error
-messages, but both of these are too important.
-
-> opTable :: Bool -> [[E.Operator [Token] ParseState Identity ScalarExpr]]
-> opTable bExpr =
->         [-- parse match and quantified comparisons as postfix ops
->           -- todo: left factor the quantified comparison with regular
->           -- binary comparison, somehow
->          [E.Postfix $ try quantifiedComparisonSuffix
->          ,E.Postfix matchPredicateSuffix
->          ]
-
->         ,[binarySym "." E.AssocLeft]
-
->         ,[postfix' arraySuffix
->          ,postfix' collateSuffix]
-
->         ,[prefixSym "+", prefixSym "-"]
-
->         ,[binarySym "^" E.AssocLeft]
-
->         ,[binarySym "*" E.AssocLeft
->          ,binarySym "/" E.AssocLeft
->          ,binarySym "%" E.AssocLeft]
-
->         ,[binarySym "+" E.AssocLeft
->          ,binarySym "-" E.AssocLeft]
-
->         ,[binarySym "||" E.AssocRight
->          ,prefixSym "~"
->          ,binarySym "&" E.AssocRight
->          ,binarySym "|" E.AssocRight]
-
->         ,[binaryKeyword "overlaps" E.AssocNone]
-
->         ,[binaryKeyword "like" E.AssocNone
->          -- have to use try with inSuffix because of a conflict
->          -- with 'in' in position function, and not between
->          -- between also has a try in it to deal with 'not'
->          -- ambiguity
->          ,E.Postfix $ try inSuffix
->          ,E.Postfix betweenSuffix]
->          -- todo: figure out where to put the try?
->          ++ [binaryKeywords $ makeKeywordTree
->              ["not like"
->              ,"is similar to"
->              ,"is not similar to"]]
->          ++ [multisetBinOp]
-
->         ,[binarySym "<" E.AssocNone
->          ,binarySym ">" E.AssocNone
->          ,binarySym ">=" E.AssocNone
->          ,binarySym "<=" E.AssocNone
->          ,binarySym "!=" E.AssocRight
->          ,binarySym "<>" E.AssocRight
->          ,binarySym "=" E.AssocRight]
-
->         ,[postfixKeywords $ makeKeywordTree
->              ["is null"
->              ,"is not null"
->              ,"is true"
->              ,"is not true"
->              ,"is false"
->              ,"is not false"
->              ,"is unknown"
->              ,"is not unknown"]]
->          ++ [binaryKeywords $ makeKeywordTree
->              ["is distinct from"
->              ,"is not distinct from"]]
-
->         ,[prefixKeyword "not"]
-
->         ,if bExpr then [] else [binaryKeyword "and" E.AssocLeft]
-
->         ,[binaryKeyword "or" E.AssocLeft]
-
->        ]
->   where
->     binarySym nm assoc = binary (symbol_ nm) nm assoc
->     binaryKeyword nm assoc = binary (keyword_ nm) nm assoc
->     binaryKeywords p =
->         E.Infix (do
->                  o <- try p
->                  pure (\a b -> BinOp a [Name Nothing $ unwords o] b))
->             E.AssocNone
->     postfixKeywords p =
->       postfix' $ do
->           o <- try p
->           pure $ PostfixOp [Name Nothing $ unwords o]
->     binary p nm assoc =
->       E.Infix (p >> pure (\a b -> BinOp a [Name Nothing nm] b)) assoc
->     multisetBinOp = E.Infix (do
->         keyword_ "multiset"
->         o <- choice [Union <$ keyword_ "union"
->                     ,Intersect <$ keyword_ "intersect"
->                     ,Except <$ keyword_ "except"]
->         d <- option SQDefault duplicates
->         pure (\a b -> MultisetBinOp a o d b))
->           E.AssocLeft
->     prefixKeyword nm = prefix (keyword_ nm) nm
->     prefixSym nm = prefix (symbol_ nm) nm
->     prefix p nm = prefix' (p >> pure (PrefixOp [Name Nothing nm]))
->     -- hack from here
->     -- http://stackoverflow.com/questions/10475337/parsec-expr-repeated-prefix-postfix-operator-not-supported
->     -- not implemented properly yet
->     -- I don't think this will be enough for all cases
->     -- at least it works for 'not not a'
->     -- ok: "x is not true is not true"
->     -- no work: "x is not true is not null"
->     prefix'  p = E.Prefix  . chainl1 p $ pure       (.)
->     postfix' p = E.Postfix . chainl1 p $ pure (flip (.))
-
-== scalar expression top level
-
-This parses most of the scalar exprs.The order of the parsers and use
-of try is carefully done to make everything work. It is a little
-fragile and could at least do with some heavy explanation. Update: the
-'try's have migrated into the individual parsers, they still need
-documenting/fixing.
-
-> scalarExpr :: Parser ScalarExpr
-> scalarExpr = E.buildExpressionParser (opTable False) term
-
-> term :: Parser ScalarExpr
-> term = choice [simpleLiteral
->               ,parameter
->               ,positionalArg
->               ,star
->               ,parensExpr
->               ,caseExpr
->               ,cast
->               ,arrayCtor
->               ,multisetCtor
->               ,nextValueFor
->               ,subquery
->               ,intervalLit
->               ,specialOpKs
->               ,idenExpr
->               ,odbcExpr]
->        <?> "scalar expression"
-
-expose the b expression for window frame clause range between
-
-> scalarExprB :: Parser ScalarExpr
-> scalarExprB = E.buildExpressionParser (opTable True) term
-
-== helper parsers
-
-This is used in interval literals and in interval type names.
-
-> intervalQualifier :: Parser (IntervalTypeField,Maybe IntervalTypeField)
-> intervalQualifier =
->     (,) <$> intervalField
->         <*> optionMaybe (keyword_ "to" *> intervalField)
->   where
->     intervalField =
->         Itf
->         <$> datetimeField
->         <*> optionMaybe
->             (parens ((,) <$> unsignedInteger
->                          <*> optionMaybe (comma *> unsignedInteger)))
-
-TODO: use datetime field in extract also
-use a data type for the datetime field?
-
-> datetimeField :: Parser String
-> datetimeField = choice (map keyword ["year","month","day"
->                                     ,"hour","minute","second"])
->                 <?> "datetime field"
-
-This is used in multiset operations (scalar expr), selects (query expr)
-and set operations (query expr).
-
-> duplicates :: Parser SetQuantifier
-> duplicates =
->     choice [All <$ keyword_ "all"
->            ,Distinct <$ keyword "distinct"]
-
--------------------------------------------------
-
-= query expressions
-
-== select lists
-
-> selectItem :: Parser (ScalarExpr,Maybe Name)
-> selectItem = (,) <$> scalarExpr <*> optionMaybe als
->   where als = optional (keyword_ "as") *> name
-
-> selectList :: Parser [(ScalarExpr,Maybe Name)]
-> selectList = commaSep1 selectItem
-
-== from
-
-Here is the rough grammar for joins
-
-tref
-(cross | [natural] ([inner] | (left | right | full) [outer])) join
-tref
-[on expr | using (...)]
-
-TODO: either use explicit 'operator precedence' parsers or build
-expression parser for the 'tref operators' such as joins, lateral,
-aliases.
-
-> from :: Parser [TableRef]
-> from = keyword_ "from" *> commaSep1 tref
->   where
->     -- TODO: use P (a->) for the join tref suffix
->     -- chainl or buildexpressionparser
->     tref = nonJoinTref >>= optionSuffix joinTrefSuffix
->     nonJoinTref = choice
->         [parens $ choice
->              [TRQueryExpr <$> queryExpr
->              ,TRParens <$> tref]
->         ,TRLateral <$> (keyword_ "lateral"
->                         *> nonJoinTref)
->         ,do
->          n <- names
->          choice [TRFunction n
->                  <$> parens (commaSep scalarExpr)
->                 ,pure $ TRSimple n]
->          -- todo: I think you can only have outer joins inside the oj,
->          -- not sure.
->         ,TROdbc <$> (symbol "{" *> keyword_ "oj" *> tref <* symbol "}")
->         ] <??> aliasSuffix
->     aliasSuffix = fromAlias <$$> TRAlias
->     joinTrefSuffix t =
->         (TRJoin t <$> option False (True <$ keyword_ "natural")
->                   <*> joinType
->                   <*> nonJoinTref
->                   <*> optionMaybe joinCondition)
->         >>= optionSuffix joinTrefSuffix
-
-TODO: factor the join stuff to produce better error messages (and make
-it more readable)
-
-> joinType :: Parser JoinType
-> joinType = choice
->     [JCross <$ keyword_ "cross" <* keyword_ "join"
->     ,JInner <$ keyword_ "inner" <* keyword_ "join"
->     ,JLeft <$ keyword_ "left"
->            <* optional (keyword_ "outer")
->            <* keyword_ "join"
->     ,JRight <$ keyword_ "right"
->             <* optional (keyword_ "outer")
->             <* keyword_ "join"
->     ,JFull <$ keyword_ "full"
->            <* optional (keyword_ "outer")
->            <* keyword_ "join"
->     ,JInner <$ keyword_ "join"]
-
-> joinCondition :: Parser JoinCondition
-> joinCondition = choice
->     [keyword_ "on" >> JoinOn <$> scalarExpr
->     ,keyword_ "using" >> JoinUsing <$> parens (commaSep1 name)]
-
-> fromAlias :: Parser Alias
-> fromAlias = Alias <$> tableAlias <*> columnAliases
->   where
->     tableAlias = optional (keyword_ "as") *> name
->     columnAliases = optionMaybe $ parens $ commaSep1 name
-
-== simple other parts
-
-Parsers for where, group by, having, order by and limit, which are
-pretty trivial.
-
-> whereClause :: Parser ScalarExpr
-> whereClause = keyword_ "where" *> scalarExpr
-
-> groupByClause :: Parser [GroupingExpr]
-> groupByClause = keywords_ ["group","by"] *> commaSep1 groupingExpression
->   where
->     groupingExpression = choice
->       [keyword_ "cube" >>
->        Cube <$> parens (commaSep groupingExpression)
->       ,keyword_ "rollup" >>
->        Rollup <$> parens (commaSep groupingExpression)
->       ,GroupingParens <$> parens (commaSep groupingExpression)
->       ,keywords_ ["grouping", "sets"] >>
->        GroupingSets <$> parens (commaSep groupingExpression)
->       ,SimpleGroup <$> scalarExpr
->       ]
-
-> having :: Parser ScalarExpr
-> having = keyword_ "having" *> scalarExpr
-
-> orderBy :: Parser [SortSpec]
-> orderBy = keywords_ ["order","by"] *> commaSep1 ob
->   where
->     ob = SortSpec
->          <$> scalarExpr
->          <*> option DirDefault (choice [Asc <$ keyword_ "asc"
->                                        ,Desc <$ keyword_ "desc"])
->          <*> option NullsOrderDefault
->              -- todo: left factor better
->              (keyword_ "nulls" >>
->                     choice [NullsFirst <$ keyword "first"
->                            ,NullsLast <$ keyword "last"])
-
-allows offset and fetch in either order
-+ postgresql offset without row(s) and limit instead of fetch also
-
-> offsetFetch :: Parser (Maybe ScalarExpr, Maybe ScalarExpr)
-> offsetFetch = permute ((,) <$?> (Nothing, Just <$> offset)
->                            <|?> (Nothing, Just <$> fetch))
-
-> offset :: Parser ScalarExpr
-> offset = keyword_ "offset" *> scalarExpr
->          <* option () (choice [keyword_ "rows"
->                               ,keyword_ "row"])
-
-> fetch :: Parser ScalarExpr
-> fetch = fetchFirst <|> limit
->   where
->     fetchFirst = guardDialect diFetchFirst
->                  *> fs *> scalarExpr <* ro
->     fs = makeKeywordTree ["fetch first", "fetch next"]
->     ro = makeKeywordTree ["rows only", "row only"]
->     -- todo: not in ansi sql dialect
->     limit = guardDialect diLimit *>
->             keyword_ "limit" *> scalarExpr
-
-== common table expressions
-
-> with :: Parser QueryExpr
-> with = keyword_ "with" >>
->     With <$> option False (True <$ keyword_ "recursive")
->          <*> commaSep1 withQuery <*> queryExpr
->   where
->     withQuery = (,) <$> (withAlias <* keyword_ "as")
->                     <*> parens queryExpr
->     withAlias = Alias <$> name <*> columnAliases
->     columnAliases = optionMaybe $ parens $ commaSep1 name
-
-
-== query expression
-
-This parser parses any query expression variant: normal select, cte,
-and union, etc..
-
-> queryExpr :: Parser QueryExpr
-> queryExpr = choice
->     [with
->     ,chainr1 (choice [values,table, select]) setOp]
->   where
->     select = keyword_ "select" >>
->         mkSelect
->         <$> option SQDefault duplicates
->         <*> selectList
->         <*> optionMaybe tableExpression
->     mkSelect d sl Nothing =
->         makeSelect{qeSetQuantifier = d, qeSelectList = sl}
->     mkSelect d sl (Just (TableExpression f w g h od ofs fe)) =
->         Select d sl f w g h od ofs fe
->     values = keyword_ "values"
->              >> Values <$> commaSep (parens (commaSep scalarExpr))
->     table = keyword_ "table" >> Table <$> names
-
-local data type to help with parsing the bit after the select list,
-called 'table expression' in the ansi sql grammar. Maybe this should
-be in the public syntax?
-
-> data TableExpression
->     = TableExpression
->       {_teFrom :: [TableRef]
->       ,_teWhere :: Maybe ScalarExpr
->       ,_teGroupBy :: [GroupingExpr]
->       ,_teHaving :: Maybe ScalarExpr
->       ,_teOrderBy :: [SortSpec]
->       ,_teOffset :: Maybe ScalarExpr
->       ,_teFetchFirst :: Maybe ScalarExpr}
-
-> tableExpression :: Parser TableExpression
-> tableExpression = mkTe <$> from
->                        <*> optionMaybe whereClause
->                        <*> option [] groupByClause
->                        <*> optionMaybe having
->                        <*> option [] orderBy
->                        <*> offsetFetch
->  where
->     mkTe f w g h od (ofs,fe) =
->         TableExpression f w g h od ofs fe
-
-> setOp :: Parser (QueryExpr -> QueryExpr -> QueryExpr)
-> setOp = cq
->         <$> setOpK
->         <*> option SQDefault duplicates
->         <*> corr
->   where
->     cq o d c q0 q1 = QueryExprSetOp q0 o d c q1
->     setOpK = choice [Union <$ keyword_ "union"
->                     ,Intersect <$ keyword_ "intersect"
->                     ,Except <$ keyword_ "except"]
->             <?> "set operator"
->     corr = option Respectively (Corresponding <$ keyword_ "corresponding")
-
-
-wrapper for query expr which ignores optional trailing semicolon.
-
-TODO: change style
-
-> topLevelQueryExpr :: Parser QueryExpr
-> topLevelQueryExpr = queryExpr <??> (id <$ semi)
-
-> topLevelStatement :: Parser Statement
-> topLevelStatement = statement <??> (id <$ semi)
-
--------------------------
-
-= Statements
-
-> statement :: Parser Statement
-> statement = choice
->     [keyword_ "create" *> choice [createSchema
->                                  ,createTable
->                                  ,createView
->                                  ,createDomain
->                                  ,createSequence
->                                  ,createRole
->                                  ,createAssertion]
->     ,keyword_ "alter" *> choice [alterTable
->                                 ,alterDomain
->                                 ,alterSequence]
->     ,keyword_ "drop" *> choice [dropSchema
->                                ,dropTable
->                                ,dropView
->                                ,dropDomain
->                                ,dropSequence
->                                ,dropRole
->                                ,dropAssertion]
->     ,delete
->     ,truncateSt
->     ,insert
->     ,update
->     ,startTransaction
->     ,savepoint
->     ,releaseSavepoint
->     ,commit
->     ,rollback
->     ,grant
->     ,revoke
->     ,SelectStatement <$> queryExpr
->     ]
-
-> createSchema :: Parser Statement
-> createSchema = keyword_ "schema" >>
->     CreateSchema <$> names
-
-> createTable :: Parser Statement
-> createTable = keyword_ "table" >>
->     CreateTable
->     <$> names
->     -- todo: is this order mandatory or is it a perm?
->     <*> parens (commaSep1 (uncurry TableConstraintDef <$> tableConstraintDef
->                            <|> TableColumnDef <$> columnDef))
-
-> columnDef :: Parser ColumnDef
-> columnDef = ColumnDef <$> name <*> typeName
->             <*> optionMaybe defaultClause
->             <*> option [] (many1 colConstraintDef)
->   where
->     defaultClause = choice [
->         keyword_ "default" >>
->         DefaultClause <$> scalarExpr
->         -- todo: left factor
->        ,try (keywords_ ["generated","always","as"] >>
->              GenerationClause <$> parens scalarExpr)
->        ,keyword_ "generated" >>
->         IdentityColumnSpec
->         <$> (GeneratedAlways <$ keyword_ "always"
->              <|> GeneratedByDefault <$ keywords_ ["by", "default"])
->         <*> (keywords_ ["as", "identity"] *>
->              option [] (parens sequenceGeneratorOptions))
->        ]
-
-> tableConstraintDef :: Parser (Maybe [Name], TableConstraint)
-> tableConstraintDef =
->     (,)
->     <$> (optionMaybe (keyword_ "constraint" *> names))
->     <*> (unique <|> primaryKey <|> check <|> references)
->   where
->     unique = keyword_ "unique" >>
->         TableUniqueConstraint <$> parens (commaSep1 name)
->     primaryKey = keywords_ ["primary", "key"] >>
->         TablePrimaryKeyConstraint <$> parens (commaSep1 name)
->     check = keyword_ "check" >> TableCheckConstraint <$> parens scalarExpr
->     references = keywords_ ["foreign", "key"] >>
->         (\cs ft ftcs m (u,d) -> TableReferencesConstraint cs ft ftcs m u d)
->         <$> parens (commaSep1 name)
->         <*> (keyword_ "references" *> names)
->         <*> optionMaybe (parens $ commaSep1 name)
->         <*> refMatch
->         <*> refActions
-
-> refMatch :: Parser ReferenceMatch
-> refMatch = option DefaultReferenceMatch
->             (keyword_ "match" *>
->              choice [MatchFull <$ keyword_ "full"
->                     ,MatchPartial <$ keyword_ "partial"
->                     ,MatchSimple <$ keyword_ "simple"])
-> refActions :: Parser (ReferentialAction,ReferentialAction)
-> refActions = permute ((,) <$?> (DefaultReferentialAction, onUpdate)
->                           <|?> (DefaultReferentialAction, onDelete))
->   where
->     -- todo: left factor?
->     onUpdate = try (keywords_ ["on", "update"]) *> referentialAction
->     onDelete = try (keywords_ ["on", "delete"]) *> referentialAction
->     referentialAction = choice [
->          RefCascade <$ keyword_ "cascade"
->          -- todo: left factor?
->         ,RefSetNull <$ try (keywords_ ["set", "null"])
->         ,RefSetDefault <$ try (keywords_ ["set", "default"])
->         ,RefRestrict <$ keyword_ "restrict"
->         ,RefNoAction <$ keywords_ ["no", "action"]]
-
-> colConstraintDef :: Parser ColConstraintDef
-> colConstraintDef =
->     ColConstraintDef
->     <$> (optionMaybe (keyword_ "constraint" *> names))
->     <*> (notNull <|> unique <|> primaryKey <|> check <|> references)
->   where
->     notNull = ColNotNullConstraint <$ keywords_ ["not", "null"]
->     unique = ColUniqueConstraint <$ keyword_ "unique"
->     primaryKey = ColPrimaryKeyConstraint <$ keywords_ ["primary", "key"]
->     check = keyword_ "check" >> ColCheckConstraint <$> parens scalarExpr
->     references = keyword_ "references" >>
->         (\t c m (ou,od) -> ColReferencesConstraint t c m ou od)
->         <$> names
->         <*> optionMaybe (parens name)
->         <*> refMatch
->         <*> refActions
-
-slightly hacky parser for signed integers
-
-> signedInteger :: Parser Integer
-> signedInteger =
->     (*) <$> option 1 (1 <$ symbol "+" <|> (-1) <$ symbol "-")
->     <*> unsignedInteger
-
-> sequenceGeneratorOptions :: Parser [SequenceGeneratorOption]
-> sequenceGeneratorOptions =
->          -- todo: could try to combine exclusive options
->          -- such as cycle and nocycle
->          -- sort out options which are sometimes not allowed
->          -- as datatype, and restart with
->     permute ((\a b c d e f g h j k -> catMaybes [a,b,c,d,e,f,g,h,j,k])
->                   <$?> nj startWith
->                   <|?> nj dataType
->                   <|?> nj restart
->                   <|?> nj incrementBy
->                   <|?> nj maxValue
->                   <|?> nj noMaxValue
->                   <|?> nj minValue
->                   <|?> nj noMinValue
->                   <|?> nj scycle
->                   <|?> nj noCycle
->                  )
->   where
->     nj p = (Nothing,Just <$> p)
->     startWith = keywords_ ["start", "with"] >>
->                 SGOStartWith <$> signedInteger
->     dataType = keyword_ "as" >>
->                SGODataType <$> typeName
->     restart = keyword_ "restart" >>
->               SGORestart <$> optionMaybe (keyword_ "with" *> signedInteger)
->     incrementBy = keywords_ ["increment", "by"] >>
->                 SGOIncrementBy <$> signedInteger
->     maxValue = keyword_ "maxvalue" >>
->                 SGOMaxValue <$> signedInteger
->     noMaxValue = SGONoMaxValue <$ try (keywords_ ["no","maxvalue"])
->     minValue = keyword_ "minvalue" >>
->                 SGOMinValue <$> signedInteger
->     noMinValue = SGONoMinValue <$ try (keywords_ ["no","minvalue"])
->     scycle = SGOCycle <$ keyword_ "cycle"
->     noCycle = SGONoCycle <$ try (keywords_ ["no","cycle"])
-
-
-> alterTable :: Parser Statement
-> alterTable = keyword_ "table" >>
->     -- the choices have been ordered so that it works
->     AlterTable <$> names <*> choice [addConstraint
->                                     ,dropConstraint
->                                     ,addColumnDef
->                                     ,alterColumn
->                                     ,dropColumn
->                                     ]
->   where
->     addColumnDef = try (keyword_ "add"
->                         *> optional (keyword_ "column")) >>
->                    AddColumnDef <$> columnDef
->     alterColumn = keyword_ "alter" >> optional (keyword_ "column") >>
->                   name <**> choice [setDefault
->                                    ,dropDefault
->                                    ,setNotNull
->                                    ,dropNotNull
->                                    ,setDataType]
->     setDefault :: Parser (Name -> AlterTableAction)
->     -- todo: left factor
->     setDefault = try (keywords_ ["set","default"]) >>
->                  scalarExpr <$$> AlterColumnSetDefault
->     dropDefault = AlterColumnDropDefault <$ try (keywords_ ["drop","default"])
->     setNotNull = AlterColumnSetNotNull <$ try (keywords_ ["set","not","null"])
->     dropNotNull = AlterColumnDropNotNull <$ try (keywords_ ["drop","not","null"])
->     setDataType = try (keywords_ ["set","data","type"]) >>
->                   typeName <$$> AlterColumnSetDataType
->     dropColumn = try (keyword_ "drop" *> optional (keyword_ "column")) >>
->                  DropColumn <$> name <*> dropBehaviour
->     -- todo: left factor, this try is especially bad
->     addConstraint = try (keyword_ "add" >>
->         uncurry AddTableConstraintDef <$> tableConstraintDef)
->     dropConstraint = try (keywords_ ["drop","constraint"]) >>
->         DropTableConstraintDef <$> names <*> dropBehaviour
-
-
-> dropSchema :: Parser Statement
-> dropSchema = keyword_ "schema" >>
->     DropSchema <$> names <*> dropBehaviour
-
-> dropTable :: Parser Statement
-> dropTable = keyword_ "table" >>
->     DropTable <$> names <*> dropBehaviour
-
-> createView :: Parser Statement
-> createView =
->     CreateView
->     <$> (option False (True <$ keyword_ "recursive") <* keyword_ "view")
->     <*> names
->     <*> optionMaybe (parens (commaSep1 name))
->     <*> (keyword_ "as" *> queryExpr)
->     <*> optionMaybe (choice [
->             -- todo: left factor
->             DefaultCheckOption <$ try (keywords_ ["with", "check", "option"])
->            ,CascadedCheckOption <$ try (keywords_ ["with", "cascaded", "check", "option"])
->            ,LocalCheckOption <$ try (keywords_ ["with", "local", "check", "option"])
->             ])
-
-> dropView :: Parser Statement
-> dropView = keyword_ "view" >>
->     DropView <$> names <*> dropBehaviour
-
-> createDomain :: Parser Statement
-> createDomain = keyword_ "domain" >>
->     CreateDomain
->     <$> names
->     <*> (optional (keyword_ "as") *> typeName)
->     <*> optionMaybe (keyword_ "default" *> scalarExpr)
->     <*> many con
->   where
->     con = (,) <$> optionMaybe (keyword_ "constraint" *> names)
->           <*> (keyword_ "check" *> parens scalarExpr)
-
-> alterDomain :: Parser Statement
-> alterDomain = keyword_ "domain" >>
->     AlterDomain
->     <$> names
->     <*> (setDefault <|> constraint
->          <|> (keyword_ "drop" *> (dropDefault <|> dropConstraint)))
->   where
->     setDefault = keywords_ ["set", "default"] >> ADSetDefault <$> scalarExpr
->     constraint = keyword_ "add" >>
->        ADAddConstraint
->        <$> optionMaybe (keyword_ "constraint" *> names)
->        <*> (keyword_ "check" *> parens scalarExpr)
->     dropDefault = ADDropDefault <$ keyword_ "default"
->     dropConstraint = keyword_ "constraint" >> ADDropConstraint <$> names
-
-> dropDomain :: Parser Statement
-> dropDomain = keyword_ "domain" >>
->     DropDomain <$> names <*> dropBehaviour
-
-> createSequence :: Parser Statement
-> createSequence = keyword_ "sequence" >>
->     CreateSequence
->     <$> names
->     <*> sequenceGeneratorOptions
-
-> alterSequence :: Parser Statement
-> alterSequence = keyword_ "sequence" >>
->     AlterSequence
->     <$> names
->     <*> sequenceGeneratorOptions
-
-> dropSequence :: Parser Statement
-> dropSequence = keyword_ "sequence" >>
->     DropSequence <$> names <*> dropBehaviour
-
-> createAssertion :: Parser Statement
-> createAssertion = keyword_ "assertion" >>
->     CreateAssertion
->     <$> names
->     <*> (keyword_ "check" *> parens scalarExpr)
-
-
-> dropAssertion :: Parser Statement
-> dropAssertion = keyword_ "assertion" >>
->     DropAssertion <$> names <*> dropBehaviour
-
------------------
-
-= dml
-
-> delete :: Parser Statement
-> delete = keywords_ ["delete","from"] >>
->     Delete
->     <$> names
->     <*> optionMaybe (optional (keyword_ "as") *> name)
->     <*> optionMaybe (keyword_ "where" *> scalarExpr)
-
-> truncateSt :: Parser Statement
-> truncateSt = keywords_ ["truncate", "table"] >>
->     Truncate
->     <$> names
->     <*> option DefaultIdentityRestart
->         (ContinueIdentity <$ keywords_ ["continue","identity"]
->          <|> RestartIdentity <$ keywords_ ["restart","identity"])
-
-> insert :: Parser Statement
-> insert = keywords_ ["insert", "into"] >>
->     Insert
->     <$> names
->     <*> optionMaybe (parens $ commaSep1 name)
->     <*> (DefaultInsertValues <$ keywords_ ["default", "values"]
->          <|> InsertQuery <$> queryExpr)
-
-> update :: Parser Statement
-> update = keywords_ ["update"] >>
->     Update
->     <$> names
->     <*> optionMaybe (optional (keyword_ "as") *> name)
->     <*> (keyword_ "set" *> commaSep1 setClause)
->     <*> optionMaybe (keyword_ "where" *> scalarExpr)
->   where
->     setClause = multipleSet <|> singleSet
->     multipleSet = SetMultiple
->                   <$> parens (commaSep1 names)
->                   <*> (symbol "=" *> parens (commaSep1 scalarExpr))
->     singleSet = Set
->                 <$> names
->                 <*> (symbol "=" *> scalarExpr)
-
-> dropBehaviour :: Parser DropBehaviour
-> dropBehaviour =
->     option DefaultDropBehaviour
->     (Restrict <$ keyword_ "restrict"
->     <|> Cascade <$ keyword_ "cascade")
-
------------------------------
-
-= transaction management
-
-> startTransaction :: Parser Statement
-> startTransaction = StartTransaction <$ keywords_ ["start","transaction"]
-
-> savepoint :: Parser Statement
-> savepoint = keyword_ "savepoint" >>
->     Savepoint <$> name
-
-> releaseSavepoint :: Parser Statement
-> releaseSavepoint = keywords_ ["release","savepoint"] >>
->     ReleaseSavepoint <$> name
-
-> commit :: Parser Statement
-> commit = Commit <$ keyword_ "commit" <* optional (keyword_ "work")
-
-> rollback :: Parser Statement
-> rollback = keyword_ "rollback" >> optional (keyword_ "work") >>
->     Rollback <$> optionMaybe (keywords_ ["to", "savepoint"] *> name)
-
-
-------------------------------
-
-= Access control
-
-TODO: fix try at the 'on'
-
-> grant :: Parser Statement
-> grant = keyword_ "grant" >> (try priv <|> role)
->   where
->     priv = GrantPrivilege
->            <$> commaSep privilegeAction
->            <*> (keyword_ "on" *> privilegeObject)
->            <*> (keyword_ "to" *> commaSep name)
->            <*> option WithoutGrantOption
->                (WithGrantOption <$ keywords_ ["with","grant","option"])
->     role = GrantRole
->            <$> commaSep name
->            <*> (keyword_ "to" *> commaSep name)
->            <*> option WithoutAdminOption
->                (WithAdminOption <$ keywords_ ["with","admin","option"])
-
-> createRole :: Parser Statement
-> createRole = keyword_ "role" >>
->     CreateRole <$> name
-
-> dropRole :: Parser Statement
-> dropRole = keyword_ "role" >>
->     DropRole <$> name
-
-TODO: fix try at the 'on'
-
-> revoke :: Parser Statement
-> revoke = keyword_ "revoke" >> (try priv <|> role)
->   where
->     priv = RevokePrivilege
->            <$> option NoGrantOptionFor
->                (GrantOptionFor <$ keywords_ ["grant","option","for"])
->            <*> commaSep privilegeAction
->            <*> (keyword_ "on" *> privilegeObject)
->            <*> (keyword_ "from" *> commaSep name)
->            <*> dropBehaviour
->     role = RevokeRole
->            <$> option NoAdminOptionFor
->                (AdminOptionFor <$ keywords_ ["admin","option", "for"])
->            <*> commaSep name
->            <*> (keyword_ "from" *> commaSep name)
->            <*> dropBehaviour
-
-> privilegeAction :: Parser PrivilegeAction
-> privilegeAction = choice
->     [PrivAll <$ keywords_ ["all","privileges"]
->     ,keyword_ "select" >>
->      PrivSelect <$> option [] (parens $ commaSep name)
->     ,PrivDelete <$ keyword_ "delete"
->     ,PrivUsage <$ keyword_ "usage"
->     ,PrivTrigger <$ keyword_ "trigger"
->     ,PrivExecute <$ keyword_ "execute"
->     ,keyword_ "insert" >>
->      PrivInsert <$> option [] (parens $ commaSep name)
->     ,keyword_ "update" >>
->      PrivUpdate <$> option [] (parens $ commaSep name)
->     ,keyword_ "references" >>
->      PrivReferences <$> option [] (parens $ commaSep name)
->     ]
-
-> privilegeObject :: Parser PrivilegeObject
-> privilegeObject = choice
->     [keyword_ "domain" >> PrivDomain <$> names
->     ,keyword_ "type" >> PrivType <$> names
->     ,keyword_ "sequence" >> PrivSequence <$> names
->     ,keywords_ ["specific","function"] >> PrivFunction <$> names
->     ,optional (keyword_ "table") >> PrivTable <$> names
->     ]
-
-
-----------------------------
-
-wrapper to parse a series of statements. They must be separated by
-semicolon, but for the last statement, the trailing semicolon is
-optional.
-
-TODO: change style
-
-> statements :: Parser [Statement]
-> statements = (:[]) <$> statement
->              >>= optionSuffix ((semi *>) . pure)
->              >>= optionSuffix (\p -> (p++) <$> statements)
-
-----------------------------------------------
-
-= multi keyword helper
-
-This helper is to help parsing multiple options of multiple keywords
-with similar prefixes, e.g. parsing 'is null' and 'is not null'.
-
-use to left factor/ improve:
-typed literal and general identifiers
-not like, not in, not between operators
-help with factoring keyword functions and other app-likes
-the join keyword sequences
-fetch first/next
-row/rows only
-
-There is probably a simpler way of doing this but I am a bit
-thick.
-
-> makeKeywordTree :: [String] -> Parser [String]
-> makeKeywordTree sets =
->     parseTrees (sort $ map words sets)
->   where
->     parseTrees :: [[String]] -> Parser [String]
->     parseTrees ws = do
->       let gs :: [[[String]]]
->           gs = groupBy ((==) `on` safeHead) ws
->       choice $ map parseGroup gs
->     parseGroup :: [[String]] -> Parser [String]
->     parseGroup l@((k:_):_) = do
->         keyword_ k
->         let tls = catMaybes $ map safeTail l
->             pr = (k:) <$> parseTrees tls
->         if (or $ map null tls)
->           then pr <|> pure [k]
->           else pr
->     parseGroup _ = guard False >> error "impossible"
->     safeHead (x:_) = Just x
->     safeHead [] = Nothing
->     safeTail (_:x) = Just x
->     safeTail [] = Nothing
-
-------------------------------------------------
-
-= lexing
-
-TODO: push checks into here:
-keyword blacklists
-unsigned integer match
-symbol matching
-keyword matching
-
-> stringTok :: Parser (String,String,String)
-> stringTok = mytoken (\tok ->
->     case tok of
->       L.SqlString s e t -> Just (s,e,t)
->       _ -> Nothing)
-
-> singleQuotesOnlyStringTok :: Parser String
-> singleQuotesOnlyStringTok = mytoken (\tok ->
->     case tok of
->       L.SqlString "'" "'" t -> Just t
->       _ -> Nothing)
-
-This is to support SQL strings where you can write
-'part of a string' ' another part'
-and it will parse as a single string
-
-It is only allowed when all the strings are quoted with ' atm.
-
-> stringTokExtend :: Parser (String,String,String)
-> stringTokExtend = do
->     (s,e,x) <- stringTok
->     choice [
->          do
->          guard (s == "'" && e == "'")
->          (s',e',y) <- stringTokExtend
->          guard (s' == "'" && e' == "'")
->          return $ (s,e,x ++ y)
->         ,return (s,e,x)
->         ]
-
-> hostParamTok :: Parser String
-> hostParamTok = mytoken (\tok ->
->     case tok of
->       L.PrefixedVariable c p -> Just (c:p)
->       _ -> Nothing)
-
-> positionalArgTok :: Parser Int
-> positionalArgTok = mytoken (\tok ->
->     case tok of
->       L.PositionalArg p -> Just p
->       _ -> Nothing)
-
-
-> sqlNumberTok :: Bool -> Parser String
-> sqlNumberTok intOnly = mytoken (\tok ->
->     case tok of
->       L.SqlNumber p | not intOnly || all isDigit p -> Just p
->       _ -> Nothing)
-
-
-> symbolTok :: Maybe String -> Parser String
-> symbolTok sym = mytoken (\tok ->
->     case (sym,tok) of
->       (Nothing, L.Symbol p) -> Just p
->       (Just s, L.Symbol p) | s == p -> Just p
->       _ -> Nothing)
-
-> identifierTok :: [String] -> Parser (Maybe (String,String), String)
-> identifierTok blackList = mytoken (\tok ->
->     case tok of
->       L.Identifier q@(Just {}) p -> Just (q,p)
->       L.Identifier q p | map toLower p `notElem` blackList -> Just (q,p)
->       _ -> Nothing)
-
-> unquotedIdentifierTok :: [String] -> Maybe String -> Parser String
-> unquotedIdentifierTok blackList kw = mytoken (\tok ->
->     case (kw,tok) of
->       (Nothing, L.Identifier Nothing p) | map toLower p `notElem` blackList -> Just p
->       (Just k, L.Identifier Nothing p) | k == map toLower p -> Just p
->       _ -> Nothing)
-
-> mytoken :: (L.Token -> Maybe a) -> Parser a
-> mytoken test = token showToken posToken testToken
->   where
->     showToken (_,tok)   = show tok
->     posToken  ((a,b,c),_)  = newPos a b c
->     testToken (_,tok)   = test tok
-
-> unsignedInteger :: Parser Integer
-> unsignedInteger = read <$> sqlNumberTok True <?> "natural number"
-
-todo: work out the symbol parsing better
-
-> symbol :: String -> Parser String
-> symbol s = symbolTok (Just s) <?> s
-
-> singleCharSymbol :: Char -> Parser Char
-> singleCharSymbol c = c <$ symbol [c]
-
-> questionMark :: Parser Char
-> questionMark = singleCharSymbol '?' <?> "question mark"
-
-> openParen :: Parser Char
-> openParen = singleCharSymbol '('
-
-> closeParen :: Parser Char
-> closeParen = singleCharSymbol ')'
-
-> openBracket :: Parser Char
-> openBracket = singleCharSymbol '['
-
-> closeBracket :: Parser Char
-> closeBracket = singleCharSymbol ']'
-
-
-> comma :: Parser Char
-> comma = singleCharSymbol ','
-
-> semi :: Parser Char
-> semi = singleCharSymbol ';'
-
-= helper functions
-
-> keyword :: String -> Parser String
-> keyword k = unquotedIdentifierTok [] (Just k) <?> k
-
-helper function to improve error messages
-
-> keywords_ :: [String] -> Parser ()
-> keywords_ ks = mapM_ keyword_ ks <?> intercalate " " ks
-
-
-> parens :: Parser a -> Parser a
-> parens = between openParen closeParen
-
-> brackets :: Parser a -> Parser a
-> brackets = between openBracket closeBracket
-
-> commaSep :: Parser a -> Parser [a]
-> commaSep = (`sepBy` comma)
-
-> keyword_ :: String -> Parser ()
-> keyword_ = void . keyword
-
-> symbol_ :: String -> Parser ()
-> symbol_ = void . symbol
-
-> commaSep1 :: Parser a -> Parser [a]
-> commaSep1 = (`sepBy1` comma)
-
-> blacklist :: Dialect -> [String]
-> blacklist d = diKeywords d
-
-These blacklisted names are mostly needed when we parse something with
-an optional alias, e.g. select a a from t. If we write select a from
-t, we have to make sure the from isn't parsed as an alias. I'm not
-sure what other places strictly need the blacklist, and in theory it
-could be tuned differently for each place the identifierString/
-identifier parsers are used to only blacklist the bare
-minimum. Something like this might be needed for dialect support, even
-if it is pretty silly to use a keyword as an unquoted identifier when
-there is a quoting syntax as well.
-
-The standard has a weird mix of reserved keywords and unreserved
-keywords (I'm not sure what exactly being an unreserved keyword
-means).
-
-The current approach tries to have everything which is a keyword only
-in the keyword list - so it can only be used in some other context if
-quoted. If something is a 'ansi keyword', but appears only as an
-identifier or function name for instance in the syntax (or something
-that looks identical to this), then it isn't treated as a keyword at
-all. When there is some overlap (e.g. 'set'), then there is either
-special case parsing code to handle this (in the case of set), or it
-is not treated as a keyword (not perfect, but if it more or less
-works, ok for now).
-
-An exception to this is the standard type names are considered as
-keywords at the moment, with a special case in the type parser to
-make this work. Maybe this isn't necessary or is a bad idea.
-
-It is possible to have a problem if you remove something which is a
-keyword from this list, and still want to parse statements using it
-as a keyword - for instance, removing things like 'from' or 'as',
-will likely mean many things don't parse anymore.
-
-
-
------------
-
-Used to make the dialect available during parsing so different parsers
-can be used for different dialects. Not sure if this is the best way
-to do it, but it's convenient
-
-> type ParseState = Dialect
-
-> type Token = ((String,Int,Int),L.Token)
-
-> type Parser = GenParser Token ParseState
-
-> guardDialect :: (Dialect -> Bool) -> Parser ()
-> guardDialect f = do
->     d <- getState
->     guard (f d)
-
-The dialect stuff could also be used for custom options: e.g. to only
-parse dml for instance.
diff --git a/Language/SQL/SimpleSQL/Pretty.hs b/Language/SQL/SimpleSQL/Pretty.hs
new file mode 100644
--- /dev/null
+++ b/Language/SQL/SimpleSQL/Pretty.hs
@@ -0,0 +1,894 @@
+
+-- | These is the pretty printing functions, which produce SQL
+-- source from ASTs. The code attempts to format the output in a
+-- readable way.
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+module Language.SQL.SimpleSQL.Pretty
+    (prettyQueryExpr
+    ,prettyScalarExpr
+    ,prettyStatement
+    ,prettyStatements
+    ) where
+
+{-
+TODO: there should be more comments in this file, especially the bits
+which have been changed to try to improve the layout of the output.
+-}
+
+import Prettyprinter (Doc
+                     ,nest
+                     ,punctuate
+                     ,comma
+                     ,squotes
+                     ,vsep
+                     ,layoutPretty
+                     ,defaultLayoutOptions
+                     ,brackets
+                     ,align
+                     ,hcat
+                     ,line
+                     )
+import qualified Prettyprinter as P
+import Prettyprinter.Internal.Type (Doc(Empty))
+
+import Prettyprinter.Render.Text (renderStrict)
+
+import Data.Maybe (maybeToList, catMaybes)
+
+import qualified Data.Text as T
+import Data.Text (Text)
+
+import Language.SQL.SimpleSQL.Syntax
+import Language.SQL.SimpleSQL.Dialect
+
+
+-- | Convert a query expr ast to Text.
+prettyQueryExpr :: Dialect -> QueryExpr -> Text
+prettyQueryExpr d = render . queryExpr d
+
+-- | Convert a value expr ast to Text.
+prettyScalarExpr :: Dialect -> ScalarExpr -> Text
+prettyScalarExpr d = render . scalarExpr d
+
+-- | A terminating semicolon.
+terminator :: Doc a
+terminator = pretty ";" <> line
+
+-- | Convert a statement ast to Text.
+prettyStatement :: Dialect -> Statement -> Text
+prettyStatement _ EmptyStatement = render terminator
+prettyStatement d s = render (statement d s)
+
+-- | Convert a list of statements to Text. A semicolon
+-- is inserted after each statement.
+prettyStatements :: Dialect -> [Statement] -> Text
+prettyStatements d = render . vsep . map prettyStatementWithSemicolon
+  where
+    prettyStatementWithSemicolon :: Statement -> Doc a
+    prettyStatementWithSemicolon s = statement d s <> terminator
+
+render :: Doc a -> Text
+render = renderStrict . layoutPretty defaultLayoutOptions
+
+-- = scalar expressions
+
+scalarExpr :: Dialect -> ScalarExpr -> Doc a
+scalarExpr _ (StringLit s e t) = pretty s <> pretty t <> pretty e
+
+scalarExpr _ (NumLit s) = pretty s
+scalarExpr _ (IntervalLit s v f t) =
+    pretty "interval"
+    <+> me (\x -> pretty $ case x of
+                             Plus -> "+"
+                             Minus -> "-") s
+    <+> squotes (pretty v)
+    <+> intervalTypeField f
+    <+> me (\x -> pretty "to" <+> intervalTypeField x) t
+scalarExpr _ (Iden i) = names i
+scalarExpr _ Star = pretty "*"
+scalarExpr _ Parameter = pretty "?"
+scalarExpr _ (PositionalArg n) = pretty $ T.cons '$' $ showText n
+scalarExpr _ (HostParameter p i) =
+    pretty p
+    <+> me (\i' -> pretty "indicator" <+> pretty i') i
+
+scalarExpr d (App f es) = names f <> parens (commaSep (map (scalarExpr d) es))
+
+scalarExpr dia (AggregateApp f d es od fil) =
+    (names f
+    <> parens ((case d of
+                  Distinct -> pretty "distinct"
+                  All -> pretty "all"
+                  SQDefault -> mempty)
+               <+> commaSep (map (scalarExpr dia) es)
+               <+> orderBy dia od))
+    <+> me (\x -> pretty "filter"
+                  <+> parens (pretty "where" <+> scalarExpr dia x)) fil
+
+scalarExpr d (AggregateAppGroup f es od) =
+    names f
+    <> parens (commaSep (map (scalarExpr d) es))
+    <+> if null od
+        then mempty
+        else pretty "within group" <+> parens (orderBy d od)
+
+scalarExpr d (WindowApp f es pb od fr) =
+    names f <> parens (commaSep $ map (scalarExpr d) es)
+    <+> pretty "over"
+    <+> parens ((case pb of
+                    [] -> mempty
+                    _ -> pretty "partition by" <+> align
+                                   (commaSep $ map (scalarExpr d) pb))
+                <+> orderBy d od
+    <+> me frd fr)
+  where
+    frd (FrameFrom rs fp) = rsd rs <+> fpd fp
+    frd (FrameBetween rs fps fpe) =
+        rsd rs <+> pretty "between" <+> fpd fps
+        <+> pretty "and" <+> fpd fpe
+    rsd rs = case rs of
+                 FrameRows -> pretty "rows"
+                 FrameRange -> pretty "range"
+    fpd UnboundedPreceding = pretty "unbounded preceding"
+    fpd UnboundedFollowing = pretty "unbounded following"
+    fpd Current = pretty "current row"
+    fpd (Preceding e) = scalarExpr d e <+> pretty "preceding"
+    fpd (Following e) = scalarExpr d e <+> pretty "following"
+
+scalarExpr dia (SpecialOp nm [a,b,c])
+    | nm `elem` [[Name Nothing "between"]
+                ,[Name Nothing "not between"]] =
+  sep [scalarExpr dia a
+      ,names nm <+> nest (T.length (unnames nm) - 3) (sep
+          [scalarExpr dia b
+          ,pretty "and" <+> scalarExpr dia c])]
+
+scalarExpr d (SpecialOp [Name Nothing "rowctor"] as) =
+    parens $ commaSep $ map (scalarExpr d) as
+
+scalarExpr d (SpecialOp nm es) =
+  names nm <+> parens (commaSep $ map (scalarExpr d) es)
+
+scalarExpr d (SpecialOpK nm fs as) =
+    names nm <> parens (sep $ catMaybes
+        (fmap (scalarExpr d) fs
+         : map (\(n,e) -> Just (pretty n <+> scalarExpr d e)) as))
+
+scalarExpr d (PrefixOp f e) = names f <+> scalarExpr d e
+scalarExpr d (PostfixOp f e) = scalarExpr d e <+> names f
+scalarExpr d e@(BinOp _ op _) | op `elem` [[Name Nothing "and"]
+                                         ,[Name Nothing "or"]] =
+    -- special case for and, or, get all the ands so we can vsep them
+    -- nicely
+    case ands e of
+      (e':es) -> vsep (scalarExpr d e'
+                       : map ((names op <+>) . scalarExpr d) es)
+      [] -> mempty -- 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 d (BinOp e0 [Name Nothing "."] e1) =
+    scalarExpr d e0 <> pretty "." <> scalarExpr d e1
+scalarExpr d (BinOp e0 f e1) =
+    scalarExpr d e0 <+> names f <+> scalarExpr d e1
+
+scalarExpr dia (Case t ws els) =
+    sep $ [pretty "case" <+> me (scalarExpr dia) t]
+          <> map w ws
+          <> maybeToList (fmap e els)
+          <> [pretty "end"]
+  where
+    w (t0,t1) =
+      pretty "when" <+> align (sep [commaSep $ map (scalarExpr dia) t0
+                                   ,pretty "then" <+> align (scalarExpr dia t1)])
+    e el = pretty "else" <+> align (scalarExpr dia el)
+scalarExpr d (Parens e) =
+    parens (scalarExpr d e)
+scalarExpr d (Cast e tn) =
+    pretty "cast" <> parens (sep [scalarExpr d e
+                                 ,pretty "as"
+                                 ,typeName tn])
+
+scalarExpr _ (TypedLit tn s) =
+    typeName tn <+> squotes (pretty s)
+
+scalarExpr d (SubQueryExpr ty qe) =
+    (case ty of
+        SqSq -> mempty
+        SqExists -> pretty "exists"
+        SqUnique -> pretty "unique"
+    ) <+> parens (queryExpr d qe)
+
+scalarExpr d (QuantifiedComparison v c cp sq) =
+    scalarExpr d v
+    <+> names c
+    <+> pretty (case cp of
+                    CPAny -> "any"
+                    CPSome -> "some"
+                    CPAll -> "all")
+    <+> parens (queryExpr d sq)
+
+scalarExpr d (Match v u sq) =
+    scalarExpr d v
+    <+> pretty "match"
+    <+> (if u then pretty "unique" else mempty)
+    <+> parens (queryExpr d sq)
+
+scalarExpr d (In b se x) =
+    scalarExpr d se <+>
+    (if b then mempty else pretty "not")
+    <+> pretty "in"
+    <+> parens (case x of
+                     InList es -> commaSep $ map (scalarExpr d) es
+                     InQueryExpr qe -> queryExpr d qe)
+
+scalarExpr d (Array v es) =
+    scalarExpr d v <> brackets (commaSep $ map (scalarExpr d) es)
+
+scalarExpr d (ArrayCtor q) =
+    pretty "array" <> parens (queryExpr d q)
+
+scalarExpr d (MultisetCtor es) =
+    pretty "multiset" <> brackets (commaSep $ map (scalarExpr d) es)
+
+scalarExpr d (MultisetQueryCtor q) =
+    pretty "multiset" <> parens (queryExpr d q)
+
+scalarExpr d (MultisetBinOp a c q b) =
+    sep
+    [scalarExpr d a
+    ,pretty "multiset"
+    ,pretty $ case c of
+                Union -> "union"
+                Intersect -> "intersect"
+                Except -> "except"
+    ,case q of
+         SQDefault -> mempty
+         All -> pretty "all"
+         Distinct -> pretty "distinct"
+    ,scalarExpr d b]
+
+{-scalarExpr d (Escape v e) =
+    scalarExpr d v <+> pretty "escape" <+> pretty [e]
+
+scalarExpr d (UEscape v e) =
+    scalarExpr d v <+> pretty "uescape" <+> pretty [e]-}
+
+scalarExpr d (Collate v c) =
+    scalarExpr d v <+> pretty "collate" <+> names c
+
+scalarExpr _ (NextValueFor ns) =
+    pretty "next value for" <+> names ns
+
+scalarExpr d (VEComment cmt v) =
+    vsep $ map comment cmt <> [scalarExpr d v]
+
+scalarExpr _ (OdbcLiteral t s) =
+    pretty "{" <> lt t <+> squotes (pretty s) <> pretty "}"
+  where
+    lt OLDate = pretty "d"
+    lt OLTime = pretty "t"
+    lt OLTimestamp = pretty "ts"
+
+scalarExpr d (OdbcFunc e) =
+    pretty "{fn" <+> scalarExpr d e <> pretty "}"
+
+scalarExpr d (Convert t e Nothing) =
+    pretty "convert(" <> typeName t <> pretty "," <+> scalarExpr d e <> pretty ")"
+scalarExpr d (Convert t e (Just i)) =
+    pretty "convert(" <> typeName t <> pretty "," <+> scalarExpr d e <> pretty "," <+> pretty (showText i) <> pretty ")"
+
+unname :: Name -> Text
+unname (Name Nothing n) = n
+unname (Name (Just (s,e)) n) =
+    s <> n <> e
+
+unnames :: [Name] -> Text
+unnames ns = T.intercalate "." $ map unname ns
+
+
+name :: Name -> Doc a
+name (Name Nothing n) = pretty n
+name (Name (Just (s,e)) n) = pretty s <> pretty n <> pretty e
+
+names :: [Name] -> Doc a
+names ns = hcat $ punctuate (pretty ".") $ map name ns
+
+typeName :: TypeName -> Doc a
+typeName (TypeName t) = names t
+typeName (PrecTypeName t a) = names t <+> parens (pretty $ showText a)
+typeName (PrecScaleTypeName t a b) =
+    names t <+> parens (pretty (showText a) <+> comma <+> pretty (showText b))
+typeName (PrecLengthTypeName t i m u) =
+    names t
+    <> parens (pretty (showText i)
+               <> me (\case
+                           PrecK -> pretty "K"
+                           PrecM -> pretty "M"
+                           PrecG -> pretty "G"
+                           PrecT -> pretty "T"
+                           PrecP -> pretty "P") m
+               <+> me (\case
+                       PrecCharacters -> pretty "CHARACTERS"
+                       PrecOctets -> pretty "OCTETS") u)
+typeName (CharTypeName t i cs col) =
+    (names t
+    <> me (\x -> parens (pretty $ showText x)) i)
+    <+> (if null cs
+         then mempty
+         else pretty "character set" <+> names cs)
+    <+> (if null col
+         then mempty
+         else pretty "collate" <+> names col)
+typeName (TimeTypeName t i tz) =
+    (names t
+    <> me (\x -> parens (pretty $ showText x)) i)
+    <+> pretty (if tz
+              then "with time zone"
+              else "without time zone")
+typeName (RowTypeName cs) =
+    pretty "row" <> parens (commaSep $ map f cs)
+  where
+    f (n,t) = name n <+> typeName t
+typeName (IntervalTypeName f t) =
+    pretty "interval"
+    <+> intervalTypeField f
+    <+> me (\x -> pretty "to" <+> intervalTypeField x) t
+
+typeName (ArrayTypeName tn sz) =
+    typeName tn <+> pretty "array" <+> me (brackets . pretty . showText) sz
+
+typeName (MultisetTypeName tn) =
+    typeName tn <+> pretty "multiset"
+
+intervalTypeField :: IntervalTypeField -> Doc a
+intervalTypeField (Itf n p) =
+    pretty n
+    <+> me (\(x,x1) ->
+             parens (pretty (showText x)
+                     <+> me (\y -> sep [comma,pretty (showText y)]) x1)) p
+
+
+-- = query expressions
+
+queryExpr :: Dialect -> QueryExpr -> Doc a
+queryExpr dia (Select d sl fr wh gb hv od off fe) =
+  sep [pretty "select" <+> align (sep
+          [case d of
+               SQDefault -> mempty
+               All -> pretty "all"
+               Distinct -> pretty "distinct"
+          ,selectList dia sl])
+      ,from dia fr
+      ,maybeScalarExpr dia "where" wh
+      ,grpBy dia gb
+      ,maybeScalarExpr dia "having" hv
+      ,orderBy dia od
+      ,me (\e -> pretty "offset" <+> scalarExpr dia e <+> pretty "rows") off
+      ,fetchFirst
+      ]
+  where
+    fetchFirst =
+      me (\e -> if diLimit dia
+                then pretty "limit" <+> scalarExpr dia e
+                else pretty "fetch first" <+> scalarExpr dia e
+                     <+> pretty "rows only") fe
+
+queryExpr dia (QueryExprSetOp q1 ct d c q2) =
+  sep [queryExpr dia q1
+      ,pretty (case ct of
+                Union -> "union"
+                Intersect -> "intersect"
+                Except -> "except")
+       <+> case d of
+               SQDefault -> mempty
+               All -> pretty "all"
+               Distinct -> pretty "distinct"
+       <+> case c of
+               Corresponding -> pretty "corresponding"
+               Respectively -> mempty
+      ,queryExpr dia q2]
+queryExpr d (With rc withs qe) =
+  pretty "with" <+> (if rc then pretty "recursive" else mempty)
+  <+> vsep [nest 5
+            (vsep $ punctuate comma $ flip map withs $ \(n,q) ->
+             withAlias n <+> pretty "as" <+> parens (queryExpr d q))
+           ,queryExpr d qe]
+  where
+    withAlias (Alias nm cols) = name nm
+                                <+> me (parens . commaSep . map name) cols
+
+
+queryExpr d (Values vs) =
+    pretty "values"
+    <+> nest 7 (commaSep (map (parens . commaSep . map (scalarExpr d)) vs))
+queryExpr _ (Table t) = pretty "table" <+> names t
+queryExpr d (QEComment cmt v) =
+    vsep $ map comment cmt <> [queryExpr d v]
+
+
+alias :: Alias -> Doc a
+alias (Alias nm cols) =
+    pretty "as" <+> name nm
+    <+> me (parens . commaSep . map name) cols
+
+selectList :: Dialect -> [(ScalarExpr,Maybe Name)] -> Doc a
+selectList d is = commaSep $ map si is
+  where
+    si (e,al) = scalarExpr d e <+> me als al
+    als al = pretty "as" <+> name al
+
+from :: Dialect -> [TableRef] -> Doc a
+from _ [] = mempty
+from d ts =
+    pretty "from" <+> align (vsep (punctuate comma $ map tr ts))
+  where
+    tr (TRSimple t) = names t
+    tr (TRLateral t) = pretty "lateral" <+> tr t
+    tr (TRFunction f as) =
+        names f <> parens (commaSep $ map (scalarExpr d) as)
+    tr (TRAlias t a) = sep [tr t, alias a]
+    tr (TRParens t) = parens $ tr t
+    tr (TRQueryExpr q) = parens $ queryExpr d q
+    tr (TRJoin t0 b jt t1 jc) =
+       sep [tr t0
+           ,if b then pretty "natural" else mempty
+           ,joinText jt <+> tr t1
+           ,joinCond jc]
+    tr (TROdbc t) = pretty "{oj" <+> tr t <+> pretty "}"
+    joinText jt =
+      sep [case jt of
+              JInner -> pretty "inner"
+              JLeft -> pretty "left"
+              JRight -> pretty "right"
+              JFull -> pretty "full"
+              JCross -> pretty "cross"
+          ,pretty "join"]
+    joinCond (Just (JoinOn e)) = pretty "on" <+> scalarExpr d e
+    joinCond (Just (JoinUsing es)) =
+        pretty "using" <+> parens (commaSep $ map name es)
+    joinCond Nothing = mempty
+
+maybeScalarExpr :: Dialect -> Text -> Maybe ScalarExpr -> Doc a
+maybeScalarExpr d k = me
+      (\e -> pretty k <+> align (scalarExpr d e))
+
+grpBy :: Dialect -> [GroupingExpr] -> Doc a
+grpBy _ [] = mempty
+grpBy d gs = pretty "group by" <+> align (commaSep $ map ge gs)
+  where
+    ge (SimpleGroup e) = scalarExpr d e
+    ge (GroupingParens g) = parens (commaSep $ map ge g)
+    ge (Cube es) = pretty "cube" <> parens (commaSep $ map ge es)
+    ge (Rollup es) = pretty "rollup" <> parens (commaSep $ map ge es)
+    ge (GroupingSets es) = pretty "grouping sets" <> parens (commaSep $ map ge es)
+
+orderBy :: Dialect -> [SortSpec] -> Doc a
+orderBy _ [] = mempty
+orderBy dia os = pretty "order by" <+> align (commaSep $ map f os)
+  where
+    f (SortSpec e d n) =
+        scalarExpr dia e
+        <+> (case d of
+                  Asc -> pretty "asc"
+                  Desc -> pretty "desc"
+                  DirDefault -> mempty)
+        <+> (case n of
+                NullsOrderDefault -> mempty
+                NullsFirst -> pretty "nulls" <+> pretty "first"
+                NullsLast -> pretty "nulls" <+> pretty "last")
+
+-- = statements
+
+statement :: Dialect -> Statement -> Doc a
+
+
+-- == ddl
+
+statement _ (CreateSchema nm) =
+    pretty "create" <+> pretty "schema" <+> names nm
+
+statement d (CreateTable nm cds) =
+    pretty "create" <+> pretty "table" <+> names nm
+    <+> parens (commaSep $ map cd cds)
+  where
+    cd (TableConstraintDef n con) =
+        maybe mempty (\s -> pretty "constraint" <+> names s) n
+        <+> tableConstraint d con
+    cd (TableColumnDef cd') = columnDef d cd'
+
+statement d (AlterTable t act) =
+    texts ["alter","table"] <+> names t
+    <+> alterTableAction d act
+
+statement _ (DropSchema nm db) =
+    pretty "drop" <+> pretty "schema" <+> names nm <+> dropBehav db
+
+statement d (CreateDomain nm ty def cs) =
+    pretty "create" <+> pretty "domain" <+> names nm
+    <+> typeName ty
+    <+> maybe mempty (\def' -> pretty "default" <+> scalarExpr d def') def
+    <+> sep (map con cs)
+  where
+    con (cn, e) =
+        maybe mempty (\cn' -> pretty "constraint" <+> names cn') cn
+        <+> pretty "check" <> parens (scalarExpr d e)
+
+statement d (AlterDomain nm act) =
+    texts ["alter","domain"]
+    <+> names nm
+    <+> a act
+  where
+    a (ADSetDefault v) = texts ["set","default"] <+> scalarExpr d v
+    a ADDropDefault = texts ["drop","default"]
+    a (ADAddConstraint cnm e) =
+        pretty "add"
+        <+> maybe mempty (\cnm' -> pretty "constraint" <+> names cnm') cnm
+        <+> pretty "check" <> parens (scalarExpr d e)
+    a (ADDropConstraint cnm) = texts ["drop", "constraint"]
+                               <+> names cnm
+
+
+statement _ (DropDomain nm db) =
+    pretty "drop" <+> pretty "domain" <+> names nm <+> dropBehav db
+
+statement _ (CreateSequence nm sgos) =
+  texts ["create","sequence"] <+> names nm
+  <+> sep (map sequenceGeneratorOption sgos)
+
+statement _ (AlterSequence nm sgos) =
+  texts ["alter","sequence"] <+> names nm
+  <+> sep (map sequenceGeneratorOption sgos)
+
+statement _ (DropSequence nm db) =
+    pretty "drop" <+> pretty "sequence" <+> names nm <+> dropBehav db
+
+
+statement d (CreateAssertion nm ex) =
+  texts ["create","assertion"] <+> names nm
+  <+> pretty "check" <+> parens (scalarExpr d ex)
+
+statement _ (DropAssertion nm db) =
+    pretty "drop" <+> pretty "assertion" <+> names nm <+> dropBehav db
+
+statement _ (CreateIndex un nm tbl cols) =
+  texts (if un
+         then ["create","unique","index"]
+         else  ["create","index"])
+  <+> names nm
+  <+> pretty "on"
+  <+> names tbl
+  <+> parens (commaSep $ map name cols)
+
+-- == dml
+
+statement d (SelectStatement q) = queryExpr d q
+
+statement d (Delete t a w) =
+    pretty "delete" <+> pretty "from"
+    <+> names t <+> maybe mempty (\x -> pretty "as" <+> name x) a
+    <+> maybeScalarExpr d "where" w
+
+statement _ (Truncate t ir) =
+    pretty "truncate" <+> pretty "table" <+> names t
+    <+> case ir of
+            DefaultIdentityRestart -> mempty
+            ContinueIdentity -> pretty "continue" <+> pretty "identity"
+            RestartIdentity -> pretty "restart" <+> pretty "identity"
+
+statement d (Insert t cs s) =
+    pretty "insert" <+> pretty "into" <+> names t
+    <+> maybe mempty (\cs' -> parens (commaSep $ map name cs')) cs
+    <+> case s of
+            DefaultInsertValues -> pretty "default" <+> pretty "values"
+            InsertQuery q -> queryExpr d q
+
+statement d (Update t a sts whr) =
+    pretty "update" <+> names t
+    <+> maybe mempty (\x -> pretty "as" <+> name x) a
+    <+> pretty "set" <+> commaSep (map sc sts)
+    <+> maybeScalarExpr d "where" whr
+  where
+    sc (Set tg v) = names tg <+> pretty "=" <+> scalarExpr d v
+    sc (SetMultiple ts vs) = parens (commaSep $ map names ts) <+> pretty "="
+                             <+> parens (commaSep $ map (scalarExpr d) vs)
+
+statement _ (DropTable n b) =
+    pretty "drop" <+> pretty "table" <+> names n <+> dropBehav b
+
+statement d (CreateView r nm al q co) =
+    pretty "create" <+> (if r then pretty "recursive" else mempty)
+    <+> pretty "view" <+> names nm
+    <+> maybe mempty (parens . commaSep . map name) al
+    <+> pretty "as"
+    <+> queryExpr d q
+    <+> case co of
+            Nothing -> mempty
+            Just DefaultCheckOption -> texts ["with", "check", "option"]
+            Just CascadedCheckOption -> texts ["with", "cascaded", "check", "option"]
+            Just LocalCheckOption -> texts ["with", "local", "check", "option"]
+
+statement _ (DropView n b) =
+    pretty "drop" <+> pretty "view" <+> names n <+> dropBehav b
+
+
+-- == transactions
+
+statement _ StartTransaction =
+    texts ["start", "transaction"]
+
+statement _ (Savepoint nm) =
+    pretty "savepoint" <+> name nm
+
+statement _ (ReleaseSavepoint nm) =
+    texts ["release", "savepoint"] <+> name nm
+
+statement _ Commit =
+    pretty "commit"
+
+statement _ (Rollback mn) =
+    pretty "rollback"
+    <+> maybe mempty (\n -> texts ["to","savepoint"] <+> name n) mn
+
+-- == access control
+
+statement _ (GrantPrivilege pas po rs go) =
+    pretty "grant" <+> commaSep (map privAct pas)
+    <+> pretty "on" <+> privObj po
+    <+> pretty "to" <+> commaSep (map name rs)
+    <+> grantOpt go
+  where
+    grantOpt WithGrantOption = texts ["with","grant","option"]
+    grantOpt WithoutGrantOption = mempty
+
+statement _ (GrantRole rs trs ao) =
+    pretty "grant" <+> commaSep (map name rs)
+    <+> pretty "to" <+> commaSep (map name trs)
+    <+> adminOpt ao
+  where
+    adminOpt WithAdminOption = texts ["with","admin","option"]
+    adminOpt WithoutAdminOption = mempty
+
+statement _ (CreateRole nm) =
+    texts ["create","role"] <+> name nm
+
+statement _ (DropRole nm) =
+    texts ["drop","role"] <+> name nm
+
+statement _ (RevokePrivilege go pas po rs db) =
+    pretty "revoke"
+    <+> grantOptFor go
+    <+> commaSep (map privAct pas)
+    <+> pretty "on" <+> privObj po
+    <+> pretty "from" <+> commaSep (map name rs)
+    <+> dropBehav db
+  where
+    grantOptFor GrantOptionFor = texts ["grant","option","for"]
+    grantOptFor NoGrantOptionFor = mempty
+
+statement _ (RevokeRole ao rs trs db) =
+    pretty "revoke"
+    <+> adminOptFor ao
+    <+> commaSep (map name rs)
+    <+> pretty "from" <+> commaSep (map name trs)
+    <+> dropBehav db
+  where
+    adminOptFor AdminOptionFor = texts ["admin","option","for"]
+    adminOptFor NoAdminOptionFor = mempty
+
+
+statement _ (StatementComment cs) = vsep $ map comment cs
+statement _ EmptyStatement = mempty
+
+
+{-
+== sessions
+
+
+== extras
+-}
+
+dropBehav :: DropBehaviour -> Doc a
+dropBehav DefaultDropBehaviour = mempty
+dropBehav Cascade = pretty "cascade"
+dropBehav Restrict = pretty "restrict"
+
+
+columnDef :: Dialect -> ColumnDef -> Doc a
+columnDef d (ColumnDef n t mdef cons) =
+      name n <+> typeName t
+      <+> case mdef of
+             Nothing -> mempty
+             Just (DefaultClause def) ->
+                 pretty "default" <+> scalarExpr d def
+             Just (GenerationClause e) ->
+                 texts ["generated","always","as"] <+> parens (scalarExpr d e)
+             Just (IdentityColumnSpec w o) ->
+                 pretty "generated"
+                 <+> (case w of
+                         GeneratedAlways -> pretty "always"
+                         GeneratedByDefault -> pretty "by" <+> pretty "default")
+                 <+> pretty "as" <+> pretty "identity"
+                 <+> (case o of
+                         [] -> mempty
+                         os -> parens (sep $ map sequenceGeneratorOption os))
+      <+> sep (map cdef cons)
+  where
+    cdef (ColConstraintDef cnm con) =
+        maybe mempty (\s -> pretty "constraint" <+> names s) cnm
+        <+> pcon con
+    pcon ColNotNullConstraint = texts ["not","null"]
+    pcon ColNullableConstraint = texts ["null"]
+    pcon ColUniqueConstraint = pretty "unique"
+    pcon (ColPrimaryKeyConstraint autoincrement) = 
+      texts $ ["primary","key"] <> ["autoincrement"|autoincrement]
+    --pcon ColPrimaryKeyConstraint = texts ["primary","key"]
+    pcon (ColCheckConstraint v) = pretty "check" <+> parens (scalarExpr d v)
+    pcon (ColReferencesConstraint tb c m u del) =
+        pretty "references"
+        <+> names tb
+        <+> maybe mempty (parens . name) c
+        <+> refMatch m
+        <+> refAct "update" u
+        <+> refAct "delete" del
+
+sequenceGeneratorOption :: SequenceGeneratorOption -> Doc a
+sequenceGeneratorOption (SGODataType t) =
+    pretty "as" <+> typeName t
+sequenceGeneratorOption (SGORestart mi) =
+    pretty "restart" <+> maybe mempty (\mi' -> texts ["with", showText mi']) mi
+sequenceGeneratorOption (SGOStartWith i) = texts ["start",  "with", showText i]
+sequenceGeneratorOption (SGOIncrementBy i) = texts ["increment", "by", showText i]
+sequenceGeneratorOption (SGOMaxValue i) = texts ["maxvalue", showText i]
+sequenceGeneratorOption SGONoMaxValue = texts ["no", "maxvalue"]
+sequenceGeneratorOption (SGOMinValue i) = texts ["minvalue", showText i]
+sequenceGeneratorOption SGONoMinValue = texts ["no", "minvalue"]
+sequenceGeneratorOption SGOCycle = pretty "cycle"
+sequenceGeneratorOption SGONoCycle = pretty "no cycle"
+
+refMatch :: ReferenceMatch -> Doc a
+refMatch m = case m of
+                     DefaultReferenceMatch -> mempty
+                     MatchFull -> texts ["match", "full"]
+                     MatchPartial -> texts ["match","partial"]
+                     MatchSimple -> texts ["match", "simple"]
+
+refAct :: Text -> ReferentialAction -> Doc a
+refAct t a = case a of
+                     DefaultReferentialAction -> mempty
+                     RefCascade -> texts ["on", t, "cascade"]
+                     RefSetNull -> texts ["on", t, "set", "null"]
+                     RefSetDefault -> texts ["on", t, "set", "default"]
+                     RefRestrict -> texts ["on", t, "restrict"]
+                     RefNoAction -> texts ["on", t, "no", "action"]
+
+alterTableAction :: Dialect -> AlterTableAction -> Doc a
+alterTableAction d (AddColumnDef cd) =
+    texts ["add", "column"] <+> columnDef d cd
+
+alterTableAction d (AlterColumnSetDefault n v) =
+    texts ["alter", "column"]
+    <+> name n
+    <+> texts ["set","default"] <+> scalarExpr d v
+alterTableAction _ (AlterColumnDropDefault n) =
+    texts ["alter", "column"]
+    <+> name n
+    <+> texts ["drop","default"]
+
+alterTableAction _ (AlterColumnSetNotNull n) =
+    texts ["alter", "column"]
+    <+> name n
+    <+> texts ["set","not","null"]
+
+alterTableAction _ (AlterColumnDropNotNull n) =
+    texts ["alter", "column"]
+    <+> name n
+    <+> texts ["drop","not","null"]
+
+alterTableAction _ (AlterColumnSetDataType n t) =
+    texts ["alter", "column"]
+    <+> name n
+    <+> texts ["set","data","Type"]
+    <+> typeName t
+
+alterTableAction _ (DropColumn n b) =
+    texts ["drop", "column"]
+    <+> name n
+    <+> dropBehav b
+
+alterTableAction d (AddTableConstraintDef n con) =
+    pretty "add"
+    <+> maybe mempty (\s -> pretty "constraint" <+> names s) n
+    <+> tableConstraint d con
+
+alterTableAction _ (DropTableConstraintDef n b) =
+    texts ["drop", "constraint"]
+    <+> names n
+    <+> dropBehav b
+
+
+tableConstraint :: Dialect -> TableConstraint -> Doc a
+tableConstraint _ (TableUniqueConstraint ns) =
+         pretty "unique" <+> parens (commaSep $ map name ns)
+tableConstraint _ (TablePrimaryKeyConstraint ns) =
+        texts ["primary","key"] <+> parens (commaSep $ map name ns)
+tableConstraint _ (TableReferencesConstraint cs t tcs m u del) =
+        texts ["foreign", "key"]
+        <+> parens (commaSep $ map name cs)
+        <+> pretty "references"
+        <+> names t
+        <+> maybe mempty (\c' -> parens (commaSep $ map name c')) tcs
+        <+> refMatch m
+        <+> refAct "update" u
+        <+> refAct "delete" del
+tableConstraint d (TableCheckConstraint v) = pretty "check" <+> parens (scalarExpr d v)
+
+
+privAct :: PrivilegeAction -> Doc a
+privAct PrivAll = texts ["all","privileges"]
+privAct (PrivSelect cs) = pretty "select" <+> maybeColList cs
+privAct (PrivInsert cs) = pretty "insert" <+> maybeColList cs
+privAct (PrivUpdate cs) = pretty "update" <+> maybeColList cs
+privAct (PrivReferences cs) = pretty "references" <+> maybeColList cs
+privAct PrivDelete = pretty "delete"
+privAct PrivUsage = pretty "usage"
+privAct PrivTrigger = pretty "trigger"
+privAct PrivExecute = pretty "execute"
+
+maybeColList :: [Name] -> Doc a
+maybeColList cs =
+    if null cs
+    then mempty
+    else parens (commaSep $ map name cs)
+
+privObj :: PrivilegeObject -> Doc a
+privObj (PrivTable nm) = names nm
+privObj (PrivDomain nm) = pretty "domain" <+> names nm
+privObj (PrivType nm) = pretty "type" <+> names nm
+privObj (PrivSequence nm) = pretty "sequence" <+> names nm
+privObj (PrivFunction nm) = texts ["specific", "function"] <+> names nm
+
+-- = utils
+
+commaSep :: [Doc a] -> Doc a
+commaSep ds = sep $ punctuate comma ds
+
+me :: (b -> Doc a) -> Maybe b -> Doc a
+me = maybe mempty
+
+comment :: Comment -> Doc a
+comment (BlockComment str) = pretty "/*" <+> pretty str <+> pretty "*/"
+
+texts :: [Text] -> Doc a
+texts ts = sep $ map pretty ts
+
+-- regular pretty completely defeats the type checker when you want
+-- to change the ast and get type errors, instead it just produces
+-- incorrect code.
+pretty :: Text -> Doc a
+pretty = P.pretty
+
+showText :: Show a => a -> Text
+showText = T.pack . show
+
+-- restore the correct behaviour of mempty
+-- this doesn't quite work when you chain <> and <+> together,
+-- so use parens in those cases
+
+sep :: [Doc a] -> Doc a
+sep = P.sep . filter isEmpty
+  where
+    isEmpty Empty = False
+    isEmpty _ = True
+
+(<+>) :: Doc a -> Doc a -> Doc a
+(<+>) a b = case (a,b) of
+    (Empty, Empty) -> Empty
+    (Empty, x) -> x
+    (x, Empty) -> x
+    _ ->  a P.<+> b
+
+parens :: Doc a -> Doc a
+parens a = P.parens (align a)
diff --git a/Language/SQL/SimpleSQL/Pretty.lhs b/Language/SQL/SimpleSQL/Pretty.lhs
deleted file mode 100644
--- a/Language/SQL/SimpleSQL/Pretty.lhs
+++ /dev/null
@@ -1,820 +0,0 @@
-
-> -- | These is the pretty printing functions, which produce SQL
-> -- source from ASTs. The code attempts to format the output in a
-> -- readable way.
-> module Language.SQL.SimpleSQL.Pretty
->     (prettyQueryExpr
->     ,prettyScalarExpr
->     ,prettyStatement
->     ,prettyStatements
->     ) where
-
-> import Prelude hiding ((<>))
-
-TODO: there should be more comments in this file, especially the bits
-which have been changed to try to improve the layout of the output.
-Try to do this when this code is ported to a modern pretty printing lib.
-
-> --import Language.SQL.SimpleSQL.Dialect
-> import Text.PrettyPrint (render, vcat, text, (<>), (<+>), empty, parens,
->                          nest, Doc, punctuate, comma, sep, quotes,
->                          brackets,hcat)
-> import Data.Maybe (maybeToList, catMaybes)
-> import Data.List (intercalate)
-
-
-> import Language.SQL.SimpleSQL.Syntax
-> import Language.SQL.SimpleSQL.Dialect
-
-
-> -- | Convert a query expr ast to concrete syntax.
-> prettyQueryExpr :: Dialect -> QueryExpr -> String
-> prettyQueryExpr d = render . queryExpr d
-
-> -- | Convert a value expr ast to concrete syntax.
-> prettyScalarExpr :: Dialect -> ScalarExpr -> String
-> prettyScalarExpr d = render . scalarExpr d
-
-> -- | Convert a statement ast to concrete syntax.
-> prettyStatement :: Dialect -> Statement -> String
-> prettyStatement d = render . statement d
-
-> -- | Convert a list of statements to concrete syntax. A semicolon
-> -- is inserted after each statement.
-> prettyStatements :: Dialect -> [Statement] -> String
-> prettyStatements d = render . vcat . map ((<> text ";\n") . statement d)
-
-= scalar expressions
-
-> scalarExpr :: Dialect -> ScalarExpr -> Doc
-> scalarExpr _ (StringLit s e t) = text s <> text t <> text e
-
-> scalarExpr _ (NumLit s) = text s
-> scalarExpr _ (IntervalLit s v f t) =
->     text "interval"
->     <+> me (\x -> text $ case x of
->                              Plus -> "+"
->                              Minus -> "-") s
->     <+> quotes (text v)
->     <+> intervalTypeField f
->     <+> me (\x -> text "to" <+> intervalTypeField x) t
-> scalarExpr _ (Iden i) = names i
-> scalarExpr _ Star = text "*"
-> scalarExpr _ Parameter = text "?"
-> scalarExpr _ (PositionalArg n) = text $ "$" ++ show n
-> scalarExpr _ (HostParameter p i) =
->     text p
->     <+> me (\i' -> text "indicator" <+> text i') i
-
-> scalarExpr d (App f es) = names f <> parens (commaSep (map (scalarExpr d) es))
-
-> scalarExpr dia (AggregateApp f d es od fil) =
->     names f
->     <> parens ((case d of
->                   Distinct -> text "distinct"
->                   All -> text "all"
->                   SQDefault -> empty)
->                <+> commaSep (map (scalarExpr dia) es)
->                <+> orderBy dia od)
->     <+> me (\x -> text "filter"
->                   <+> parens (text "where" <+> scalarExpr dia x)) fil
-
-> scalarExpr d (AggregateAppGroup f es od) =
->     names f
->     <> parens (commaSep (map (scalarExpr d) es))
->     <+> if null od
->         then empty
->         else text "within group" <+> parens (orderBy d od)
-
-> scalarExpr d (WindowApp f es pb od fr) =
->     names f <> parens (commaSep $ map (scalarExpr d) es)
->     <+> text "over"
->     <+> parens ((case pb of
->                     [] -> empty
->                     _ -> text "partition by"
->                           <+> nest 13 (commaSep $ map (scalarExpr d) pb))
->                 <+> orderBy d od
->     <+> me 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 d e <+> text "preceding"
->     fpd (Following e) = scalarExpr d e <+> text "following"
-
-> scalarExpr dia (SpecialOp nm [a,b,c]) | nm `elem` [[Name Nothing "between"]
->                                                  ,[Name Nothing "not between"]] =
->   sep [scalarExpr dia a
->       ,names nm <+> scalarExpr dia b
->       ,nest (length (unnames nm) + 1) $ text "and" <+> scalarExpr dia c]
-
-> scalarExpr d (SpecialOp [Name Nothing "rowctor"] as) =
->     parens $ commaSep $ map (scalarExpr d) as
-
-> scalarExpr d (SpecialOp nm es) =
->   names nm <+> parens (commaSep $ map (scalarExpr d) es)
-
-> scalarExpr d (SpecialOpK nm fs as) =
->     names nm <> parens (sep $ catMaybes
->         (fmap (scalarExpr d) fs
->          : map (\(n,e) -> Just (text n <+> scalarExpr d e)) as))
-
-> scalarExpr d (PrefixOp f e) = names f <+> scalarExpr d e
-> scalarExpr d (PostfixOp f e) = scalarExpr d e <+> names f
-> scalarExpr d e@(BinOp _ op _) | op `elem` [[Name Nothing "and"]
->                                          ,[Name Nothing "or"]] =
->     -- special case for and, or, get all the ands so we can vcat them
->     -- nicely
->     case ands e of
->       (e':es) -> vcat (scalarExpr d e'
->                        : map ((names op <+>) . scalarExpr d) 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 d (BinOp e0 [Name Nothing "."] e1) =
->     scalarExpr d e0 <> text "." <> scalarExpr d e1
-> scalarExpr d (BinOp e0 f e1) =
->     scalarExpr d e0 <+> names f <+> scalarExpr d e1
-
-> scalarExpr dia (Case t ws els) =
->     sep $ [text "case" <+> me (scalarExpr dia) t]
->           ++ map w ws
->           ++ maybeToList (fmap e els)
->           ++ [text "end"]
->   where
->     w (t0,t1) =
->       text "when" <+> nest 5 (commaSep $ map (scalarExpr dia) t0)
->       <+> text "then" <+> nest 5 (scalarExpr dia t1)
->     e el = text "else" <+> nest 5 (scalarExpr dia el)
-> scalarExpr d (Parens e) = parens $ scalarExpr d e
-> scalarExpr d (Cast e tn) =
->     text "cast" <> parens (sep [scalarExpr d e
->                                ,text "as"
->                                ,typeName tn])
-
-> scalarExpr _ (TypedLit tn s) =
->     typeName tn <+> quotes (text s)
-
-> scalarExpr d (SubQueryExpr ty qe) =
->     (case ty of
->         SqSq -> empty
->         SqExists -> text "exists"
->         SqUnique -> text "unique"
->     ) <+> parens (queryExpr d qe)
-
-> scalarExpr d (QuantifiedComparison v c cp sq) =
->     scalarExpr d v
->     <+> names c
->     <+> (text $ case cp of
->              CPAny -> "any"
->              CPSome -> "some"
->              CPAll -> "all")
->     <+> parens (queryExpr d sq)
-
-> scalarExpr d (Match v u sq) =
->     scalarExpr d v
->     <+> text "match"
->     <+> (if u then text "unique" else empty)
->     <+> parens (queryExpr d sq)
-
-> scalarExpr d (In b se x) =
->     scalarExpr d se <+>
->     (if b then empty else text "not")
->     <+> text "in"
->     <+> parens (nest (if b then 3 else 7) $
->                  case x of
->                      InList es -> commaSep $ map (scalarExpr d) es
->                      InQueryExpr qe -> queryExpr d qe)
-
-> scalarExpr d (Array v es) =
->     scalarExpr d v <> brackets (commaSep $ map (scalarExpr d) es)
-
-> scalarExpr d (ArrayCtor q) =
->     text "array" <> parens (queryExpr d q)
-
-> scalarExpr d (MultisetCtor es) =
->     text "multiset" <> brackets (commaSep $ map (scalarExpr d) es)
-
-> scalarExpr d (MultisetQueryCtor q) =
->     text "multiset" <> parens (queryExpr d q)
-
-> scalarExpr d (MultisetBinOp a c q b) =
->     sep
->     [scalarExpr d a
->     ,text "multiset"
->     ,text $ case c of
->                 Union -> "union"
->                 Intersect -> "intersect"
->                 Except -> "except"
->     ,case q of
->          SQDefault -> empty
->          All -> text "all"
->          Distinct -> text "distinct"
->     ,scalarExpr d b]
-
-> {-scalarExpr d (Escape v e) =
->     scalarExpr d v <+> text "escape" <+> text [e]
-
-> scalarExpr d (UEscape v e) =
->     scalarExpr d v <+> text "uescape" <+> text [e]-}
-
-> scalarExpr d (Collate v c) =
->     scalarExpr d v <+> text "collate" <+> names c
-
-> scalarExpr _ (NextValueFor ns) =
->     text "next value for" <+> names ns
-
-> scalarExpr d (VEComment cmt v) =
->     vcat $ map comment cmt ++ [scalarExpr d v]
-
-> scalarExpr _ (OdbcLiteral t s) =
->     text "{" <> lt t <+> quotes (text s) <> text "}"
->   where
->     lt OLDate = text "d"
->     lt OLTime = text "t"
->     lt OLTimestamp = text "ts"
-
-> scalarExpr d (OdbcFunc e) =
->     text "{fn" <+> scalarExpr d e <> text "}"
-
-> unname :: Name -> String
-> unname (Name Nothing n) = n
-> unname (Name (Just (s,e)) n) =
->     s ++ n ++ e
-
-> unnames :: [Name] -> String
-> unnames ns = intercalate "." $ map unname ns
-
-
-> name :: Name -> Doc
-> name (Name Nothing n) = text n
-> name (Name (Just (s,e)) n) = text s <> text n <> text e
-
-> names :: [Name] -> Doc
-> names ns = hcat $ punctuate (text ".") $ map name ns
-
-> typeName :: TypeName -> Doc
-> typeName (TypeName t) = names t
-> typeName (PrecTypeName t a) = names t <+> parens (text $ show a)
-> typeName (PrecScaleTypeName t a b) =
->     names t <+> parens (text (show a) <+> comma <+> text (show b))
-> typeName (PrecLengthTypeName t i m u) =
->     names t
->     <> parens (text (show i)
->                <> me (\x -> case x of
->                            PrecK -> text "K"
->                            PrecM -> text "M"
->                            PrecG -> text "G"
->                            PrecT -> text "T"
->                            PrecP -> text "P") m
->                <+> me (\x -> case x of
->                        PrecCharacters -> text "CHARACTERS"
->                        PrecOctets -> text "OCTETS") u)
-> typeName (CharTypeName t i cs col) =
->     names t
->     <> me (\x -> parens (text $ show x)) i
->     <+> (if null cs
->          then empty
->          else text "character set" <+> names cs)
->     <+> (if null col
->          then empty
->          else text "collate" <+> names col)
-> typeName (TimeTypeName t i tz) =
->     names t
->     <> me (\x -> parens (text $ show x)) i
->     <+> text (if tz
->               then "with time zone"
->               else "without time zone")
-> typeName (RowTypeName cs) =
->     text "row" <> parens (commaSep $ map f cs)
->   where
->     f (n,t) = name n <+> typeName t
-> typeName (IntervalTypeName f t) =
->     text "interval"
->     <+> intervalTypeField f
->     <+> me (\x -> text "to" <+> intervalTypeField x) t
-
-> typeName (ArrayTypeName tn sz) =
->     typeName tn <+> text "array" <+> me (brackets . text . show) sz
-
-> typeName (MultisetTypeName tn) =
->     typeName tn <+> text "multiset"
-
-> intervalTypeField :: IntervalTypeField -> Doc
-> intervalTypeField (Itf n p) =
->     text n
->     <+> me (\(x,x1) ->
->              parens (text (show x)
->                      <+> me (\y -> (sep [comma,text (show y)])) x1)) p
-
-
-= query expressions
-
-> queryExpr :: Dialect -> QueryExpr -> Doc
-> queryExpr dia (Select d sl fr wh gb hv od off fe) =
->   sep [text "select"
->       ,case d of
->           SQDefault -> empty
->           All -> text "all"
->           Distinct -> text "distinct"
->       ,nest 7 $ sep [selectList dia sl]
->       ,from dia fr
->       ,maybeScalarExpr dia "where" wh
->       ,grpBy dia gb
->       ,maybeScalarExpr dia "having" hv
->       ,orderBy dia od
->       ,me (\e -> text "offset" <+> scalarExpr dia e <+> text "rows") off
->       ,fetchFirst
->       ]
->   where
->     fetchFirst =
->       me (\e -> if diLimit dia
->                 then text "limit" <+> scalarExpr dia e
->                 else text "fetch first" <+> scalarExpr dia e
->                      <+> text "rows only") fe
-
-> queryExpr dia (QueryExprSetOp q1 ct d c q2) =
->   sep [queryExpr dia q1
->       ,text (case ct of
->                 Union -> "union"
->                 Intersect -> "intersect"
->                 Except -> "except")
->        <+> case d of
->                SQDefault -> empty
->                All -> text "all"
->                Distinct -> text "distinct"
->        <+> case c of
->                Corresponding -> text "corresponding"
->                Respectively -> empty
->       ,queryExpr dia q2]
-> queryExpr d (With rc withs qe) =
->   text "with" <+> (if rc then text "recursive" else empty)
->   <+> vcat [nest 5
->             (vcat $ punctuate comma $ flip map withs $ \(n,q) ->
->              withAlias n <+> text "as" <+> parens (queryExpr d q))
->            ,queryExpr d qe]
->   where
->     withAlias (Alias nm cols) = name nm
->                                 <+> me (parens . commaSep . map name) cols
-
-
-> queryExpr d (Values vs) =
->     text "values"
->     <+> nest 7 (commaSep (map (parens . commaSep . map (scalarExpr d)) vs))
-> queryExpr _ (Table t) = text "table" <+> names t
-> queryExpr d (QEComment cmt v) =
->     vcat $ map comment cmt ++ [queryExpr d v]
-
-
-> alias :: Alias -> Doc
-> alias (Alias nm cols) =
->     text "as" <+> name nm
->     <+> me (parens . commaSep . map name) cols
-
-> selectList :: Dialect -> [(ScalarExpr,Maybe Name)] -> Doc
-> selectList d is = commaSep $ map si is
->   where
->     si (e,al) = scalarExpr d e <+> me als al
->     als al = text "as" <+> name al
-
-> from :: Dialect -> [TableRef] -> Doc
-> from _ [] = empty
-> from d ts =
->     sep [text "from"
->         ,nest 5 $ vcat $ punctuate comma $ map tr ts]
->   where
->     tr (TRSimple t) = names t
->     tr (TRLateral t) = text "lateral" <+> tr t
->     tr (TRFunction f as) =
->         names f <> parens (commaSep $ map (scalarExpr d) as)
->     tr (TRAlias t a) = sep [tr t, alias a]
->     tr (TRParens t) = parens $ tr t
->     tr (TRQueryExpr q) = parens $ queryExpr d q
->     tr (TRJoin t0 b jt t1 jc) =
->        sep [tr t0
->            ,if b then text "natural" else empty
->            ,joinText jt <+> tr t1
->            ,joinCond jc]
->     tr (TROdbc t) = text "{oj" <+> tr t <+> text "}"
->     joinText jt =
->       sep [case jt of
->               JInner -> text "inner"
->               JLeft -> text "left"
->               JRight -> text "right"
->               JFull -> text "full"
->               JCross -> text "cross"
->           ,text "join"]
->     joinCond (Just (JoinOn e)) = text "on" <+> scalarExpr d e
->     joinCond (Just (JoinUsing es)) =
->         text "using" <+> parens (commaSep $ map name es)
->     joinCond Nothing = empty
-
-> maybeScalarExpr :: Dialect -> String -> Maybe ScalarExpr -> Doc
-> maybeScalarExpr d k = me
->       (\e -> sep [text k
->                  ,nest (length k + 1) $ scalarExpr d e])
-
-> grpBy :: Dialect -> [GroupingExpr] -> Doc
-> grpBy _ [] = empty
-> grpBy d gs = sep [text "group by"
->                ,nest 9 $ commaSep $ map ge gs]
->   where
->     ge (SimpleGroup e) = scalarExpr d 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 :: Dialect -> [SortSpec] -> Doc
-> orderBy _ [] = empty
-> orderBy dia os = sep [text "order by"
->                  ,nest 9 $ commaSep $ map f os]
->   where
->     f (SortSpec e d n) =
->         scalarExpr dia e
->         <+> (case d of
->                   Asc -> text "asc"
->                   Desc -> text "desc"
->                   DirDefault -> empty)
->         <+> (case n of
->                 NullsOrderDefault -> empty
->                 NullsFirst -> text "nulls" <+> text "first"
->                 NullsLast -> text "nulls" <+> text "last")
-
-= statements
-
-> statement :: Dialect -> Statement -> Doc
-
-
-== ddl
-
-> statement _ (CreateSchema nm) =
->     text "create" <+> text "schema" <+> names nm
-
-> statement d (CreateTable nm cds) =
->     text "create" <+> text "table" <+> names nm
->     <+> parens (commaSep $ map cd cds)
->   where
->     cd (TableConstraintDef n con) =
->         maybe empty (\s -> text "constraint" <+> names s) n
->         <+> tableConstraint d con
->     cd (TableColumnDef cd') = columnDef d cd'
-
-> statement d (AlterTable t act) =
->     texts ["alter","table"] <+> names t
->     <+> alterTableAction d act
-
-> statement _ (DropSchema nm db) =
->     text "drop" <+> text "schema" <+> names nm <+> dropBehav db
-
-> statement d (CreateDomain nm ty def cs) =
->     text "create" <+> text "domain" <+> names nm
->     <+> typeName ty
->     <+> maybe empty (\def' -> text "default" <+> scalarExpr d def') def
->     <+> sep (map con cs)
->   where
->     con (cn, e) =
->         maybe empty (\cn' -> text "constraint" <+> names cn') cn
->         <+> text "check" <> parens (scalarExpr d e)
-
-> statement d (AlterDomain nm act) =
->     texts ["alter","domain"]
->     <+> names nm
->     <+> a act
->   where
->     a (ADSetDefault v) = texts ["set","default"] <+> scalarExpr d v
->     a (ADDropDefault) = texts ["drop","default"]
->     a (ADAddConstraint cnm e) =
->         text "add"
->         <+> maybe empty (\cnm' -> text "constraint" <+> names cnm') cnm
->         <+> text "check" <> parens (scalarExpr d e)
->     a (ADDropConstraint cnm) = texts ["drop", "constraint"]
->                                <+> names cnm
-
-
-> statement _ (DropDomain nm db) =
->     text "drop" <+> text "domain" <+> names nm <+> dropBehav db
-
-> statement _ (CreateSequence nm sgos) =
->   texts ["create","sequence"] <+> names nm
->   <+> sep (map sequenceGeneratorOption sgos)
-
-> statement _ (AlterSequence nm sgos) =
->   texts ["alter","sequence"] <+> names nm
->   <+> sep (map sequenceGeneratorOption sgos)
-
-> statement _ (DropSequence nm db) =
->     text "drop" <+> text "sequence" <+> names nm <+> dropBehav db
-
-
-> statement d (CreateAssertion nm ex) =
->   texts ["create","assertion"] <+> names nm
->   <+> text "check" <+> parens (scalarExpr d ex)
-
-> statement _ (DropAssertion nm db) =
->     text "drop" <+> text "assertion" <+> names nm <+> dropBehav db
-
-== dml
-
-> statement d (SelectStatement q) = queryExpr d q
-
-> statement d (Delete t a w) =
->     text "delete" <+> text "from"
->     <+> names t <+> maybe empty (\x -> text "as" <+> name x) a
->     <+> maybeScalarExpr d "where" w
-
-> statement _ (Truncate t ir) =
->     text "truncate" <+> text "table" <+> names t
->     <+> case ir of
->             DefaultIdentityRestart -> empty
->             ContinueIdentity -> text "continue" <+> text "identity"
->             RestartIdentity -> text "restart" <+> text "identity"
-
-> statement d (Insert t cs s) =
->     text "insert" <+> text "into" <+> names t
->     <+> maybe empty (\cs' -> parens (commaSep $ map name cs')) cs
->     <+> case s of
->             DefaultInsertValues -> text "default" <+> text "values"
->             InsertQuery q -> queryExpr d q
-
-> statement d (Update t a sts whr) =
->     text "update" <+> names t
->     <+> maybe empty (\x -> text "as" <+> name x) a
->     <+> text "set" <+> commaSep (map sc sts)
->     <+> maybeScalarExpr d "where" whr
->   where
->     sc (Set tg v) = names tg <+> text "=" <+> scalarExpr d v
->     sc (SetMultiple ts vs) = parens (commaSep $ map names ts) <+> text "="
->                              <+> parens (commaSep $ map (scalarExpr d) vs)
-
-> statement _ (DropTable n b) =
->     text "drop" <+> text "table" <+> names n <+> dropBehav b
-
-> statement d (CreateView r nm al q co) =
->     text "create" <+> (if r then text "recursive" else empty)
->     <+> text "view" <+> names nm
->     <+> (maybe empty (\al' -> parens $ commaSep $ map name al')) al
->     <+> text "as"
->     <+> queryExpr d q
->     <+> case co of
->             Nothing -> empty
->             Just DefaultCheckOption -> texts ["with", "check", "option"]
->             Just CascadedCheckOption -> texts ["with", "cascaded", "check", "option"]
->             Just LocalCheckOption -> texts ["with", "local", "check", "option"]
-
-> statement _ (DropView n b) =
->     text "drop" <+> text "view" <+> names n <+> dropBehav b
-
-
-== transactions
-
-> statement _ StartTransaction =
->     texts ["start", "transaction"]
-
-> statement _ (Savepoint nm) =
->     text "savepoint" <+> name nm
-
-> statement _ (ReleaseSavepoint nm) =
->     texts ["release", "savepoint"] <+> name nm
-
-> statement _ Commit =
->     text "commit"
-
-> statement _ (Rollback mn) =
->     text "rollback"
->     <+> maybe empty (\n -> texts ["to","savepoint"] <+> name n) mn
-
-== access control
-
-> statement _ (GrantPrivilege pas po rs go) =
->     text "grant" <+> commaSep (map privAct pas)
->     <+> text "on" <+> privObj po
->     <+> text "to" <+> commaSep (map name rs)
->     <+> grantOpt go
->   where
->     grantOpt WithGrantOption = texts ["with","grant","option"]
->     grantOpt WithoutGrantOption = empty
-
-> statement _ (GrantRole rs trs ao) =
->     text "grant" <+> commaSep (map name rs)
->     <+> text "to" <+> commaSep (map name trs)
->     <+> adminOpt ao
->   where
->     adminOpt WithAdminOption = texts ["with","admin","option"]
->     adminOpt WithoutAdminOption = empty
-
-> statement _ (CreateRole nm) =
->     texts ["create","role"] <+> name nm
-
-> statement _ (DropRole nm) =
->     texts ["drop","role"] <+> name nm
-
-> statement _ (RevokePrivilege go pas po rs db) =
->     text "revoke"
->     <+> grantOptFor go
->     <+> commaSep (map privAct pas)
->     <+> text "on" <+> privObj po
->     <+> text "from" <+> commaSep (map name rs)
->     <+> dropBehav db
->   where
->     grantOptFor GrantOptionFor = texts ["grant","option","for"]
->     grantOptFor NoGrantOptionFor = empty
-
-> statement _ (RevokeRole ao rs trs db) =
->     text "revoke"
->     <+> adminOptFor ao
->     <+> commaSep (map name rs)
->     <+> text "from" <+> commaSep (map name trs)
->     <+> dropBehav db
->   where
->     adminOptFor AdminOptionFor = texts ["admin","option","for"]
->     adminOptFor NoAdminOptionFor = empty
-
-
-> statement _ (StatementComment cs) = vcat $ map comment cs
-
-
-== sessions
-
-
-== extras
-
-> dropBehav :: DropBehaviour -> Doc
-> dropBehav DefaultDropBehaviour = empty
-> dropBehav Cascade = text "cascade"
-> dropBehav Restrict = text "restrict"
-
-
-> columnDef :: Dialect -> ColumnDef -> Doc
-> columnDef d (ColumnDef n t mdef cons) =
->       name n <+> typeName t
->       <+> case mdef of
->              Nothing -> empty
->              Just (DefaultClause def) ->
->                  text "default" <+> scalarExpr d def
->              Just (GenerationClause e) ->
->                  texts ["generated","always","as"] <+> parens (scalarExpr d e)
->              Just (IdentityColumnSpec w o) ->
->                  text "generated"
->                  <+> (case w of
->                          GeneratedAlways -> text "always"
->                          GeneratedByDefault -> text "by" <+> text "default")
->                  <+> text "as" <+> text "identity"
->                  <+> (case o of
->                          [] -> empty
->                          os -> parens (sep $ map sequenceGeneratorOption os))
->       <+> sep (map cdef cons)
->   where
->     cdef (ColConstraintDef cnm con) =
->         maybe empty (\s -> text "constraint" <+> names s) cnm
->         <+> pcon con
->     pcon ColNotNullConstraint = texts ["not","null"]
->     pcon ColUniqueConstraint = text "unique"
->     pcon ColPrimaryKeyConstraint = texts ["primary","key"]
->     pcon (ColCheckConstraint v) = text "check" <+> parens (scalarExpr d v)
->     pcon (ColReferencesConstraint tb c m u del) =
->         text "references"
->         <+> names tb
->         <+> maybe empty (\c' -> parens (name c')) c
->         <+> refMatch m
->         <+> refAct "update" u
->         <+> refAct "delete" del
-
-> sequenceGeneratorOption :: SequenceGeneratorOption -> Doc
-> sequenceGeneratorOption (SGODataType t) =
->     text "as" <+> typeName t
-> sequenceGeneratorOption (SGORestart mi) =
->     text "restart" <+> maybe empty (\mi' -> texts ["with", show mi']) mi
-> sequenceGeneratorOption (SGOStartWith i) = texts ["start",  "with", show i]
-> sequenceGeneratorOption (SGOIncrementBy i) = texts ["increment", "by", show i]
-> sequenceGeneratorOption (SGOMaxValue i) = texts ["maxvalue", show i]
-> sequenceGeneratorOption SGONoMaxValue = texts ["no", "maxvalue"]
-> sequenceGeneratorOption (SGOMinValue i) = texts ["minvalue", show i]
-> sequenceGeneratorOption SGONoMinValue = texts ["no", "minvalue"]
-> sequenceGeneratorOption SGOCycle = text "cycle"
-> sequenceGeneratorOption SGONoCycle = text "no cycle"
-
-> refMatch :: ReferenceMatch -> Doc
-> refMatch m = case m of
->                      DefaultReferenceMatch -> empty
->                      MatchFull -> texts ["match", "full"]
->                      MatchPartial -> texts ["match","partial"]
->                      MatchSimple -> texts ["match", "simple"]
-
-> refAct :: String -> ReferentialAction -> Doc
-> refAct t a = case a of
->                      DefaultReferentialAction -> empty
->                      RefCascade -> texts ["on", t, "cascade"]
->                      RefSetNull -> texts ["on", t, "set", "null"]
->                      RefSetDefault -> texts ["on", t, "set", "default"]
->                      RefRestrict -> texts ["on", t, "restrict"]
->                      RefNoAction -> texts ["on", t, "no", "action"]
-
-> alterTableAction :: Dialect -> AlterTableAction -> Doc
-> alterTableAction d (AddColumnDef cd) =
->     texts ["add", "column"] <+> columnDef d cd
-
-> alterTableAction d (AlterColumnSetDefault n v) =
->     texts ["alter", "column"]
->     <+> name n
->     <+> texts ["set","default"] <+> scalarExpr d v
-> alterTableAction _ (AlterColumnDropDefault n) =
->     texts ["alter", "column"]
->     <+> name n
->     <+> texts ["drop","default"]
-
-> alterTableAction _ (AlterColumnSetNotNull n) =
->     texts ["alter", "column"]
->     <+> name n
->     <+> texts ["set","not","null"]
-
-> alterTableAction _ (AlterColumnDropNotNull n) =
->     texts ["alter", "column"]
->     <+> name n
->     <+> texts ["drop","not","null"]
-
-> alterTableAction _ (AlterColumnSetDataType n t) =
->     texts ["alter", "column"]
->     <+> name n
->     <+> texts ["set","data","Type"]
->     <+> typeName t
-
-> alterTableAction _ (DropColumn n b) =
->     texts ["drop", "column"]
->     <+> name n
->     <+> dropBehav b
-
-> alterTableAction d (AddTableConstraintDef n con) =
->     text "add"
->     <+> maybe empty (\s -> text "constraint" <+> names s) n
->     <+> tableConstraint d con
-
-> alterTableAction _ (DropTableConstraintDef n b) =
->     texts ["drop", "constraint"]
->     <+> names n
->     <+> dropBehav b
-
-
-> tableConstraint :: Dialect -> TableConstraint -> Doc
-> tableConstraint _ (TableUniqueConstraint ns) =
->          text "unique" <+> parens (commaSep $ map name ns)
-> tableConstraint _ (TablePrimaryKeyConstraint ns) =
->         texts ["primary","key"] <+> parens (commaSep $ map name ns)
-> tableConstraint _ (TableReferencesConstraint cs t tcs m u del) =
->         texts ["foreign", "key"]
->         <+> parens (commaSep $ map name cs)
->         <+> text "references"
->         <+> names t
->         <+> maybe empty (\c' -> parens (commaSep $ map name c')) tcs
->         <+> refMatch m
->         <+> refAct "update" u
->         <+> refAct "delete" del
-> tableConstraint d (TableCheckConstraint v) = text "check" <+> parens (scalarExpr d v)
-
-
-> privAct :: PrivilegeAction -> Doc
-> privAct PrivAll = texts ["all","privileges"]
-> privAct (PrivSelect cs) = text "select" <+> maybeColList cs
-> privAct (PrivInsert cs) = text "insert" <+> maybeColList cs
-> privAct (PrivUpdate cs) = text "update" <+> maybeColList cs
-> privAct (PrivReferences cs) = text "references" <+> maybeColList cs
-> privAct PrivDelete = text "delete"
-> privAct PrivUsage = text "usage"
-> privAct PrivTrigger = text "trigger"
-> privAct PrivExecute = text "execute"
-
-> maybeColList :: [Name] -> Doc
-> maybeColList cs =
->     if null cs
->     then empty
->     else parens (commaSep $ map name cs)
-
-> privObj :: PrivilegeObject -> Doc
-> privObj (PrivTable nm) = names nm
-> privObj (PrivDomain nm) = text "domain" <+> names nm
-> privObj (PrivType nm) = text "type" <+> names nm
-> privObj (PrivSequence nm) = text "sequence" <+> names nm
-> privObj (PrivFunction nm) = texts ["specific", "function"] <+> names nm
-
-= utils
-
-> commaSep :: [Doc] -> Doc
-> commaSep ds = sep $ punctuate comma ds
-
-> me :: (a -> Doc) -> Maybe a -> Doc
-> me = maybe empty
-
-> comment :: Comment -> Doc
-> comment (BlockComment str) = text "/*" <+> text str <+> text "*/"
-
-> texts :: [String] -> Doc
-> texts ts = sep $ map text ts
diff --git a/Language/SQL/SimpleSQL/Syntax.hs b/Language/SQL/SimpleSQL/Syntax.hs
new file mode 100644
--- /dev/null
+++ b/Language/SQL/SimpleSQL/Syntax.hs
@@ -0,0 +1,771 @@
+
+-- | The AST for SQL.
+{-# LANGUAGE DeriveDataTypeable #-}
+module Language.SQL.SimpleSQL.Syntax
+    (-- * Scalar expressions
+     ScalarExpr(..)
+    ,Name(..)
+    ,TypeName(..)
+    ,IntervalTypeField(..)
+    ,Sign(..)
+    ,PrecMultiplier(..)
+    ,PrecUnits(..)
+    ,SetQuantifier(..)
+    ,SortSpec(..)
+    ,Direction(..)
+    ,NullsOrder(..)
+    ,InPredValue(..)
+    ,SubQueryExprType(..)
+    ,CompPredQuantifier(..)
+    ,Frame(..)
+    ,FrameRows(..)
+    ,FramePos(..)
+    ,OdbcLiteralType(..)
+     -- * Query expressions
+    ,QueryExpr(..)
+    ,SetOperatorName(..)
+    ,Corresponding(..)
+    ,Alias(..)
+    ,GroupingExpr(..)
+     -- ** From
+    ,TableRef(..)
+    ,JoinType(..)
+    ,JoinCondition(..)
+     -- * Statements
+    ,Statement(..)
+    ,DropBehaviour(..)
+    ,IdentityRestart(..)
+    ,InsertSource(..)
+    ,SetClause(..)
+    ,TableElement(..)
+    ,ColumnDef(..)
+    ,DefaultClause(..)
+    ,IdentityWhen(..)
+    ,SequenceGeneratorOption(..)
+    ,ColConstraintDef(..)
+    ,AutoincrementClause
+    ,ColConstraint(..)
+    ,TableConstraint(..)
+    ,ReferenceMatch(..)
+    ,ReferentialAction(..)
+    ,AlterTableAction(..)
+    ,CheckOption(..)
+    ,AlterDomainAction(..)
+    ,AdminOption(..)
+    ,GrantOption(..)
+    ,PrivilegeObject(..)
+    ,PrivilegeAction(..)
+    ,AdminOptionFor(..)
+    ,GrantOptionFor(..)
+     -- * Comment
+    ,Comment(..)
+
+    ,makeSelect
+    ,toQueryExpr
+    ,MakeSelect(..)
+    ) where
+
+import Data.Text (Text)
+
+import Data.Data (Data, Typeable)
+
+-- | Represents a value expression. This is used for the expressions
+-- in select lists. It is also used for expressions in where, group
+-- by, having, order by and so on.
+data ScalarExpr
+    = -- | a numeric literal optional decimal point, e+-
+      -- integral exponent, e.g
+      --
+      -- * 10
+      --
+      -- * 10.
+      --
+      -- * .1
+      --
+      -- * 10.1
+      --
+      -- * 1e5
+      --
+      -- * 12.34e-6
+      NumLit Text
+      -- | string literal, with the start and end quote
+      -- e.g. 'test' -> TextLit "'" "'" "test"
+    | StringLit Text Text Text
+      -- | text of interval literal, units of interval precision,
+      -- e.g. interval 3 days (3)
+    | IntervalLit
+      {ilSign :: Maybe Sign -- ^ if + or - used
+      ,ilLiteral :: Text -- ^ literal text
+      ,ilFrom :: IntervalTypeField
+      ,ilTo :: Maybe IntervalTypeField
+      }
+
+      -- | prefix 'typed literal', e.g. int '42'
+    | TypedLit TypeName Text
+
+      -- | identifier with parts separated by dots
+    | Iden [Name]
+      -- | star, as in select *, t.*, count(*)
+    | Star
+
+    | Parameter -- ^ Represents a ? in a parameterized query
+    | PositionalArg Int -- ^ Represents an e.g. $1 in a parameterized query
+    | HostParameter Text (Maybe Text) -- ^ represents a host
+                                          -- parameter, e.g. :a. The
+                                          -- Maybe Text is for the
+                                          -- indicator, e.g. :var
+                                          -- indicator :nl
+
+
+      -- | 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 [Name] ScalarExpr
+      -- | Prefix unary operators. This is used for symbol
+      -- operators, keyword operators and multiple keyword operators.
+    | PrefixOp [Name] ScalarExpr
+      -- | Postfix unary operators. This is used for symbol
+      -- operators, keyword operators and multiple keyword operators.
+    | PostfixOp [Name] ScalarExpr
+      -- | Used for ternary, mixfix and other non orthodox
+      -- operators. Currently used for row constructors, and for
+      -- between.
+    | SpecialOp [Name] [ScalarExpr]
+
+      -- | function application (anything that looks like c style
+      -- function application syntactically)
+    | App [Name] [ScalarExpr]
+
+
+      -- | aggregate application, which adds distinct or all, and
+      -- order by, to regular function application
+    | AggregateApp
+      {aggName :: [Name] -- ^ aggregate function name
+      ,aggDistinct :: SetQuantifier -- ^ distinct
+      ,aggArgs :: [ScalarExpr]-- ^ args
+      ,aggOrderBy :: [SortSpec] -- ^ order by
+      ,aggFilter :: Maybe ScalarExpr -- ^ filter
+      }
+      -- | aggregates with within group
+    | AggregateAppGroup
+      {aggName :: [Name] -- ^ aggregate function name
+      ,aggArgs :: [ScalarExpr] -- ^ args
+      ,aggGroup :: [SortSpec] -- ^ within group
+      }
+      -- | window application, which adds over (partition by a order
+      -- by b) to regular function application. Explicit frames are
+      -- not currently supported
+    | WindowApp
+      {wnName :: [Name] -- ^ window function name
+      ,wnArgs :: [ScalarExpr] -- ^ args
+      ,wnPartition :: [ScalarExpr] -- ^ partition by
+      ,wnOrderBy :: [SortSpec] -- ^ order by
+      ,wnFrame :: Maybe Frame -- ^ frame clause
+      }
+
+      -- | Used for the operators which look like functions
+      -- except the arguments are separated by keywords instead
+      -- of commas. The maybe is for the first unnamed argument
+      -- if it is present, and the list is for the keyword argument
+      -- pairs.
+    | SpecialOpK [Name] (Maybe ScalarExpr) [(Text,ScalarExpr)]
+
+      -- | cast(a as typename)
+    | Cast ScalarExpr TypeName
+
+      -- | convert expression to given datatype @CONVERT(data_type(length), expression, style)@
+    | Convert TypeName ScalarExpr (Maybe Integer)
+
+      -- | case expression. both flavours supported
+    | Case
+      {caseTest :: Maybe ScalarExpr -- ^ test value
+      ,caseWhens :: [([ScalarExpr],ScalarExpr)] -- ^ when branches
+      ,caseElse :: Maybe ScalarExpr -- ^ else value
+      }
+
+    | Parens ScalarExpr
+
+      -- | in list literal and in subquery, if the bool is false it
+      -- means not in was used ('a not in (1,2)')
+    | In Bool ScalarExpr InPredValue
+
+      -- | exists, all, any, some subqueries
+    | SubQueryExpr SubQueryExprType QueryExpr
+
+    | QuantifiedComparison
+            ScalarExpr
+            [Name] -- operator
+            CompPredQuantifier
+            QueryExpr
+
+    | Match ScalarExpr Bool -- true if unique
+          QueryExpr
+    | Array ScalarExpr [ScalarExpr] -- ^ represents an array
+                                  -- access expression, or an array ctor
+                                  -- e.g. a[3]. The first
+                                  -- scalarExpr is the array, the
+                                  -- second is the subscripts/ctor args
+    | ArrayCtor QueryExpr -- ^ this is used for the query expression version of array constructors, e.g. array(select * from t)
+
+{-
+todo: special syntax for like, similar with escape - escape cannot go
+in other places
+-}
+
+    --  | Escape ScalarExpr Char
+    --  | UEscape ScalarExpr Char
+    | Collate ScalarExpr [Name]
+    | MultisetBinOp ScalarExpr SetOperatorName SetQuantifier ScalarExpr
+    | MultisetCtor [ScalarExpr]
+    | MultisetQueryCtor QueryExpr
+    | NextValueFor [Name]
+    | VEComment [Comment] ScalarExpr
+    | OdbcLiteral OdbcLiteralType Text
+      -- ^ an odbc literal e.g. {d '2000-01-01'}
+    | OdbcFunc ScalarExpr
+      -- ^ an odbc function call e.g. {fn CHARACTER_LENGTH('test')}
+      deriving (Eq,Show,Read,Data,Typeable)
+
+-- | Represents an identifier name, which can be quoted or unquoted.
+-- examples:
+--
+-- * test -> Name Nothing "test"
+-- * "test" -> Name (Just "\"","\"") "test"
+-- * `something` -> Name (Just ("`","`") "something"
+-- * [ms] -> Name (Just ("[","]") "ms"
+data Name = Name (Maybe (Text,Text)) Text
+            deriving (Eq,Show,Read,Data,Typeable)
+
+-- | Represents a type name, used in casts.
+data TypeName
+    = TypeName [Name]
+    | PrecTypeName [Name] Integer
+    | PrecScaleTypeName [Name] Integer Integer
+    | PrecLengthTypeName [Name] Integer (Maybe PrecMultiplier) (Maybe PrecUnits)
+      -- precision, characterset, collate
+    | CharTypeName [Name] (Maybe Integer) [Name] [Name]
+    | TimeTypeName [Name] (Maybe Integer) Bool -- true == with time zone
+    | RowTypeName [(Name,TypeName)]
+    | IntervalTypeName IntervalTypeField (Maybe IntervalTypeField)
+    | ArrayTypeName TypeName (Maybe Integer)
+    | MultisetTypeName TypeName
+      deriving (Eq,Show,Read,Data,Typeable)
+
+data IntervalTypeField = Itf Text (Maybe (Integer, Maybe Integer))
+                         deriving (Eq,Show,Read,Data,Typeable)
+
+data Sign = Plus | Minus
+            deriving (Eq,Show,Read,Data,Typeable)
+
+data PrecMultiplier = PrecK | PrecM | PrecG | PrecT | PrecP
+                      deriving (Eq,Show,Read,Data,Typeable)
+data PrecUnits = PrecCharacters
+               | PrecOctets
+                deriving (Eq,Show,Read,Data,Typeable)
+
+-- | Used for 'expr in (scalar expression list)', and 'expr in
+-- (subquery)' syntax.
+data InPredValue = InList [ScalarExpr]
+                 | InQueryExpr QueryExpr
+                   deriving (Eq,Show,Read,Data,Typeable)
+
+-- not sure if scalar subquery, exists and unique should be represented like this
+
+-- | A subquery in a scalar expression.
+data SubQueryExprType
+    = -- | exists (query expr)
+      SqExists
+      -- | unique (query expr)
+    | SqUnique
+      -- | a scalar subquery
+    | SqSq
+      deriving (Eq,Show,Read,Data,Typeable)
+
+data CompPredQuantifier
+    = CPAny
+    | CPSome
+    | CPAll
+      deriving (Eq,Show,Read,Data,Typeable)
+
+-- | Represents one field in an order by list.
+data SortSpec = SortSpec ScalarExpr Direction NullsOrder
+                deriving (Eq,Show,Read,Data,Typeable)
+
+-- | Represents 'nulls first' or 'nulls last' in an order by clause.
+data NullsOrder = NullsOrderDefault
+                | NullsFirst
+                | NullsLast
+                  deriving (Eq,Show,Read,Data,Typeable)
+
+-- | 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,Data,Typeable)
+
+-- | Represents whether a window frame clause is over rows or ranges.
+data FrameRows = FrameRows | FrameRange
+                 deriving (Eq,Show,Read,Data,Typeable)
+
+-- | represents the start or end of a frame
+data FramePos = UnboundedPreceding
+              | Preceding ScalarExpr
+              | Current
+              | Following ScalarExpr
+              | UnboundedFollowing
+                deriving (Eq,Show,Read,Data,Typeable)
+
+
+-- | the type of an odbc literal (e.g. {d '2000-01-01'}),
+-- correpsonding to the letter after the opening {
+data OdbcLiteralType = OLDate
+                     | OLTime
+                     | OLTimestamp
+                       deriving (Eq,Show,Read,Data,Typeable)
+
+
+-- | Represents a query expression, which can be:
+--
+-- * a regular select;
+--
+-- * a set operator (union, except, intersect);
+--
+-- * a common table expression (with);
+--
+-- * a table value constructor (values (1,2),(3,4)); or
+--
+-- * an explicit table (table t).
+data QueryExpr
+    = Select
+      {qeSetQuantifier :: SetQuantifier
+      ,qeSelectList :: [(ScalarExpr,Maybe Name)]
+       -- ^ the expressions and the column aliases
+
+{-
+TODO: consider breaking this up. The SQL grammar has
+queryexpr = select <select list> [<table expression>]
+table expression = <from> [where] [groupby] [having] ...
+
+This would make some things a bit cleaner?
+-}
+
+      ,qeFrom :: [TableRef]
+      ,qeWhere :: Maybe ScalarExpr
+      ,qeGroupBy :: [GroupingExpr]
+      ,qeHaving :: Maybe ScalarExpr
+      ,qeOrderBy :: [SortSpec]
+      ,qeOffset :: Maybe ScalarExpr
+      ,qeFetchFirst :: Maybe ScalarExpr
+      }
+    | QueryExprSetOp
+      {qe0 :: QueryExpr
+      ,qeCombOp :: SetOperatorName
+      ,qeSetQuantifier :: SetQuantifier
+      ,qeCorresponding :: Corresponding
+      ,qe1 :: QueryExpr
+      }
+    | With
+      {qeWithRecursive :: Bool
+      ,qeViews :: [(Alias,QueryExpr)]
+      ,qeQueryExpression :: QueryExpr}
+    | Values [[ScalarExpr]]
+    | Table [Name]
+    | QEComment [Comment] QueryExpr
+      deriving (Eq,Show,Read,Data,Typeable)
+
+{-
+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 SetQuantifier = SQDefault | Distinct | All deriving (Eq,Show,Read,Data,Typeable)
+
+-- | The direction for a column in order by.
+data Direction = DirDefault | Asc | Desc deriving (Eq,Show,Read,Data,Typeable)
+-- | Query expression set operators.
+data SetOperatorName = Union | Except | Intersect deriving (Eq,Show,Read,Data,Typeable)
+-- | Corresponding, an option for the set operators.
+data Corresponding = Corresponding | Respectively deriving (Eq,Show,Read,Data,Typeable)
+
+-- | Represents an item in a group by clause.
+data GroupingExpr
+    = GroupingParens [GroupingExpr]
+    | Cube [GroupingExpr]
+    | Rollup [GroupingExpr]
+    | GroupingSets [GroupingExpr]
+    | SimpleGroup ScalarExpr
+      deriving (Eq,Show,Read,Data,Typeable)
+
+-- | Represents a entry in the csv of tables in the from clause.
+data TableRef = -- | from t / from s.t
+                TRSimple [Name]
+                -- | from a join b, the bool is true if natural was used
+              | TRJoin TableRef Bool JoinType TableRef (Maybe JoinCondition)
+                -- | from (a)
+              | TRParens TableRef
+                -- | from a as b(c,d)
+              | TRAlias TableRef Alias
+                -- | from (query expr)
+              | TRQueryExpr QueryExpr
+                -- | from function(args)
+              | TRFunction [Name] [ScalarExpr]
+                -- | from lateral t
+              | TRLateral TableRef
+                -- | ODBC {oj t1 left outer join t2 on expr} syntax
+              | TROdbc TableRef
+                deriving (Eq,Show,Read,Data,Typeable)
+
+-- | 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,Data,Typeable)
+
+-- | The type of a join.
+data JoinType = JInner | JLeft | JRight | JFull | JCross
+                deriving (Eq,Show,Read,Data,Typeable)
+
+-- | The join condition.
+data JoinCondition = JoinOn ScalarExpr -- ^ on expr
+                   | JoinUsing [Name] -- ^ using (column list)
+                     deriving (Eq,Show,Read,Data,Typeable)
+
+-- ---------------------------
+
+data Statement =
+    -- ddl
+    CreateSchema [Name]
+  | DropSchema [Name] DropBehaviour
+  | CreateTable [Name] [TableElement]
+  | AlterTable [Name] AlterTableAction
+  | DropTable [Name] DropBehaviour
+  | CreateIndex Bool [Name] [Name] [Name]
+  | CreateView Bool [Name] (Maybe [Name])
+        QueryExpr (Maybe CheckOption)
+  | DropView [Name]  DropBehaviour
+  | CreateDomain [Name] TypeName (Maybe ScalarExpr)
+       [(Maybe [Name], ScalarExpr)]
+  | AlterDomain [Name] AlterDomainAction
+  | DropDomain [Name] DropBehaviour
+
+    -- probably won't do character sets, collations
+    -- and translations because I think they are too far from
+    -- reality
+  {-  | CreateCharacterSet
+  | DropCharacterSet
+  | CreateCollation
+  | DropCollation
+  | CreateTranslation
+  | DropTranslation -}
+  | CreateAssertion [Name] ScalarExpr
+  | DropAssertion [Name] DropBehaviour
+  {-   | CreateTrigger
+  | DropTrigger
+  | CreateType
+  | AlterType
+  | DropType
+    -- routine stuff? TODO
+  | CreateCast
+  | DropCast
+  | CreateOrdering
+  | DropOrdering -}
+    -- transforms
+  | CreateSequence [Name] [SequenceGeneratorOption]
+  | AlterSequence [Name] [SequenceGeneratorOption]
+  | DropSequence [Name] DropBehaviour
+    -- dml
+  | SelectStatement QueryExpr
+  {-    | DeclareCursor
+  | OpenCursor
+  | FetchCursor
+  | CloseCursor
+  | SelectInto -}
+  --   | DeletePositioned
+  | Delete [Name] (Maybe Name) (Maybe ScalarExpr)
+  | Truncate [Name] IdentityRestart
+  | Insert [Name] (Maybe [Name]) InsertSource
+  --  | Merge
+  | Update [Name] (Maybe Name) [SetClause] (Maybe ScalarExpr)
+  {-  | TemporaryTable
+  | FreeLocator
+  | HoldLocator  -}
+    -- access control
+  | GrantPrivilege [PrivilegeAction] PrivilegeObject [Name] GrantOption
+  | GrantRole [Name] [Name] AdminOption
+  | CreateRole Name
+  | DropRole Name
+  | RevokePrivilege GrantOptionFor [PrivilegeAction] PrivilegeObject
+            [Name] DropBehaviour
+  | RevokeRole AdminOptionFor [Name] [Name] DropBehaviour
+    -- transaction management
+  | StartTransaction
+  --  | SetTransaction
+  --  | SetContraints
+  | Savepoint Name
+  | ReleaseSavepoint Name
+  | Commit
+  | Rollback (Maybe Name)
+    -- session
+  {-  | SetSessionCharacteristics
+  | SetSessionAuthorization
+  | SetRole
+  | SetTimeZone
+  | SetCatalog
+  | SetSchema
+  | SetNames
+  | SetTransform
+  | SetCollation -}
+  | StatementComment [Comment]
+  | EmptyStatement
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data DropBehaviour =
+    Restrict
+  | Cascade
+  | DefaultDropBehaviour
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data IdentityRestart =
+    ContinueIdentity
+  | RestartIdentity
+  | DefaultIdentityRestart
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data InsertSource =
+    InsertQuery QueryExpr
+  | DefaultInsertValues
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data SetClause =
+    Set [Name] ScalarExpr
+  | SetMultiple [[Name]] [ScalarExpr]
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data TableElement =
+    TableColumnDef ColumnDef
+  | TableConstraintDef (Maybe [Name]) TableConstraint
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data ColumnDef = ColumnDef Name TypeName
+       (Maybe DefaultClause)
+       [ColConstraintDef]
+       -- (Maybe CollateClause)
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data ColConstraintDef =
+    ColConstraintDef (Maybe [Name]) ColConstraint
+      -- (Maybe [ConstraintCharacteristics])
+    deriving (Eq,Show,Read,Data,Typeable)
+
+type AutoincrementClause = Bool
+
+data ColConstraint =
+    ColNullableConstraint
+  | ColNotNullConstraint
+  | ColUniqueConstraint
+  | ColPrimaryKeyConstraint AutoincrementClause
+  | ColReferencesConstraint [Name] (Maybe Name)
+       ReferenceMatch
+       ReferentialAction
+       ReferentialAction
+  | ColCheckConstraint ScalarExpr
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data TableConstraint =
+    TableUniqueConstraint [Name]
+  | TablePrimaryKeyConstraint [Name]
+  | TableReferencesConstraint [Name] [Name] (Maybe [Name])
+       ReferenceMatch
+       ReferentialAction
+       ReferentialAction
+  | TableCheckConstraint ScalarExpr
+    deriving (Eq,Show,Read,Data,Typeable)
+
+
+data ReferenceMatch =
+    DefaultReferenceMatch
+  | MatchFull
+  | MatchPartial
+  | MatchSimple
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data ReferentialAction =
+    DefaultReferentialAction
+  | RefCascade
+  | RefSetNull
+  | RefSetDefault
+  | RefRestrict
+  | RefNoAction
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data AlterTableAction =
+    AddColumnDef ColumnDef
+  | AlterColumnSetDefault Name ScalarExpr
+  | AlterColumnDropDefault Name
+  | AlterColumnSetNotNull Name
+  | AlterColumnDropNotNull Name
+  | AlterColumnSetDataType Name TypeName
+  {-  | AlterColumnAlterIdentity
+  | AlterColumnDropIdentity
+  | AlterColumnDropColumnGeneration-}
+  | DropColumn Name DropBehaviour
+  | AddTableConstraintDef (Maybe [Name]) TableConstraint
+  --  | AlterTableConstraintDef
+  | DropTableConstraintDef [Name] DropBehaviour
+    deriving (Eq,Show,Read,Data,Typeable)
+
+{-data ConstraintCharacteristics =
+    ConstraintCharacteristics
+        ConstraintCheckTime
+        Deferrable
+        ConstraintEnforcement
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data ConstraintCheckTime =
+    DefaultConstraintCheckTime
+  | InitiallyDeferred
+  | InitiallyImmeditate
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data Deferrable =
+    DefaultDefferable
+  | Deferrable
+  | NotDeferrable
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data ConstraintEnforcement =
+    DefaultConstraintEnforcement
+  | Enforced
+  | NotEnforced
+    deriving (Eq,Show,Read,Data,Typeable) -}
+
+{-data TableConstraintDef
+    deriving (Eq,Show,Read,Data,Typeable) -}
+
+data DefaultClause =
+     DefaultClause ScalarExpr
+   | IdentityColumnSpec IdentityWhen [SequenceGeneratorOption]
+   | GenerationClause ScalarExpr
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data IdentityWhen =
+    GeneratedAlways
+  | GeneratedByDefault
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data SequenceGeneratorOption =
+    SGODataType TypeName
+  | SGOStartWith Integer
+  | SGORestart (Maybe Integer)
+  | SGOIncrementBy Integer
+  | SGOMaxValue Integer
+  | SGONoMaxValue
+  | SGOMinValue Integer
+  | SGONoMinValue
+  | SGOCycle
+  | SGONoCycle
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data CheckOption =
+    DefaultCheckOption
+  | CascadedCheckOption
+  | LocalCheckOption
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data AlterDomainAction =
+    ADSetDefault ScalarExpr
+  | ADDropDefault
+  | ADAddConstraint (Maybe [Name]) ScalarExpr
+  | ADDropConstraint [Name]
+    deriving (Eq,Show,Read,Data,Typeable)
+
+
+data AdminOption = WithAdminOption | WithoutAdminOption
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data GrantOption = WithGrantOption | WithoutGrantOption
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data AdminOptionFor = AdminOptionFor | NoAdminOptionFor
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data GrantOptionFor = GrantOptionFor | NoGrantOptionFor
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data PrivilegeObject =
+      PrivTable [Name]
+    | PrivDomain [Name]
+    | PrivType [Name]
+    | PrivSequence [Name]
+    | PrivFunction [Name]
+    deriving (Eq,Show,Read,Data,Typeable)
+
+data PrivilegeAction =
+      PrivAll
+    | PrivSelect [Name]
+    | PrivDelete
+    | PrivInsert [Name]
+    | PrivUpdate [Name]
+    | PrivReferences [Name]
+    | PrivUsage
+    | PrivTrigger
+    | PrivExecute
+    deriving (Eq,Show,Read,Data,Typeable)
+
+-- | Comment. Useful when generating SQL code programmatically. The
+-- parser doesn't produce these.
+newtype Comment = BlockComment Text
+               deriving (Eq,Show,Read,Data,Typeable)
+
+data MakeSelect
+    = MakeSelect
+    {msSetQuantifier :: SetQuantifier
+    ,msSelectList :: [(ScalarExpr,Maybe Name)]
+    ,msFrom :: [TableRef]
+    ,msWhere :: Maybe ScalarExpr
+    ,msGroupBy :: [GroupingExpr]
+    ,msHaving :: Maybe ScalarExpr
+    ,msOrderBy :: [SortSpec]
+    ,msOffset :: Maybe ScalarExpr
+    ,msFetchFirst :: Maybe ScalarExpr
+    }
+
+-- | Helper/'default' value for query exprs to make creating query
+-- expr values a little easier. It is defined like this:
+--
+-- > makeSelect :: MakeSelect
+-- > makeSelect
+-- >     = MakeSelect
+-- >     {msSetQuantifier = SQDefault
+-- >     ,msSelectList = []
+-- >     ,msFrom = []
+-- >     ,msWhere = Nothing
+-- >     ,msGroupBy = []
+-- >     ,msHaving = Nothing
+-- >     ,msOrderBy = []
+-- >     ,msOffset = Nothing
+-- >     ,msFetchFirst = Nothing}
+-- >
+-- > Example, to create a select query expression with a select list 'sl':
+-- >   toQueryExpr $ makeSelect {msSelectList = sl}
+
+makeSelect :: MakeSelect
+makeSelect
+    = MakeSelect
+    {msSetQuantifier = SQDefault
+    ,msSelectList = []
+    ,msFrom = []
+    ,msWhere = Nothing
+    ,msGroupBy = []
+    ,msHaving = Nothing
+    ,msOrderBy = []
+    ,msOffset = Nothing
+    ,msFetchFirst = Nothing}
+
+toQueryExpr :: MakeSelect -> QueryExpr
+toQueryExpr (MakeSelect q sl f w g h o ff fetch) = Select q sl f w g h o ff fetch
diff --git a/Language/SQL/SimpleSQL/Syntax.lhs b/Language/SQL/SimpleSQL/Syntax.lhs
deleted file mode 100644
--- a/Language/SQL/SimpleSQL/Syntax.lhs
+++ /dev/null
@@ -1,729 +0,0 @@
-
-> -- | The AST for SQL.
-> {-# LANGUAGE DeriveDataTypeable #-}
-> module Language.SQL.SimpleSQL.Syntax
->     (-- * Scalar expressions
->      ScalarExpr(..)
->     ,Name(..)
->     ,TypeName(..)
->     ,IntervalTypeField(..)
->     ,Sign(..)
->     ,PrecMultiplier(..)
->     ,PrecUnits(..)
->     ,SetQuantifier(..)
->     ,SortSpec(..)
->     ,Direction(..)
->     ,NullsOrder(..)
->     ,InPredValue(..)
->     ,SubQueryExprType(..)
->     ,CompPredQuantifier(..)
->     ,Frame(..)
->     ,FrameRows(..)
->     ,FramePos(..)
->     ,OdbcLiteralType(..)
->      -- * Query expressions
->     ,QueryExpr(..)
->     ,makeSelect
->     ,SetOperatorName(..)
->     ,Corresponding(..)
->     ,Alias(..)
->     ,GroupingExpr(..)
->      -- ** From
->     ,TableRef(..)
->     ,JoinType(..)
->     ,JoinCondition(..)
->      -- * Statements
->     ,Statement(..)
->     ,DropBehaviour(..)
->     ,IdentityRestart(..)
->     ,InsertSource(..)
->     ,SetClause(..)
->     ,TableElement(..)
->     ,ColumnDef(..)
->     ,DefaultClause(..)
->     ,IdentityWhen(..)
->     ,SequenceGeneratorOption(..)
->     ,ColConstraintDef(..)
->     ,ColConstraint(..)
->     ,TableConstraint(..)
->     ,ReferenceMatch(..)
->     ,ReferentialAction(..)
->     ,AlterTableAction(..)
->     ,CheckOption(..)
->     ,AlterDomainAction(..)
->     ,AdminOption(..)
->     ,GrantOption(..)
->     ,PrivilegeObject(..)
->     ,PrivilegeAction(..)
->     ,AdminOptionFor(..)
->     ,GrantOptionFor(..)
->      -- * Comment
->     ,Comment(..)
->     ) where
-
-> import Data.Data
-
-> -- | Represents a value expression. This is used for the expressions
-> -- in select lists. It is also used for expressions in where, group
-> -- by, having, order by and so on.
-> data ScalarExpr
->     = -- | a numeric literal optional decimal point, e+-
->       -- integral exponent, e.g
->       --
->       -- * 10
->       --
->       -- * 10.
->       --
->       -- * .1
->       --
->       -- * 10.1
->       --
->       -- * 1e5
->       --
->       -- * 12.34e-6
->       NumLit String
->       -- | string literal, with the start and end quote
->       -- e.g. 'test' -> StringLit "'" "'" "test"
->     | StringLit String String String
->       -- | text of interval literal, units of interval precision,
->       -- e.g. interval 3 days (3)
->     | IntervalLit
->       {ilSign :: Maybe Sign -- ^ if + or - used
->       ,ilLiteral :: String -- ^ literal text
->       ,ilFrom :: IntervalTypeField
->       ,ilTo :: Maybe IntervalTypeField
->       }
-
->       -- | prefix 'typed literal', e.g. int '42'
->     | TypedLit TypeName String
-
->       -- | identifier with parts separated by dots
->     | Iden [Name]
->       -- | star, as in select *, t.*, count(*)
->     | Star
-
->     | Parameter -- ^ Represents a ? in a parameterized query
->     | PositionalArg Int -- ^ Represents an e.g. $1 in a parameterized query
->     | HostParameter String (Maybe String) -- ^ represents a host
->                                           -- parameter, e.g. :a. The
->                                           -- Maybe String is for the
->                                           -- indicator, e.g. :var
->                                           -- indicator :nl
-
-
->       -- | 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 [Name] ScalarExpr
->       -- | Prefix unary operators. This is used for symbol
->       -- operators, keyword operators and multiple keyword operators.
->     | PrefixOp [Name] ScalarExpr
->       -- | Postfix unary operators. This is used for symbol
->       -- operators, keyword operators and multiple keyword operators.
->     | PostfixOp [Name] ScalarExpr
->       -- | Used for ternary, mixfix and other non orthodox
->       -- operators. Currently used for row constructors, and for
->       -- between.
->     | SpecialOp [Name] [ScalarExpr]
-
->       -- | function application (anything that looks like c style
->       -- function application syntactically)
->     | App [Name] [ScalarExpr]
-
-
->       -- | aggregate application, which adds distinct or all, and
->       -- order by, to regular function application
->     | AggregateApp
->       {aggName :: [Name] -- ^ aggregate function name
->       ,aggDistinct :: SetQuantifier -- ^ distinct
->       ,aggArgs :: [ScalarExpr]-- ^ args
->       ,aggOrderBy :: [SortSpec] -- ^ order by
->       ,aggFilter :: Maybe ScalarExpr -- ^ filter
->       }
->       -- | aggregates with within group
->     | AggregateAppGroup
->       {aggName :: [Name] -- ^ aggregate function name
->       ,aggArgs :: [ScalarExpr] -- ^ args
->       ,aggGroup :: [SortSpec] -- ^ within group
->       }
->       -- | window application, which adds over (partition by a order
->       -- by b) to regular function application. Explicit frames are
->       -- not currently supported
->     | WindowApp
->       {wnName :: [Name] -- ^ window function name
->       ,wnArgs :: [ScalarExpr] -- ^ args
->       ,wnPartition :: [ScalarExpr] -- ^ partition by
->       ,wnOrderBy :: [SortSpec] -- ^ order by
->       ,wnFrame :: Maybe Frame -- ^ frame clause
->       }
-
->       -- | Used for the operators which look like functions
->       -- except the arguments are separated by keywords instead
->       -- of commas. The maybe is for the first unnamed argument
->       -- if it is present, and the list is for the keyword argument
->       -- pairs.
->     | SpecialOpK [Name] (Maybe ScalarExpr) [(String,ScalarExpr)]
-
->       -- | cast(a as typename)
->     | Cast ScalarExpr TypeName
-
->       -- | case expression. both flavours supported
->     | Case
->       {caseTest :: Maybe ScalarExpr -- ^ test value
->       ,caseWhens :: [([ScalarExpr],ScalarExpr)] -- ^ when branches
->       ,caseElse :: Maybe ScalarExpr -- ^ else value
->       }
-
->     | Parens ScalarExpr
-
->       -- | in list literal and in subquery, if the bool is false it
->       -- means not in was used ('a not in (1,2)')
->     | In Bool ScalarExpr InPredValue
-
->       -- | exists, all, any, some subqueries
->     | SubQueryExpr SubQueryExprType QueryExpr
-
->     | QuantifiedComparison
->             ScalarExpr
->             [Name] -- operator
->             CompPredQuantifier
->             QueryExpr
-
->     | Match ScalarExpr Bool -- true if unique
->           QueryExpr
->     | Array ScalarExpr [ScalarExpr] -- ^ represents an array
->                                   -- access expression, or an array ctor
->                                   -- e.g. a[3]. The first
->                                   -- scalarExpr is the array, the
->                                   -- second is the subscripts/ctor args
->     | ArrayCtor QueryExpr -- ^ this is used for the query expression version of array constructors, e.g. array(select * from t)
-
-todo: special syntax for like, similar with escape - escape cannot go
-in other places
-
->     --  | Escape ScalarExpr Char
->     --  | UEscape ScalarExpr Char
->     | Collate ScalarExpr [Name]
->     | MultisetBinOp ScalarExpr SetOperatorName SetQuantifier ScalarExpr
->     | MultisetCtor [ScalarExpr]
->     | MultisetQueryCtor QueryExpr
->     | NextValueFor [Name]
->     | VEComment [Comment] ScalarExpr
->     | OdbcLiteral OdbcLiteralType String
->       -- ^ an odbc literal e.g. {d '2000-01-01'}
->     | OdbcFunc ScalarExpr
->       -- ^ an odbc function call e.g. {fn CHARACTER_LENGTH('test')}
->       deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | Represents an identifier name, which can be quoted or unquoted.
-> -- examples:
-> --
-> -- * test -> Name Nothing "test"
-> -- * "test" -> Name (Just "\"","\"") "test"
-> -- * `something` -> Name (Just ("`","`") "something"
-> -- * [ms] -> Name (Just ("[","]") "ms"
-> data Name = Name (Maybe (String,String)) String
->             deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | Represents a type name, used in casts.
-> data TypeName
->     = TypeName [Name]
->     | PrecTypeName [Name] Integer
->     | PrecScaleTypeName [Name] Integer Integer
->     | PrecLengthTypeName [Name] Integer (Maybe PrecMultiplier) (Maybe PrecUnits)
->       -- precision, characterset, collate
->     | CharTypeName [Name] (Maybe Integer) [Name] [Name]
->     | TimeTypeName [Name] (Maybe Integer) Bool -- true == with time zone
->     | RowTypeName [(Name,TypeName)]
->     | IntervalTypeName IntervalTypeField (Maybe IntervalTypeField)
->     | ArrayTypeName TypeName (Maybe Integer)
->     | MultisetTypeName TypeName
->       deriving (Eq,Show,Read,Data,Typeable)
-
-> data IntervalTypeField = Itf String (Maybe (Integer, Maybe Integer))
->                          deriving (Eq,Show,Read,Data,Typeable)
-
-> data Sign = Plus | Minus
->             deriving (Eq,Show,Read,Data,Typeable)
-
-> data PrecMultiplier = PrecK | PrecM | PrecG | PrecT | PrecP
->                       deriving (Eq,Show,Read,Data,Typeable)
-> data PrecUnits = PrecCharacters
->                | PrecOctets
->                 deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | Used for 'expr in (scalar expression list)', and 'expr in
-> -- (subquery)' syntax.
-> data InPredValue = InList [ScalarExpr]
->                  | InQueryExpr QueryExpr
->                    deriving (Eq,Show,Read,Data,Typeable)
-
-not sure if scalar subquery, exists and unique should be represented like this
-
-> -- | A subquery in a scalar expression.
-> data SubQueryExprType
->     = -- | exists (query expr)
->       SqExists
->       -- | unique (query expr)
->     | SqUnique
->       -- | a scalar subquery
->     | SqSq
->       deriving (Eq,Show,Read,Data,Typeable)
-
-> data CompPredQuantifier
->     = CPAny
->     | CPSome
->     | CPAll
->       deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | Represents one field in an order by list.
-> data SortSpec = SortSpec ScalarExpr Direction NullsOrder
->                 deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | Represents 'nulls first' or 'nulls last' in an order by clause.
-> data NullsOrder = NullsOrderDefault
->                 | NullsFirst
->                 | NullsLast
->                   deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | 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,Data,Typeable)
-
-> -- | Represents whether a window frame clause is over rows or ranges.
-> data FrameRows = FrameRows | FrameRange
->                  deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | represents the start or end of a frame
-> data FramePos = UnboundedPreceding
->               | Preceding ScalarExpr
->               | Current
->               | Following ScalarExpr
->               | UnboundedFollowing
->                 deriving (Eq,Show,Read,Data,Typeable)
-
-
-> -- | the type of an odbc literal (e.g. {d '2000-01-01'}),
-> -- correpsonding to the letter after the opening {
-> data OdbcLiteralType = OLDate
->                      | OLTime
->                      | OLTimestamp
->                        deriving (Eq,Show,Read,Data,Typeable)
-
-
-> -- | Represents a query expression, which can be:
-> --
-> -- * a regular select;
-> --
-> -- * a set operator (union, except, intersect);
-> --
-> -- * a common table expression (with);
-> --
-> -- * a table value constructor (values (1,2),(3,4)); or
-> --
-> -- * an explicit table (table t).
-> data QueryExpr
->     = Select
->       {qeSetQuantifier :: SetQuantifier
->       ,qeSelectList :: [(ScalarExpr,Maybe Name)]
->        -- ^ the expressions and the column aliases
-
-TODO: consider breaking this up. The SQL grammar has
-queryexpr = select <select list> [<table expression>]
-table expression = <from> [where] [groupby] [having] ...
-
-This would make some things a bit cleaner?
-
->       ,qeFrom :: [TableRef]
->       ,qeWhere :: Maybe ScalarExpr
->       ,qeGroupBy :: [GroupingExpr]
->       ,qeHaving :: Maybe ScalarExpr
->       ,qeOrderBy :: [SortSpec]
->       ,qeOffset :: Maybe ScalarExpr
->       ,qeFetchFirst :: Maybe ScalarExpr
->       }
->     | QueryExprSetOp
->       {qe0 :: QueryExpr
->       ,qeCombOp :: SetOperatorName
->       ,qeSetQuantifier :: SetQuantifier
->       ,qeCorresponding :: Corresponding
->       ,qe1 :: QueryExpr
->       }
->     | With
->       {qeWithRecursive :: Bool
->       ,qeViews :: [(Alias,QueryExpr)]
->       ,qeQueryExpression :: QueryExpr}
->     | Values [[ScalarExpr]]
->     | Table [Name]
->     | QEComment [Comment] QueryExpr
->       deriving (Eq,Show,Read,Data,Typeable)
-
-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.
-
-> -- | Helper/'default' value for query exprs to make creating query
-> -- expr values a little easier. It is defined like this:
-> --
-> -- > makeSelect :: QueryExpr
-> -- > makeSelect = Select {qeSetQuantifier = SQDefault
-> -- >                     ,qeSelectList = []
-> -- >                     ,qeFrom = []
-> -- >                     ,qeWhere = Nothing
-> -- >                     ,qeGroupBy = []
-> -- >                     ,qeHaving = Nothing
-> -- >                     ,qeOrderBy = []
-> -- >                     ,qeOffset = Nothing
-> -- >                     ,qeFetchFirst = Nothing}
-
-> makeSelect :: QueryExpr
-> makeSelect = Select {qeSetQuantifier = SQDefault
->                     ,qeSelectList = []
->                     ,qeFrom = []
->                     ,qeWhere = Nothing
->                     ,qeGroupBy = []
->                     ,qeHaving = Nothing
->                     ,qeOrderBy = []
->                     ,qeOffset = Nothing
->                     ,qeFetchFirst = 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 SetQuantifier = SQDefault | Distinct | All deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | The direction for a column in order by.
-> data Direction = DirDefault | Asc | Desc deriving (Eq,Show,Read,Data,Typeable)
-> -- | Query expression set operators.
-> data SetOperatorName = Union | Except | Intersect deriving (Eq,Show,Read,Data,Typeable)
-> -- | Corresponding, an option for the set operators.
-> data Corresponding = Corresponding | Respectively deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | Represents an item in a group by clause.
-> data GroupingExpr
->     = GroupingParens [GroupingExpr]
->     | Cube [GroupingExpr]
->     | Rollup [GroupingExpr]
->     | GroupingSets [GroupingExpr]
->     | SimpleGroup ScalarExpr
->       deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | Represents a entry in the csv of tables in the from clause.
-> data TableRef = -- | from t / from s.t
->                 TRSimple [Name]
->                 -- | from a join b, the bool is true if natural was used
->               | TRJoin TableRef Bool JoinType TableRef (Maybe JoinCondition)
->                 -- | from (a)
->               | TRParens TableRef
->                 -- | from a as b(c,d)
->               | TRAlias TableRef Alias
->                 -- | from (query expr)
->               | TRQueryExpr QueryExpr
->                 -- | from function(args)
->               | TRFunction [Name] [ScalarExpr]
->                 -- | from lateral t
->               | TRLateral TableRef
->                 -- | ODBC {oj t1 left outer join t2 on expr} syntax
->               | TROdbc TableRef
->                 deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | 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,Data,Typeable)
-
-> -- | The type of a join.
-> data JoinType = JInner | JLeft | JRight | JFull | JCross
->                 deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | The join condition.
-> data JoinCondition = JoinOn ScalarExpr -- ^ on expr
->                    | JoinUsing [Name] -- ^ using (column list)
->                      deriving (Eq,Show,Read,Data,Typeable)
-
----------------------------
-
-> data Statement =
->     -- ddl
->     CreateSchema [Name]
->   | DropSchema [Name] DropBehaviour
->   | CreateTable [Name] [TableElement]
->   | AlterTable [Name] AlterTableAction
->   | DropTable [Name] DropBehaviour
->   | CreateView Bool [Name] (Maybe [Name])
->         QueryExpr (Maybe CheckOption)
->   | DropView [Name]  DropBehaviour
->   | CreateDomain [Name] TypeName (Maybe ScalarExpr)
->        [(Maybe [Name], ScalarExpr)]
->   | AlterDomain [Name] AlterDomainAction
->   | DropDomain [Name] DropBehaviour
-
->     -- probably won't do character sets, collations
->     -- and translations because I think they are too far from
->     -- reality
->   {-  | CreateCharacterSet
->   | DropCharacterSet
->   | CreateCollation
->   | DropCollation
->   | CreateTranslation
->   | DropTranslation -}
->   | CreateAssertion [Name] ScalarExpr
->   | DropAssertion [Name] DropBehaviour
->   {-   | CreateTrigger
->   | DropTrigger
->   | CreateType
->   | AlterType
->   | DropType
->     -- routine stuff? TODO
->   | CreateCast
->   | DropCast
->   | CreateOrdering
->   | DropOrdering -}
->     -- transforms
->   | CreateSequence [Name] [SequenceGeneratorOption]
->   | AlterSequence [Name] [SequenceGeneratorOption]
->   | DropSequence [Name] DropBehaviour
->     -- dml
->   | SelectStatement QueryExpr
->   {-    | DeclareCursor
->   | OpenCursor
->   | FetchCursor
->   | CloseCursor
->   | SelectInto -}
->   --   | DeletePositioned
->   | Delete [Name] (Maybe Name) (Maybe ScalarExpr)
->   | Truncate [Name] IdentityRestart
->   | Insert [Name] (Maybe [Name]) InsertSource
->   --  | Merge
->   | Update [Name] (Maybe Name) [SetClause] (Maybe ScalarExpr)
->   {-  | TemporaryTable
->   | FreeLocator
->   | HoldLocator  -}
->     -- access control
->   | GrantPrivilege [PrivilegeAction] PrivilegeObject [Name] GrantOption
->   | GrantRole [Name] [Name] AdminOption
->   | CreateRole Name
->   | DropRole Name
->   | RevokePrivilege GrantOptionFor [PrivilegeAction] PrivilegeObject
->             [Name] DropBehaviour
->   | RevokeRole AdminOptionFor [Name] [Name] DropBehaviour
->     -- transaction management
->   | StartTransaction
->   --  | SetTransaction
->   --  | SetContraints
->   | Savepoint Name
->   | ReleaseSavepoint Name
->   | Commit
->   | Rollback (Maybe Name)
->     -- session
->   {-  | SetSessionCharacteristics
->   | SetSessionAuthorization
->   | SetRole
->   | SetTimeZone
->   | SetCatalog
->   | SetSchema
->   | SetNames
->   | SetTransform
->   | SetCollation -}
->   | StatementComment [Comment]
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data DropBehaviour =
->     Restrict
->   | Cascade
->   | DefaultDropBehaviour
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data IdentityRestart =
->     ContinueIdentity
->   | RestartIdentity
->   | DefaultIdentityRestart
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data InsertSource =
->     InsertQuery QueryExpr
->   | DefaultInsertValues
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data SetClause =
->     Set [Name] ScalarExpr
->   | SetMultiple [[Name]] [ScalarExpr]
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data TableElement =
->     TableColumnDef ColumnDef
->   | TableConstraintDef (Maybe [Name]) TableConstraint
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data ColumnDef = ColumnDef Name TypeName
->        (Maybe DefaultClause)
->        [ColConstraintDef]
->        -- (Maybe CollateClause)
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data ColConstraintDef =
->     ColConstraintDef (Maybe [Name]) ColConstraint
->       -- (Maybe [ConstraintCharacteristics])
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data ColConstraint =
->     ColNotNullConstraint
->   | ColUniqueConstraint
->   | ColPrimaryKeyConstraint
->   | ColReferencesConstraint [Name] (Maybe Name)
->        ReferenceMatch
->        ReferentialAction
->        ReferentialAction
->   | ColCheckConstraint ScalarExpr
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data TableConstraint =
->     TableUniqueConstraint [Name]
->   | TablePrimaryKeyConstraint [Name]
->   | TableReferencesConstraint [Name] [Name] (Maybe [Name])
->        ReferenceMatch
->        ReferentialAction
->        ReferentialAction
->   | TableCheckConstraint ScalarExpr
->     deriving (Eq,Show,Read,Data,Typeable)
-
-
-> data ReferenceMatch =
->     DefaultReferenceMatch
->   | MatchFull
->   | MatchPartial
->   | MatchSimple
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data ReferentialAction =
->     DefaultReferentialAction
->   | RefCascade
->   | RefSetNull
->   | RefSetDefault
->   | RefRestrict
->   | RefNoAction
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data AlterTableAction =
->     AddColumnDef ColumnDef
->   | AlterColumnSetDefault Name ScalarExpr
->   | AlterColumnDropDefault Name
->   | AlterColumnSetNotNull Name
->   | AlterColumnDropNotNull Name
->   | AlterColumnSetDataType Name TypeName
->   {-  | AlterColumnAlterIdentity
->   | AlterColumnDropIdentity
->   | AlterColumnDropColumnGeneration-}
->   | DropColumn Name DropBehaviour
->   | AddTableConstraintDef (Maybe [Name]) TableConstraint
->   --  | AlterTableConstraintDef
->   | DropTableConstraintDef [Name] DropBehaviour
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> {-data ConstraintCharacteristics =
->     ConstraintCharacteristics
->         ConstraintCheckTime
->         Deferrable
->         ConstraintEnforcement
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data ConstraintCheckTime =
->     DefaultConstraintCheckTime
->   | InitiallyDeferred
->   | InitiallyImmeditate
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data Deferrable =
->     DefaultDefferable
->   | Deferrable
->   | NotDeferrable
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data ConstraintEnforcement =
->     DefaultConstraintEnforcement
->   | Enforced
->   | NotEnforced
->     deriving (Eq,Show,Read,Data,Typeable) -}
-
-> {-data TableConstraintDef
->     deriving (Eq,Show,Read,Data,Typeable) -}
-
-> data DefaultClause =
->      DefaultClause ScalarExpr
->    | IdentityColumnSpec IdentityWhen [SequenceGeneratorOption]
->    | GenerationClause ScalarExpr
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data IdentityWhen =
->     GeneratedAlways
->   | GeneratedByDefault
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data SequenceGeneratorOption =
->     SGODataType TypeName
->   | SGOStartWith Integer
->   | SGORestart (Maybe Integer)
->   | SGOIncrementBy Integer
->   | SGOMaxValue Integer
->   | SGONoMaxValue
->   | SGOMinValue Integer
->   | SGONoMinValue
->   | SGOCycle
->   | SGONoCycle
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data CheckOption =
->     DefaultCheckOption
->   | CascadedCheckOption
->   | LocalCheckOption
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data AlterDomainAction =
->     ADSetDefault ScalarExpr
->   | ADDropDefault
->   | ADAddConstraint (Maybe [Name]) ScalarExpr
->   | ADDropConstraint [Name]
->     deriving (Eq,Show,Read,Data,Typeable)
-
-
-> data AdminOption = WithAdminOption | WithoutAdminOption
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data GrantOption = WithGrantOption | WithoutGrantOption
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data AdminOptionFor = AdminOptionFor | NoAdminOptionFor
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data GrantOptionFor = GrantOptionFor | NoGrantOptionFor
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data PrivilegeObject =
->       PrivTable [Name]
->     | PrivDomain [Name]
->     | PrivType [Name]
->     | PrivSequence [Name]
->     | PrivFunction [Name]
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> data PrivilegeAction =
->       PrivAll
->     | PrivSelect [Name]
->     | PrivDelete
->     | PrivInsert [Name]
->     | PrivUpdate [Name]
->     | PrivReferences [Name]
->     | PrivUsage
->     | PrivTrigger
->     | PrivExecute
->     deriving (Eq,Show,Read,Data,Typeable)
-
-> -- | Comment. Useful when generating SQL code programmatically. The
-> -- parser doesn't produce these.
-> data Comment = BlockComment String
->                deriving (Eq,Show,Read,Data,Typeable)
-
diff --git a/README b/README
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
-A parser for SQL queries in Haskell.
+A parser for SQL in Haskell.
 
 Homepage: http://jakewheat.github.io/simple-sql-parser/latest
 
-Contact: jakewheatmail@gmail.com
+Contact: jakewheat@tutanota.com
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,3 +1,19 @@
+0.7.0
+	support autoincrement for sqlite
+	support table constraints without separating comma for sqlite
+	switch source from literate to regular haskell
+	use prettyprinter lib instead of pretty
+	parsing nested block comments regressed - post a bug if you need this
+	fixed fixity parsing of union, except and intersect (matches postgres docs now)
+	removed the Errors module - the pretty printer function for errors is in the Parse module
+	parses from and pretty prints to strict Text
+	use strict Text instead of String everywhere
+	tested with latest three main ghc releases (9.8.1, 9.6.4, and 9.4.8) and stack lts 22.5
+	the makeSelect helper is now a distinct type, code using it will need some trivial
+	  tweaks, this is change so that code using makeSelect doesn't emit warnings
+	overhaul website
+0.6.1   added odbc handling to sqlsqerver dialect
+		added sqlserver dialect case for convert function
 0.6.0
         tested with ghc 8.8.1 also
         change the dialect handling - now a dialect is a bunch of flags
diff --git a/examples/SimpleSQLParserTool.hs b/examples/SimpleSQLParserTool.hs
new file mode 100644
--- /dev/null
+++ b/examples/SimpleSQLParserTool.hs
@@ -0,0 +1,105 @@
+
+{-
+Simple command line tool to experiment with simple-sql-parser
+
+Commands:
+
+parse: parse sql from file, stdin or from command line
+lex: lex sql same
+indent: parse then pretty print sql
+
+TODO: this is supposed to be a simple example, but it's a total mess
+write some simple helpers so it's all in text?
+
+-}
+
+{-# LANGUAGE TupleSections #-}
+import System.Environment (getArgs)
+import Control.Monad (forM_, when)
+import Data.Maybe (isJust)
+import System.Exit (exitFailure)
+import Data.List (intercalate)
+import Text.Show.Pretty (ppShow)
+--import Control.Applicative
+
+import qualified Data.Text as T
+
+import Language.SQL.SimpleSQL.Pretty
+    (prettyStatements)
+import Language.SQL.SimpleSQL.Parse
+    (parseStatements
+    ,prettyError)
+import qualified Language.SQL.SimpleSQL.Lex as L
+import Language.SQL.SimpleSQL.Dialect (ansi2011)
+
+
+main :: IO ()
+main = do
+    args <- getArgs
+    case args of
+        [] -> do
+              showHelp $ Just "no command given"
+        (c:as) -> do
+             let cmd = lookup c commands
+             maybe (showHelp (Just "command not recognised"))
+                   (\(_,cmd') -> cmd' as)
+                   cmd
+
+commands :: [(String, (String,[String] -> IO ()))]
+commands =
+    [("help", helpCommand)
+    ,("parse", parseCommand)
+    ,("lex", lexCommand)
+    ,("format", formatCommand)]
+
+showHelp :: Maybe String -> IO ()
+showHelp msg = do
+          maybe (return ()) (\e -> putStrLn $ "Error: " ++ e) msg
+          putStrLn "Usage:\n SimpleSQLParserTool command args"
+          forM_ commands $ \(c, (h,_)) -> do
+               putStrLn $ c ++ "\t" ++ h
+          when (isJust msg) $ exitFailure
+
+helpCommand :: (String,[String] -> IO ())
+helpCommand =
+    ("show help for this progam", \_ -> showHelp Nothing)
+
+getInput :: [String] -> IO (FilePath,String)
+getInput as =
+    case as of
+      ["-"] -> ("",) <$> getContents
+      ("-c":as') -> return ("", unwords as')
+      [filename] -> (filename,) <$> readFile filename
+      _ -> showHelp (Just "arguments not recognised") >> error ""
+
+parseCommand :: (String,[String] -> IO ())
+parseCommand =
+  ("parse SQL from file/stdin/command line (use -c to parse from command line)"
+  ,\args -> do
+      (f,src) <- getInput args
+      either (error . T.unpack . prettyError)
+          (putStrLn . ppShow)
+          $ parseStatements ansi2011 (T.pack f) Nothing (T.pack src)
+  )
+
+lexCommand :: (String,[String] -> IO ())
+lexCommand =
+  ("lex SQL from file/stdin/command line (use -c to parse from command line)"
+  ,\args -> do
+      (f,src) <- getInput args
+      either (error . T.unpack . L.prettyError)
+             (putStrLn . intercalate ",\n" . map show)
+             $ L.lexSQL ansi2011 (T.pack f) Nothing (T.pack src)
+  )
+
+
+formatCommand :: (String,[String] -> IO ())
+formatCommand =
+  ("parse then pretty print SQL from file/stdin/command line (use -c to parse from command line)"
+  ,\args -> do
+      (f,src) <- getInput args
+      either (error . T.unpack . prettyError)
+          (putStrLn . T.unpack . prettyStatements ansi2011)
+          $ parseStatements ansi2011 (T.pack f) Nothing (T.pack src)
+
+  )
diff --git a/simple-sql-parser.cabal b/simple-sql-parser.cabal
--- a/simple-sql-parser.cabal
+++ b/simple-sql-parser.cabal
@@ -1,5 +1,7 @@
+cabal-version:       2.2
+
 name:                simple-sql-parser
-version:             0.6.0
+version:             0.7.0
 synopsis:            A parser for SQL.
 
 description:
@@ -11,15 +13,14 @@
                      <http://jakewheat.github.io/simple-sql-parser/latest>.
 
 homepage:            http://jakewheat.github.io/simple-sql-parser/latest
-license:             BSD3
+license:             BSD-3-Clause
 license-file:        LICENSE
 author:              Jake Wheat
-maintainer:          jakewheatmail@gmail.com
-copyright:           Copyright Jake Wheat 2013, 2014, 2015
+maintainer:          jakewheat@tutanota.com
+copyright:           Copyright 2013 - 2024, Jake Wheat and the simple-sql-parser contributors.
 category:            Database,Language
 build-type:          Simple
-extra-source-files:  README,LICENSE,changelog
-cabal-version:       >=1.10
+extra-doc-files:     README,LICENSE,changelog
 bug-reports:         https://github.com/JakeWheat/simple-sql-parser/issues
 
 source-repository head
@@ -27,50 +28,40 @@
   location:            https://github.com/JakeWheat/simple-sql-parser.git
 
 Flag parserexe
-  Description: Build SimpleSqlParserTool exe
+  Description: Build SimpleSQLParserTool exe
   Default:     False
 
-Flag fixitytest
-  Description: Build fixity test exe
-  Default:     False
+common shared-properties
+  default-language:    Haskell2010
+  build-depends:       base >=4 && <5,
+                       megaparsec >=9.6 && <9.7, 
+                       parser-combinators >= 1.3 && < 1.4,
+                       mtl >=2.1 && <2.4,
+                       prettyprinter >= 1.7 && < 1.8,
+                       text >= 2.0 && < 2.2,
+                       containers >= 0.6 && < 0.8
+
+  ghc-options:         -Wall
+
   
 library
+  import:              shared-properties
   exposed-modules:     Language.SQL.SimpleSQL.Pretty,
                        Language.SQL.SimpleSQL.Parse,
                        Language.SQL.SimpleSQL.Lex,
                        Language.SQL.SimpleSQL.Syntax,
                        Language.SQL.SimpleSQL.Dialect
-  Other-Modules:       Language.SQL.SimpleSQL.Errors,
-                       Language.SQL.SimpleSQL.Combinators
-  build-depends:       base >=4 && <5,
-                       parsec >=3.1 && <3.2,
-                       mtl >=2.1 && <2.3,
-                       pretty >= 1.1 && < 1.2
-  -- hs-source-dirs:
-  default-language:    Haskell2010
-  ghc-options:         -Wall
-  other-extensions:    TupleSections,DeriveDataTypeable
 
 Test-Suite Tests
+  import:              shared-properties
   type:                exitcode-stdio-1.0
-  main-is:             RunTests.lhs
-  hs-source-dirs:      .,tools
-  Build-Depends:       base >=4 && <5,
-                       parsec >=3.1 && <3.2,
-                       mtl >=2.1 && <2.3,
-                       pretty >= 1.1 && < 1.2,
-                       tasty >= 1.1 && < 1.3,
+  main-is:             RunTests.hs
+  hs-source-dirs:      tests
+  Build-Depends:       simple-sql-parser,
+                       tasty >= 1.1 && < 1.6,
                        tasty-hunit >= 0.9 && < 0.11
 
-  Other-Modules:       Language.SQL.SimpleSQL.Pretty,
-                       Language.SQL.SimpleSQL.Parse,
-                       Language.SQL.SimpleSQL.Lex,
-                       Language.SQL.SimpleSQL.Syntax,
-                       Language.SQL.SimpleSQL.Errors,
-                       Language.SQL.SimpleSQL.Combinators,
-                       Language.SQL.SimpleSQL.Dialect
-
-                       Language.SQL.SimpleSQL.ErrorMessages,
+  Other-Modules:       Language.SQL.SimpleSQL.ErrorMessages,
                        Language.SQL.SimpleSQL.FullQueries,
                        Language.SQL.SimpleSQL.GroupBy,
                        Language.SQL.SimpleSQL.MySQL,
@@ -90,52 +81,19 @@
                        Language.SQL.SimpleSQL.Tpch,
                        Language.SQL.SimpleSQL.ScalarExprs,
                        Language.SQL.SimpleSQL.LexerTests,
-                       Language.SQL.SimpleSQL.CustomDialect
-
-  other-extensions:    TupleSections,DeriveDataTypeable
-  default-language:    Haskell2010
-  ghc-options:         -Wall -threaded
+                       Language.SQL.SimpleSQL.CustomDialect,
+                       Language.SQL.SimpleSQL.EmptyStatement,
+                       Language.SQL.SimpleSQL.CreateIndex
+                       
+  ghc-options:         -threaded
 
-executable SimpleSqlParserTool
-  main-is:             SimpleSqlParserTool.lhs
-  hs-source-dirs:      .,tools
-  Build-Depends:       base >=4 && <5,
-                       parsec >=3.1 && <3.2,
-                       mtl >=2.1 && <2.3,
-                       pretty >= 1.1 && < 1.2,
+executable SimpleSQLParserTool
+  import:              shared-properties
+  main-is:             SimpleSQLParserTool.hs
+  hs-source-dirs:      examples
+  Build-Depends:       simple-sql-parser,
                        pretty-show >= 1.6 && < 1.10
-  other-extensions:    TupleSections,DeriveDataTypeable
-  default-language:    Haskell2010
-  ghc-options:         -Wall
-  Other-Modules:       Language.SQL.SimpleSQL.Combinators
-                       Language.SQL.SimpleSQL.Dialect
-                       Language.SQL.SimpleSQL.Errors
-                       Language.SQL.SimpleSQL.Lex
-                       Language.SQL.SimpleSQL.Parse
-                       Language.SQL.SimpleSQL.Pretty
-                       Language.SQL.SimpleSQL.Syntax
-
   if flag(parserexe)
     buildable:         True
   else
     buildable:         False
-
-executable Fixity
-  main-is:             Fixity.lhs
-  hs-source-dirs:      .,tools
-  Build-Depends:       base >=4 && <5,
-                       parsec >=3.1 && <3.2,
-                       mtl >=2.1 && <2.3,
-                       pretty >= 1.1 && < 1.2,
-                       pretty-show >= 1.6 && < 1.10,
-                       tasty >= 1.1 && < 1.3,
-                       tasty-hunit >= 0.9 && < 0.11
-
-  other-extensions:    TupleSections,DeriveDataTypeable
-  default-language:    Haskell2010
-  ghc-options:         -Wall
-  if flag(fixitytest)
-    buildable:         True
-  else
-    buildable:         False
-
diff --git a/tests/Language/SQL/SimpleSQL/CreateIndex.hs b/tests/Language/SQL/SimpleSQL/CreateIndex.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/CreateIndex.hs
@@ -0,0 +1,18 @@
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.CreateIndex where
+
+import Language.SQL.SimpleSQL.Syntax
+import Language.SQL.SimpleSQL.TestTypes
+
+createIndexTests :: TestItem
+createIndexTests = Group "create index tests"
+    [TestStatement ansi2011 "create index a on tbl(c1)"
+      $ CreateIndex False [nm "a"] [nm "tbl"] [nm "c1"]
+    ,TestStatement ansi2011 "create index a.b on sc.tbl (c1, c2)"
+      $ CreateIndex False [nm "a", nm "b"] [nm "sc", nm "tbl"] [nm "c1", nm "c2"]
+    ,TestStatement ansi2011 "create unique index a on tbl(c1)"
+      $ CreateIndex True [nm "a"] [nm "tbl"] [nm "c1"]
+    ]
+  where
+    nm = Name Nothing
diff --git a/tests/Language/SQL/SimpleSQL/CustomDialect.hs b/tests/Language/SQL/SimpleSQL/CustomDialect.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/CustomDialect.hs
@@ -0,0 +1,28 @@
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.CustomDialect (customDialectTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+
+customDialectTests :: TestItem
+customDialectTests = Group "custom dialect tests" (map (uncurry ParseQueryExpr) passTests
+    ++ map (uncurry ParseScalarExprFails) failTests )
+  where
+    failTests = [(ansi2011,"SELECT DATE('2000-01-01')")
+                ,(ansi2011,"SELECT DATE")
+                ,(dateApp,"SELECT DATE")
+                ,(dateIden,"SELECT DATE('2000-01-01')")
+                -- show this never being allowed as an alias
+                ,(ansi2011,"SELECT a date")
+                ,(dateApp,"SELECT a date")
+                ,(dateIden,"SELECT a date")
+                ]
+    passTests = [(ansi2011,"SELECT a b")
+                ,(noDateKeyword,"SELECT DATE('2000-01-01')")
+                ,(noDateKeyword,"SELECT DATE")
+                ,(dateApp,"SELECT DATE('2000-01-01')")
+                ,(dateIden,"SELECT DATE")
+                ]
+    noDateKeyword = ansi2011 {diKeywords = filter (/="date") (diKeywords ansi2011)}
+    dateIden = ansi2011 {diIdentifierKeywords = "date" : diIdentifierKeywords ansi2011}
+    dateApp = ansi2011 {diAppKeywords = "date" : diAppKeywords ansi2011}
diff --git a/tests/Language/SQL/SimpleSQL/EmptyStatement.hs b/tests/Language/SQL/SimpleSQL/EmptyStatement.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/EmptyStatement.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.EmptyStatement where
+
+import Language.SQL.SimpleSQL.Syntax
+import Language.SQL.SimpleSQL.TestTypes
+
+emptyStatementTests :: TestItem
+emptyStatementTests = Group "empty statement"
+  [ TestStatement ansi2011 ";" EmptyStatement
+  , TestStatements ansi2011 ";" [EmptyStatement]
+  , TestStatements ansi2011 ";;" [EmptyStatement, EmptyStatement]
+  , TestStatements ansi2011 ";;;" [EmptyStatement, EmptyStatement, EmptyStatement]
+  , TestStatement ansi2011 "/* comment */ ;" EmptyStatement
+  , TestStatements ansi2011 "" []
+  , TestStatements ansi2011 "/* comment */" []
+  , TestStatements ansi2011 "/* comment */ ;" [EmptyStatement]
+  , TestStatements ansi2011 "/* comment */ ; /* comment */ ;"
+      [EmptyStatement, EmptyStatement]
+  , TestStatements ansi2011 "/* comment */ ; /* comment */ ; /* comment */ ;"
+      [EmptyStatement, EmptyStatement, EmptyStatement]
+  ]
diff --git a/tests/Language/SQL/SimpleSQL/ErrorMessages.hs b/tests/Language/SQL/SimpleSQL/ErrorMessages.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/ErrorMessages.hs
@@ -0,0 +1,156 @@
+
+{-
+Want to work on the error messages. Ultimately, parsec won't give the
+best error message for a parser combinator library in haskell. Should
+check out the alternatives such as polyparse and uu-parsing.
+
+For now the plan is to try to get the best out of parsec. Skip heavy
+work on this until the parser is more left factored?
+
+Ideas:
+
+1. generate large lists of invalid syntax
+2. create table of the sql source and the error message
+3. save these tables and compare from version to version. Want to
+   catch improvements and regressions and investigate. Have to do this
+   manually
+
+= generating bad sql source
+
+take good sql statements or expressions. Convert them into sequences
+of tokens - want to preserve the whitespace and comments perfectly
+here. Then modify these lists by either adding a token, removing a
+token, or modifying a token (including creating bad tokens of raw
+strings which don't represent anything than can be tokenized.
+
+Now can see the error message for all of these bad strings. Probably
+have to generate and prune this list manually in stages since there
+will be too many.
+
+Contexts:
+
+another area to focus on is contexts: for instance, we have a set of
+e.g. 1000 bad scalar expressions with error messages. Now can put
+those bad scalar expressions into various contexts and see that the
+error messages are still good.
+
+plan:
+
+1. create a list of all the value expression, with some variations for
+   each
+2. manually create some error variations for each expression
+3. create a renderer which will create a csv of the expressions and
+   the errors
+   this is to load as a spreadsheet to investigate more
+4. create a renderer for the csv which will create a markdown file for
+   the website. this is to demonstrate the error messages in the
+   documentation
+
+Then create some contexts for all of these: inside another value
+expression, or inside a query expression. Do the same: render and
+review the error messages.
+
+Then, create some query expressions to focus on the non value
+expression parts.
+-}
+
+
+module Language.SQL.SimpleSQL.ErrorMessages where
+
+{-import Language.SQL.SimpleSQL.Parser
+import Data.List
+import Text.Groom
+
+valueExpressions :: [String]
+valueExpressions =
+    ["10.."
+    ,"..10"
+    ,"10e1e2"
+    ,"10e--3"
+    ,"1a"
+    ,"1%"
+
+    ,"'b'ad'"
+    ,"'bad"
+    ,"bad'"
+
+    ,"interval '5' ay"
+    ,"interval '5' day (4.4)"
+    ,"interval '5' day (a)"
+    ,"intervala '5' day"
+    ,"interval 'x' day (3"
+    ,"interval 'x' day 3)"
+
+    ,"1badiden"
+    ,"$"
+    ,"!"
+    ,"*.a"
+
+    ,"??"
+    ,"3?"
+    ,"?a"
+
+    ,"row"
+    ,"row 1,2"
+    ,"row(1,2"
+    ,"row 1,2)"
+    ,"row(1 2)"
+
+    ,"f("
+    ,"f)"
+
+    ,"f(a"
+    ,"f a)"
+    ,"f(a b)"
+
+{-
+TODO:
+case
+operators
+-}
+
+   ,"a + (b + c"
+
+{-
+casts
+subqueries: + whole set of parentheses use
+in list
+'keyword' functions
+aggregates
+window functions
+-}
+
+
+   ]
+
+queryExpressions :: [String]
+queryExpressions =
+    map sl1 valueExpressions
+    ++ map sl2 valueExpressions
+    ++ map sl3 valueExpressions
+    ++
+    ["select a from t inner jin u"]
+  where
+    sl1 x = "select " ++ x ++ " from t"
+    sl2 x = "select " ++ x ++ ", y from t"
+    sl3 x = "select " ++ x ++ " fom t"
+
+valExprs :: [String] -> [(String,String)]
+valExprs = map parseOne
+  where
+    parseOne x = let p = parseValueExpr "" Nothing x
+                 in (x,either peFormattedError (\x -> "ERROR: parsed ok " ++ groom x) p)
+
+
+queryExprs :: [String] -> [(String,String)]
+queryExprs = map parseOne
+  where
+    parseOne x = let p = parseQueryExpr "" Nothing x
+                 in (x,either peFormattedError (\x -> "ERROR: parsed ok " ++ groom x) p)
+
+
+pExprs :: [String] -> [String] -> String
+pExprs x y =
+    let l = valExprs x ++ queryExprs y
+    in intercalate "\n\n\n\n" $ map (\(a,b) -> a ++ "\n" ++ b) l
+-}
diff --git a/tests/Language/SQL/SimpleSQL/FullQueries.hs b/tests/Language/SQL/SimpleSQL/FullQueries.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/FullQueries.hs
@@ -0,0 +1,40 @@
+
+-- 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 ansi2011))
+    [("select count(*) from t"
+     ,toQueryExpr $ makeSelect
+      {msSelectList = [(App [Name Nothing "count"] [Star], Nothing)]
+      ,msFrom = [TRSimple [Name Nothing "t"]]
+      }
+     )
+
+    ,("select a, sum(c+d) as s\n\
+      \  from t,u\n\
+      \  where a > 5\n\
+      \  group by a\n\
+      \  having count(1) > 5\n\
+      \  order by s"
+     ,toQueryExpr $ makeSelect
+      {msSelectList = [(Iden [Name Nothing "a"], Nothing)
+                      ,(App [Name Nothing "sum"]
+                        [BinOp (Iden [Name Nothing "c"])
+                               [Name Nothing "+"] (Iden [Name Nothing "d"])]
+                       ,Just $ Name Nothing "s")]
+      ,msFrom = [TRSimple [Name Nothing "t"], TRSimple [Name Nothing "u"]]
+      ,msWhere = Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5")
+      ,msGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]]
+      ,msHaving = Just $ BinOp (App [Name Nothing "count"] [NumLit "1"])
+                               [Name Nothing ">"] (NumLit "5")
+      ,msOrderBy = [SortSpec (Iden [Name Nothing "s"]) DirDefault NullsOrderDefault]
+      }
+     )
+    ]
diff --git a/tests/Language/SQL/SimpleSQL/GroupBy.hs b/tests/Language/SQL/SimpleSQL/GroupBy.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/GroupBy.hs
@@ -0,0 +1,238 @@
+
+-- 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 ansi2011))
+    [("select a,sum(b) from t group by a"
+     ,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
+                                 ,(App [Name Nothing "sum"] [Iden [Name Nothing "b"]],Nothing)]
+                 ,msFrom = [TRSimple [Name Nothing "t"]]
+                 ,msGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]]
+                 })
+
+    ,("select a,b,sum(c) from t group by a,b"
+     ,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
+                                 ,(Iden [Name Nothing "b"],Nothing)
+                                 ,(App [Name Nothing "sum"] [Iden [Name Nothing "c"]],Nothing)]
+                 ,msFrom = [TRSimple [Name Nothing "t"]]
+                 ,msGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]
+                              ,SimpleGroup $ Iden [Name Nothing "b"]]
+                 })
+    ]
+
+{-
+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 ansi2011))
+    [("select * from t group by ()", ms [GroupingParens []])
+    ,("select * from t group by grouping sets ((), (a))"
+     ,ms [GroupingSets [GroupingParens []
+                       ,GroupingParens [SimpleGroup $ Iden [Name Nothing "a"]]]])
+    ,("select * from t group by cube(a,b)"
+     ,ms [Cube [SimpleGroup $ Iden [Name Nothing "a"], SimpleGroup $ Iden [Name Nothing "b"]]])
+    ,("select * from t group by rollup(a,b)"
+     ,ms [Rollup [SimpleGroup $ Iden [Name Nothing "a"], SimpleGroup $ Iden [Name Nothing "b"]]])
+    ]
+  where
+    ms g = toQueryExpr $ makeSelect {msSelectList = [(Star,Nothing)]
+                      ,msFrom = [TRSimple [Name Nothing "t"]]
+                      ,msGroupBy = g}
+
+randomGroupBy :: TestItem
+randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
+    ["select * from t GROUP BY a"
+    ,"select * from t GROUP BY GROUPING SETS((a))"
+    ,"select * from t GROUP BY a,b,c"
+    ,"select * from t GROUP BY GROUPING SETS((a,b,c))"
+    ,"select * from t GROUP BY ROLLUP(a,b)"
+    ,"select * from t GROUP BY GROUPING SETS((a,b),\n\
+     \(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
+
+    -- decimal as a function not allowed due to the reserved keyword
+    -- handling: todo, review if this is ansi standard function or
+    -- if there are places where reserved keywords can still be used
+    ,"SELECT MONTH(SALES_DATE) AS MONTH,\n\
+     \REGION,\n\
+     \SUM(SALES) AS UNITS_SOLD,\n\
+     \MAX(SALES) AS BEST_SALE,\n\
+     \CAST(ROUND(AVG(DECIMALx(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/tests/Language/SQL/SimpleSQL/LexerTests.hs b/tests/Language/SQL/SimpleSQL/LexerTests.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/LexerTests.hs
@@ -0,0 +1,398 @@
+
+
+-- Test for the lexer
+
+
+{-
+TODO:
+figure out a way to do quickcheck testing:
+1. generate valid tokens and check they parse
+
+2. combine two generated tokens together for the combo testing
+
+this especially will work much better for the postgresql extensible
+operator tests which doing exhaustively takes ages and doesn't bring
+much benefit over testing a few using quickcheck.
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.LexerTests (lexerTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+import Language.SQL.SimpleSQL.Lex
+    (Token(..)
+    ,tokenListWillPrintAndLex
+    )
+
+import qualified Data.Text as T
+import Data.Text (Text)
+    
+--import Debug.Trace
+--import Data.Char (isAlpha)
+-- import Data.List
+
+lexerTests :: TestItem
+lexerTests = Group "lexerTests" $
+    [bootstrapTests
+    ,ansiLexerTests
+    ,postgresLexerTests
+    ,sqlServerLexerTests
+    ,oracleLexerTests
+    ,mySqlLexerTests
+    ,odbcLexerTests]
+
+-- quick sanity tests to see something working
+bootstrapTests :: TestItem
+bootstrapTests = Group "bootstrap tests" [Group "bootstrap tests" $
+    map (uncurry (LexTest ansi2011)) (
+    [("iden", [Identifier Nothing "iden"])
+    ,("'string'", [SqlString "'" "'" "string"])
+
+    ,("  ", [Whitespace "  "])
+    ,("\t  ", [Whitespace "\t  "])
+    ,("  \n  ", [Whitespace "  \n  "])
+
+    ,("--", [LineComment "--"])
+    ,("--\n", [LineComment "--\n"])
+    ,("--stuff", [LineComment "--stuff"])
+    ,("-- stuff", [LineComment "-- stuff"])
+    ,("-- stuff\n", [LineComment "-- stuff\n"])
+    ,("--\nstuff", [LineComment "--\n", Identifier Nothing "stuff"])
+    ,("-- com \nstuff", [LineComment "-- com \n", Identifier Nothing "stuff"])
+
+    ,("/*test1*/", [BlockComment "/*test1*/"])
+    ,("/**/", [BlockComment "/**/"])
+    ,("/***/", [BlockComment "/***/"])
+    ,("/* * */", [BlockComment "/* * */"])
+    ,("/*test*/", [BlockComment "/*test*/"])
+    ,("/*te/*st*/", [BlockComment "/*te/*st*/"])
+    ,("/*te*st*/", [BlockComment "/*te*st*/"])
+    ,("/*lines\nmore lines*/", [BlockComment "/*lines\nmore lines*/"])
+    ,("/*test1*/\n", [BlockComment "/*test1*/", Whitespace "\n"])
+    ,("/*test1*/stuff", [BlockComment "/*test1*/", Identifier Nothing "stuff"])
+
+    ,("1", [SqlNumber "1"])
+    ,("42", [SqlNumber "42"])
+
+    -- have to fix the dialect handling in the tests
+    --,("$1", [PositionalArg 1])
+    --,("$200", [PositionalArg 200])
+
+    ,(":test", [PrefixedVariable ':' "test"])
+
+    ] ++ map (\a -> (a, [Symbol a])) (
+     ["!=", "<>", ">=", "<=", "||"]
+     ++ map T.singleton ("(),-+*/<>=." :: [Char])))]
+
+
+ansiLexerTable :: [(Text,[Token])]
+ansiLexerTable =
+    -- single char symbols
+    map (\s -> (T.singleton s,[Symbol $ T.singleton s])) "+-^*/%~&|?<>[]=,;()"
+    -- multi char symbols
+    ++ map (\s -> (s,[Symbol s])) [">=","<=","!=","<>","||"]
+    ++ (let idens = ["a", "_a", "test", "table", "Stuff", "STUFF"]
+        -- simple identifiers
+        in map (\i -> (i, [Identifier Nothing i])) idens
+           <> map (\i -> ("\"" <> i <> "\"", [Identifier (Just ("\"","\"")) i])) idens
+           -- todo: in order to make lex . pretty id, need to
+           -- preserve the case of the u
+           <> map (\i -> ("u&\"" <> i <> "\"", [Identifier (Just ("u&\"","\"")) i])) idens
+           -- host param
+           <> map (\i -> (T.cons ':' i, [PrefixedVariable ':' i])) idens
+       )
+    -- quoted identifiers with embedded double quotes
+    -- the lexer doesn't unescape the quotes
+    ++ [("\"normal \"\" iden\"", [Identifier (Just ("\"","\"")) "normal \"\" iden"])]
+    -- strings
+    -- the lexer doesn't apply escapes at all
+    ++ [("'string'", [SqlString "'" "'" "string"])
+       ,("'normal '' quote'", [SqlString "'" "'" "normal '' quote"])
+       ,("'normalendquote '''", [SqlString "'" "'" "normalendquote ''"])
+       ,("'\n'", [SqlString "'" "'" "\n"])]
+    -- csstrings
+    ++ map (\c -> (c <> "'test'", [SqlString (c <> "'") "'" "test"]))
+       ["n", "N","b", "B","x", "X", "u&"]
+    -- numbers
+    ++ [("10", [SqlNumber "10"])
+       ,(".1", [SqlNumber ".1"])
+       ,("5e3", [SqlNumber "5e3"])
+       ,("5e+3", [SqlNumber "5e+3"])
+       ,("5e-3", [SqlNumber "5e-3"])
+       ,("10.2", [SqlNumber "10.2"])
+       ,("10.2e7", [SqlNumber "10.2e7"])]
+    -- whitespace
+    ++ concat [[(T.singleton a,[Whitespace $ T.singleton a])
+               ,(T.singleton a <> T.singleton b, [Whitespace (T.singleton a <> T.singleton b)])]
+              | a <- " \n\t", b <- " \n\t"]
+    -- line comment
+    ++ map (\c -> (c, [LineComment c]))
+       ["--", "-- ", "-- this is a comment", "-- line com\n"]
+    -- block comment
+    ++ map (\c -> (c, [BlockComment c]))
+       ["/**/", "/* */","/* this is a comment */"
+       ,"/* this *is/ a comment */"
+       ]
+
+
+ansiLexerTests :: TestItem
+ansiLexerTests = Group "ansiLexerTests" $
+    [Group "ansi lexer token tests" $ [LexTest ansi2011 s t |  (s,t) <- ansiLexerTable]
+    ,Group "ansi generated combination lexer tests" $
+    [ LexTest ansi2011 (s <> s1) (t <> t1)
+    | (s,t) <- ansiLexerTable
+    , (s1,t1) <- ansiLexerTable
+    , tokenListWillPrintAndLex ansi2011 $ t <> t1
+
+    ]
+    ,Group "ansiadhoclexertests" $
+       map (uncurry $ LexTest ansi2011)
+       [("", [])
+       ,("-- line com\nstuff", [LineComment "-- line com\n",Identifier Nothing "stuff"])
+       ] ++
+       [-- want to make sure this gives a parse error
+        LexFails ansi2011 "*/"
+        -- combinations of pipes: make sure they fail because they could be
+        -- ambiguous and it is really unclear when they are or not, and
+        -- what the result is even when they are not ambiguous
+       ,LexFails ansi2011 "|||"
+       ,LexFails ansi2011 "||||"
+       ,LexFails ansi2011 "|||||"
+       -- another user experience thing: make sure extra trailing
+       -- number chars are rejected rather than attempting to parse
+       -- if the user means to write something that is rejected by this code,
+       -- then they can use whitespace to make it clear and then it will parse
+       ,LexFails ansi2011 "12e3e4"
+       ,LexFails ansi2011 "12e3e4"
+       ,LexFails ansi2011 "12e3e4"
+       ,LexFails ansi2011 "12e3.4"
+       ,LexFails ansi2011 "12.4.5"
+       ,LexFails ansi2011 "12.4e5.6"
+       ,LexFails ansi2011 "12.4e5e7"]
+     ]
+
+{-
+todo: lexing tests
+do quickcheck testing:
+can try to generate valid tokens then check they parse
+
+same as above: can also try to pair tokens, create an accurate
+  function to say which ones can appear adjacent, and test
+
+I think this plus the explicit lists of tokens like above which do
+basic sanity + explicit edge casts will provide a high level of
+assurance.
+-}
+
+
+
+postgresLexerTable :: [(Text,[Token])]
+postgresLexerTable =
+    -- single char symbols
+    map (\s -> (T.singleton s,[Symbol $ T.singleton s])) "+-^*/%~&|?<>[]=,;():"
+    -- multi char symbols
+    ++ map (\s -> (s,[Symbol s])) [">=","<=","!=","<>","||", "::","..",":="]
+    -- generic symbols
+
+    ++ (let idens = ["a", "_a", "test", "table", "Stuff", "STUFF"]
+        -- simple identifiers
+        in map (\i -> (i, [Identifier Nothing i])) idens
+           ++ map (\i -> ("\"" <> i <> "\"", [Identifier (Just ("\"","\"")) i])) idens
+           -- todo: in order to make lex . pretty id, need to
+           -- preserve the case of the u
+           ++ map (\i -> ("u&\"" <> i <> "\"", [Identifier (Just ("u&\"","\"")) i])) idens
+           -- host param
+           ++ map (\i -> (T.cons ':' i, [PrefixedVariable ':' i])) idens
+       )
+    -- positional var
+    ++ [("$1", [PositionalArg 1])]
+    -- quoted identifiers with embedded double quotes
+    ++ [("\"normal \"\" iden\"", [Identifier (Just ("\"","\"")) "normal \"\" iden"])]
+    -- strings
+    ++ [("'string'", [SqlString "'" "'" "string"])
+       ,("'normal '' quote'", [SqlString "'" "'" "normal '' quote"])
+       ,("'normalendquote '''", [SqlString "'" "'" "normalendquote ''"])
+       ,("'\n'", [SqlString "'" "'" "\n"])
+       ,("E'\n'", [SqlString "E'" "'" "\n"])
+       ,("e'this '' quote'", [SqlString "e'" "'" "this '' quote"])
+       ,("e'this \\' quote'", [SqlString "e'" "'" "this \\' quote"])
+       ,("'not this \\' quote", [SqlString "'" "'" "not this \\"
+                                ,Whitespace " "
+                                ,Identifier Nothing "quote"])
+       ,("$$ string 1 $$", [SqlString "$$" "$$" " string 1 "])
+       ,("$$ string $ 2 $$", [SqlString "$$" "$$" " string $ 2 "])
+       ,("$a$ $$string 3$$ $a$", [SqlString "$a$" "$a$" " $$string 3$$ "])
+       ]
+    -- csstrings
+    ++ map (\c -> (c <> "'test'", [SqlString (c <> "'") "'" "test"]))
+       ["n", "N","b", "B","x", "X", "u&", "e", "E"]
+    -- numbers
+    ++ [("10", [SqlNumber "10"])
+       ,(".1", [SqlNumber ".1"])
+       ,("5e3", [SqlNumber "5e3"])
+       ,("5e+3", [SqlNumber "5e+3"])
+       ,("5e-3", [SqlNumber "5e-3"])
+       ,("10.2", [SqlNumber "10.2"])
+       ,("10.2e7", [SqlNumber "10.2e7"])]
+    -- whitespace
+    ++ concat [[(T.singleton a,[Whitespace $ T.singleton a])
+               ,(T.singleton a <> T.singleton b, [Whitespace $ T.singleton a <> T.singleton b])]
+              | a <- " \n\t", b <- " \n\t"]
+    -- line comment
+    ++ map (\c -> (c, [LineComment c]))
+       ["--", "-- ", "-- this is a comment", "-- line com\n"]
+    -- block comment
+    ++ map (\c -> (c, [BlockComment c]))
+       ["/**/", "/* */","/* this is a comment */"
+       ,"/* this *is/ a comment */"
+       ]
+
+{-
+An operator name is a sequence of up to NAMEDATALEN-1 (63 by default) characters from the following list:
+
++ - * / < > = ~ ! @ # % ^ & | ` ?
+
+There are a few restrictions on operator names, however:
+-- and /* cannot appear anywhere in an operator name, since they will be taken as the start of a comment.
+
+A multiple-character operator name cannot end in + or -, unless the name also contains at least one of these characters:
+
+~ ! @ # % ^ & | ` ?
+
+todo: 'negative' tests
+symbol then --
+symbol then /*
+operators without one of the exception chars
+  followed by + or - without whitespace
+
+also: do the testing for the ansi compatibility special cases
+-}
+
+postgresShortOperatorTable :: [(Text,[Token])]
+postgresShortOperatorTable =
+    [ (x, [Symbol x]) | x <- someValidPostgresOperators 2]
+
+
+postgresExtraOperatorTable :: [(Text,[Token])]
+postgresExtraOperatorTable =
+    [ (x, [Symbol x]) | x <- someValidPostgresOperators 4]
+
+
+someValidPostgresOperators :: Int -> [Text]
+someValidPostgresOperators l =
+       [ x
+       | n <- [1..l]
+       , x <- combos "+-*/<>=~!@#%^&|`?" n
+       , not ("--" `T.isInfixOf` x || "/*" `T.isInfixOf` x || "*/" `T.isInfixOf` x)
+       , not (T.last x `T.elem` "+-")
+         || or (map (`T.elem` x) "~!@#%^&|`?")
+       ]
+
+{-
+These are postgres operators, which if followed immediately by a + or
+-, will lex as separate operators rather than one operator including
+the + or -.
+-}
+
+somePostgresOpsWhichWontAddTrailingPlusMinus :: Int -> [Text]
+somePostgresOpsWhichWontAddTrailingPlusMinus l =
+       [ x
+       | n <- [1..l]
+       , x <- combos "+-*/<>=" n
+       , not ("--" `T.isInfixOf` x || "/*" `T.isInfixOf` x || "*/" `T.isInfixOf` x)
+       , not (T.last x `T.elem` "+-")
+       ]
+
+
+postgresLexerTests :: TestItem
+postgresLexerTests = Group "postgresLexerTests" $
+    [Group "postgres lexer token tests" $
+     [LexTest postgres s t | (s,t) <- postgresLexerTable]
+    ,Group "postgres generated lexer token tests" $
+     [LexTest postgres s t | (s,t) <- postgresShortOperatorTable ++ postgresExtraOperatorTable]
+    ,Group "postgres generated combination lexer tests" $
+    [ LexTest postgres (s <> s1) (t <> t1)
+    | (s,t) <- postgresLexerTable ++ postgresShortOperatorTable
+    , (s1,t1) <- postgresLexerTable ++ postgresShortOperatorTable
+    , tokenListWillPrintAndLex postgres $ t ++ t1
+
+    ]
+    ,Group "generated postgres edgecase lexertests" $
+     [LexTest postgres s t
+     | (s,t) <- edgeCaseCommentOps
+                ++ edgeCasePlusMinusOps
+                ++ edgeCasePlusMinusComments]
+
+    ,Group "adhoc postgres lexertests" $
+      -- need more tests for */ to make sure it is caught if it is in the middle of a
+      -- sequence of symbol letters
+        [LexFails postgres "*/"
+        ,LexFails postgres ":::"
+        ,LexFails postgres "::::"
+        ,LexFails postgres ":::::"
+        ,LexFails postgres "@*/"
+        ,LexFails postgres "-*/"
+        ,LexFails postgres "12e3e4"
+        ,LexFails postgres "12e3e4"
+        ,LexFails postgres "12e3e4"
+        ,LexFails postgres "12e3.4"
+        ,LexFails postgres "12.4.5"
+        ,LexFails postgres "12.4e5.6"
+        ,LexFails postgres "12.4e5e7"
+         -- special case allow this to lex to 1 .. 2
+         -- this is for 'for loops' in plpgsql
+        ,LexTest postgres "1..2" [SqlNumber "1", Symbol "..", SqlNumber "2"]]
+    ]
+ where
+   edgeCaseCommentOps =
+     [ (x <> "/*<test*/", [Symbol x, BlockComment "/*<test*/"])
+     | x <- eccops
+     , not (T.last x == '*')
+     ] ++
+     [ (x <> "--<test", [Symbol x, LineComment "--<test"])
+     | x <- eccops
+     , not (T.last x == '-')
+     ]
+   eccops = someValidPostgresOperators 2
+   edgeCasePlusMinusOps = concat
+     [ [ (x <> "+", [Symbol x, Symbol "+"])
+       , (x <> "-", [Symbol x, Symbol "-"]) ]
+     | x <- somePostgresOpsWhichWontAddTrailingPlusMinus 2
+     ]
+   edgeCasePlusMinusComments =
+     [("---", [LineComment "---"])
+     ,("+--", [Symbol "+", LineComment "--"])
+     ,("-/**/", [Symbol "-", BlockComment "/**/"])
+     ,("+/**/", [Symbol "+", BlockComment "/**/"])
+     ]
+
+sqlServerLexerTests :: TestItem
+sqlServerLexerTests = Group "sqlServerLexTests" $
+    [ LexTest sqlserver s t | (s,t) <-
+    [("@variable", [(PrefixedVariable '@' "variable")])
+    ,("#variable", [(PrefixedVariable '#' "variable")])
+    ,("[quoted identifier]", [(Identifier (Just ("[", "]")) "quoted identifier")])
+    ]]
+
+oracleLexerTests :: TestItem
+oracleLexerTests = Group "oracleLexTests" $
+    [] -- nothing oracle specific atm
+
+mySqlLexerTests :: TestItem
+mySqlLexerTests = Group "mySqlLexerTests" $
+    [ LexTest mysql s t | (s,t) <-
+    [("`quoted identifier`", [(Identifier (Just ("`", "`")) "quoted identifier")])
+    ]
+    ]
+
+odbcLexerTests :: TestItem
+odbcLexerTests = Group "odbcLexTests" $
+    [ LexTest sqlserver {diOdbc = True} s t | (s,t) <-
+    [("{}", [Symbol "{", Symbol "}"])
+    ]]
+    ++ [LexFails sqlserver {diOdbc = False} "{"
+       ,LexFails sqlserver {diOdbc = False} "}"]
+
+combos :: [Char] -> Int -> [Text]
+combos _ 0 = [T.empty]
+combos l n = [ T.cons x tl | x <- l, tl <- combos l (n - 1) ]
diff --git a/tests/Language/SQL/SimpleSQL/MySQL.hs b/tests/Language/SQL/SimpleSQL/MySQL.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/MySQL.hs
@@ -0,0 +1,43 @@
+
+-- Tests for mysql dialect parsing
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.MySQL (mySQLTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+import Language.SQL.SimpleSQL.Syntax
+
+mySQLTests :: TestItem
+mySQLTests = Group "mysql dialect"
+    [backtickQuotes
+    ,limit]
+
+{-
+backtick quotes
+
+limit syntax
+
+[LIMIT {[offset,] row_count | row_count OFFSET offset}]
+-}
+
+backtickQuotes :: TestItem
+backtickQuotes = Group "backtickQuotes" (map (uncurry (TestScalarExpr mysql))
+    [("`test`", Iden [Name (Just ("`","`")) "test"])
+    ]
+    ++ [ParseScalarExprFails ansi2011 "`test`"]
+    )
+
+limit :: TestItem
+limit = Group "queries" ( map (uncurry (TestQueryExpr mysql))
+    [("select * from t limit 5"
+     ,toQueryExpr $ sel {msFetchFirst = Just (NumLit "5")}
+     )
+    ]
+    ++ [ParseQueryExprFails mysql "select a from t fetch next 10 rows only;"
+       ,ParseQueryExprFails ansi2011 "select * from t limit 5"]
+    )
+  where
+    sel = makeSelect
+          {msSelectList = [(Star, Nothing)]
+          ,msFrom = [TRSimple [Name Nothing "t"]]
+          }
diff --git a/tests/Language/SQL/SimpleSQL/Odbc.hs b/tests/Language/SQL/SimpleSQL/Odbc.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/Odbc.hs
@@ -0,0 +1,53 @@
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.Odbc (odbcTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+import Language.SQL.SimpleSQL.Syntax
+
+odbcTests :: TestItem
+odbcTests = Group "odbc" [
+       Group "datetime" [
+           e "{d '2000-01-01'}" (OdbcLiteral OLDate "2000-01-01")
+          ,e "{t '12:00:01.1'}" (OdbcLiteral OLTime "12:00:01.1")
+          ,e "{ts '2000-01-01 12:00:01.1'}"
+               (OdbcLiteral OLTimestamp "2000-01-01 12:00:01.1")
+       ]
+       ,Group "functions" [
+             e "{fn CHARACTER_LENGTH(string_exp)}"
+             $ OdbcFunc (ap "CHARACTER_LENGTH" [iden "string_exp"])
+            ,e "{fn EXTRACT(day from t)}"
+            $ OdbcFunc (SpecialOpK [Name Nothing "extract"] (Just $ Iden [Name Nothing "day"]) [("from", Iden [Name Nothing "t"])])
+            ,e "{fn now()}"
+             $ OdbcFunc (ap "now" [])
+            ,e "{fn CONVERT('2000-01-01', SQL_DATE)}"
+             $ OdbcFunc (ap "CONVERT"
+              [StringLit "'" "'" "2000-01-01"
+              ,iden "SQL_DATE"])
+            ,e "{fn CONVERT({fn CURDATE()}, SQL_DATE)}"
+             $ OdbcFunc (ap "CONVERT"
+              [OdbcFunc (ap "CURDATE" [])
+              ,iden "SQL_DATE"])
+            ]
+       ,Group "outer join" [
+             TestQueryExpr ansi2011 {diOdbc=True}
+             "select * from {oj t1 left outer join t2 on expr}"
+             $ toQueryExpr $ makeSelect
+                   {msSelectList = [(Star,Nothing)]
+                   ,msFrom = [TROdbc $ TRJoin (TRSimple [Name Nothing "t1"]) False JLeft (TRSimple [Name Nothing "t2"])
+                                         (Just $ JoinOn $ Iden [Name Nothing "expr"])]}]
+       ,Group "check parsing bugs" [
+             TestQueryExpr ansi2011 {diOdbc=True}
+             "select {fn CONVERT(cint,SQL_BIGINT)} from t;"
+             $ toQueryExpr $ makeSelect
+                   {msSelectList = [(OdbcFunc (ap "CONVERT"
+                                                      [iden "cint"
+                                                      ,iden "SQL_BIGINT"]), Nothing)]
+                   ,msFrom = [TRSimple [Name Nothing "t"]]}]
+       ]
+  where
+    e = TestScalarExpr ansi2011 {diOdbc = True}
+    --tsql = ParseProcSql defaultParseFlags {pfDialect=sqlServerDialect}
+    ap n = App [Name Nothing n]
+    iden n = Iden [Name Nothing n]
+
diff --git a/tests/Language/SQL/SimpleSQL/Oracle.hs b/tests/Language/SQL/SimpleSQL/Oracle.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/Oracle.hs
@@ -0,0 +1,30 @@
+
+-- Tests for oracle dialect parsing
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.Oracle (oracleTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+import Language.SQL.SimpleSQL.Syntax
+
+oracleTests :: TestItem
+oracleTests = Group "oracle dialect"
+    [oracleLobUnits]
+
+
+oracleLobUnits :: TestItem
+oracleLobUnits = Group "oracleLobUnits" (map (uncurry (TestScalarExpr oracle))
+    [("cast (a as varchar2(3 char))"
+     ,Cast (Iden [Name Nothing "a"]) (
+         PrecLengthTypeName [Name Nothing "varchar2"] 3 Nothing (Just PrecCharacters)))
+     ,("cast (a as varchar2(3 byte))"
+     ,Cast (Iden [Name Nothing "a"]) (
+         PrecLengthTypeName [Name Nothing "varchar2"] 3 Nothing (Just PrecOctets)))
+     ]
+    ++ [TestStatement oracle
+      "create table t (a varchar2(55 BYTE));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a")
+        (PrecLengthTypeName [Name Nothing "varchar2"] 55 Nothing (Just PrecOctets))
+        Nothing []]]
+    )
diff --git a/tests/Language/SQL/SimpleSQL/Postgres.hs b/tests/Language/SQL/SimpleSQL/Postgres.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/Postgres.hs
@@ -0,0 +1,279 @@
+
+{-
+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.
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.Postgres (postgresTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+
+postgresTests :: TestItem
+postgresTests = Group "postgresTests" $ map (ParseQueryExpr ansi2011)
+
+{-
+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');"
+
+    -- table is a reservered keyword?
+    --,"SELECT ROW(table.*) IS NULL FROM table;"
+    ,"SELECT ROW(tablex.*) IS NULL FROM tablex;"
+
+    ,"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;"
+    -- s.date changed to s.datex because of reserved keyword
+    -- handling, not sure if this is correct or not for ansi sql
+    ,"SELECT product_id, p.name, (sum(s.units) * p.price) AS sales\n\
+     \    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.datex > CURRENT_DATE - INTERVAL '4 weeks'\n\
+     \    GROUP BY product_id, p.name, p.price, p.cost\n\
+     \    HAVING sum(p.price * s.units) > 5000;"
+
+    ,"SELECT a, b, c FROM t"
+
+    ,"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;" -- limit is not standard
+
+-- 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;"
+
+    -- simple-sql-parser doesn't support where without from
+    -- this can be added for the postgres dialect when it is written
+    --,"SELECT distributors.* WHERE distributors.name = 'Westward';"
+
+    ]
diff --git a/tests/Language/SQL/SimpleSQL/QueryExprComponents.hs b/tests/Language/SQL/SimpleSQL/QueryExprComponents.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/QueryExprComponents.hs
@@ -0,0 +1,209 @@
+
+{-
+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 ansi2011))
+    [("select a from t" ,ms SQDefault)
+    ,("select all a from t" ,ms All)
+    ,("select distinct a from t", ms Distinct)
+    ]
+ where
+   ms d = toQueryExpr $ makeSelect
+          {msSetQuantifier = d
+          ,msSelectList = [(Iden [Name Nothing "a"],Nothing)]
+          ,msFrom = [TRSimple [Name Nothing "t"]]}
+
+selectLists :: TestItem
+selectLists = Group "selectLists" $ map (uncurry (TestQueryExpr ansi2011))
+    [("select 1",
+      toQueryExpr $ makeSelect {msSelectList = [(NumLit "1",Nothing)]})
+
+    ,("select a"
+     ,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)]})
+
+    ,("select a,b"
+     ,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
+                                 ,(Iden [Name Nothing "b"],Nothing)]})
+
+    ,("select 1+2,3+4"
+     ,toQueryExpr $ makeSelect {msSelectList =
+                     [(BinOp (NumLit "1") [Name Nothing "+"] (NumLit "2"),Nothing)
+                     ,(BinOp (NumLit "3") [Name Nothing "+"] (NumLit "4"),Nothing)]})
+
+    ,("select a as a, /*comment*/ b as b"
+     ,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "a")
+                                 ,(Iden [Name Nothing "b"], Just $ Name Nothing "b")]})
+
+    ,("select a a, b b"
+     ,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "a")
+                                 ,(Iden [Name Nothing "b"], Just $ Name Nothing "b")]})
+
+    ,("select a + b * c"
+     ,toQueryExpr $ makeSelect {msSelectList =
+      [(BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"]
+        (BinOp (Iden [Name Nothing "b"]) [Name Nothing "*"] (Iden [Name Nothing "c"]))
+       ,Nothing)]})
+
+    ]
+
+whereClause :: TestItem
+whereClause = Group "whereClause" $ map (uncurry (TestQueryExpr ansi2011))
+    [("select a from t where a = 5"
+     ,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)]
+                 ,msFrom = [TRSimple [Name Nothing "t"]]
+                 ,msWhere = Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "5")})
+    ]
+
+having :: TestItem
+having = Group "having" $ map (uncurry (TestQueryExpr ansi2011))
+    [("select a,sum(b) from t group by a having sum(b) > 5"
+     ,toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)
+                                 ,(App [Name Nothing "sum"] [Iden [Name Nothing "b"]],Nothing)]
+                 ,msFrom = [TRSimple [Name Nothing "t"]]
+                 ,msGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]]
+                 ,msHaving = Just $ BinOp (App [Name Nothing "sum"] [Iden [Name Nothing "b"]])
+                                          [Name Nothing ">"] (NumLit "5")
+                 })
+    ]
+
+orderBy :: TestItem
+orderBy = Group "orderBy" $ map (uncurry (TestQueryExpr ansi2011))
+    [("select a from t order by a"
+     ,ms [SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault])
+
+    ,("select a from t order by a, b"
+     ,ms [SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault
+         ,SortSpec (Iden [Name Nothing "b"]) DirDefault NullsOrderDefault])
+
+    ,("select a from t order by a asc"
+     ,ms [SortSpec (Iden [Name Nothing "a"]) Asc NullsOrderDefault])
+
+    ,("select a from t order by a desc, b desc"
+     ,ms [SortSpec (Iden [Name Nothing "a"]) Desc NullsOrderDefault
+         ,SortSpec (Iden [Name Nothing "b"]) Desc NullsOrderDefault])
+
+    ,("select a from t order by a desc nulls first, b desc nulls last"
+     ,ms [SortSpec (Iden [Name Nothing "a"]) Desc NullsFirst
+         ,SortSpec (Iden [Name Nothing "b"]) Desc NullsLast])
+
+    ]
+  where
+    ms o = toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)]
+                      ,msFrom = [TRSimple [Name Nothing "t"]]
+                      ,msOrderBy = o}
+
+offsetFetch :: TestItem
+offsetFetch = Group "offsetFetch" $ map (uncurry (TestQueryExpr ansi2011))
+    [-- 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: disabled, will add back when postgres
+     -- dialect is added
+    --,("select a from t limit 10 offset 5"
+    -- ,ms (Just $ NumLit "5") (Just $ NumLit "10"))
+    ]
+  where
+    ms o l = toQueryExpr $ makeSelect
+             {msSelectList = [(Iden [Name Nothing "a"],Nothing)]
+             ,msFrom = [TRSimple [Name Nothing "t"]]
+             ,msOffset = o
+             ,msFetchFirst = l}
+
+combos :: TestItem
+combos = Group "combos" $ map (uncurry (TestQueryExpr ansi2011))
+    [("select a from t union select b from u"
+     ,QueryExprSetOp mst Union SQDefault Respectively msu)
+
+    ,("select a from t intersect select b from u"
+     ,QueryExprSetOp mst Intersect SQDefault Respectively msu)
+
+    ,("select a from t except all select b from u"
+     ,QueryExprSetOp mst Except All Respectively msu)
+
+    ,("select a from t union distinct corresponding \
+      \select b from u"
+     ,QueryExprSetOp mst Union Distinct Corresponding msu)
+
+    ,("select a from t union select a from t union select a from t"
+     ,QueryExprSetOp (QueryExprSetOp mst Union SQDefault Respectively mst)
+       Union SQDefault Respectively mst)
+    ]
+  where
+    mst = toQueryExpr $ makeSelect
+          {msSelectList = [(Iden [Name Nothing "a"],Nothing)]
+          ,msFrom = [TRSimple [Name Nothing "t"]]}
+    msu = toQueryExpr $ makeSelect
+          {msSelectList = [(Iden [Name Nothing "b"],Nothing)]
+          ,msFrom = [TRSimple [Name Nothing "u"]]}
+
+
+withQueries :: TestItem
+withQueries = Group "with queries" $ map (uncurry (TestQueryExpr ansi2011))
+    [("with u as (select a from t) select a from u"
+     ,With False [(Alias (Name Nothing "u") Nothing, ms1)] ms2)
+
+    ,("with u(b) as (select a from t) select a from u"
+     ,With False [(Alias (Name Nothing "u") (Just [Name Nothing "b"]), ms1)] ms2)
+
+    ,("with x as (select a from t),\n\
+      \     u as (select a from x)\n\
+      \select a from u"
+     ,With False [(Alias (Name Nothing "x") Nothing, ms1), (Alias (Name Nothing "u") Nothing,ms3)] ms2)
+
+    ,("with recursive u as (select a from t) select a from u"
+     ,With True [(Alias (Name Nothing "u") Nothing, ms1)] ms2)
+    ]
+ where
+   ms c t = toQueryExpr $ makeSelect
+            {msSelectList = [(Iden [Name Nothing c],Nothing)]
+            ,msFrom = [TRSimple [Name Nothing t]]}
+   ms1 = ms "a" "t"
+   ms2 = ms "a" "u"
+   ms3 = ms "a" "x"
+
+values :: TestItem
+values = Group "values" $ map (uncurry (TestQueryExpr ansi2011))
+    [("values (1,2),(3,4)"
+      ,Values [[NumLit "1", NumLit "2"]
+              ,[NumLit "3", NumLit "4"]])
+    ]
+
+tables :: TestItem
+tables = Group "tables" $ map (uncurry (TestQueryExpr ansi2011))
+    [("table tbl", Table [Name Nothing "tbl"])
+    ]
diff --git a/tests/Language/SQL/SimpleSQL/QueryExprs.hs b/tests/Language/SQL/SimpleSQL/QueryExprs.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/QueryExprs.hs
@@ -0,0 +1,27 @@
+
+{-
+These are the tests for the queryExprs parsing which parses multiple
+query expressions from one string.
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+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 (TestStatements ansi2011))
+    [("select 1",[ms])
+    ,("select 1;",[ms])
+    ,("select 1;select 1",[ms,ms])
+    ,(" select 1;select 1; ",[ms,ms])
+    ,("SELECT CURRENT_TIMESTAMP;"
+     ,[SelectStatement $ toQueryExpr $ makeSelect
+      {msSelectList = [(Iden [Name Nothing "CURRENT_TIMESTAMP"],Nothing)]}])
+    ,("SELECT \"CURRENT_TIMESTAMP\";"
+     ,[SelectStatement $ toQueryExpr $ makeSelect
+      {msSelectList = [(Iden [Name (Just ("\"","\"")) "CURRENT_TIMESTAMP"],Nothing)]}])
+    ]
+  where
+    ms = SelectStatement $ toQueryExpr $ makeSelect {msSelectList = [(NumLit "1",Nothing)]}
diff --git a/tests/Language/SQL/SimpleSQL/SQL2011AccessControl.hs b/tests/Language/SQL/SimpleSQL/SQL2011AccessControl.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/SQL2011AccessControl.hs
@@ -0,0 +1,330 @@
+
+{-
+Section 12 in Foundation
+
+grant, etc
+-}
+
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.SQL2011AccessControl (sql2011AccessControlTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+import Language.SQL.SimpleSQL.Syntax
+
+sql2011AccessControlTests :: TestItem
+sql2011AccessControlTests = Group "sql 2011 access control tests" [
+
+{-
+12 Access control
+
+12.1 <grant statement>
+
+<grant statement> ::=
+    <grant privilege statement>
+  | <grant role statement>
+
+12.2 <grant privilege statement>
+
+<grant privilege statement> ::=
+  GRANT <privileges> TO <grantee> [ { <comma> <grantee> }... ]
+      [ WITH HIERARCHY OPTION ]
+      [ WITH GRANT OPTION ]
+      [ GRANTED BY <grantor> ]
+
+12.3 <privileges>
+<privileges> ::=
+  <object privileges> ON <object name>
+
+<object name> ::=
+      [ TABLE ] <table name>
+  |   DOMAIN <domain name>
+  |   COLLATION <collation name>
+  |   CHARACTER SET <character set name>
+  |   TRANSLATION <transliteration name>
+  |   TYPE <schema-resolved user-defined type name>
+  |   SEQUENCE <sequence generator name>
+  |   <specific routine designator>
+
+<object privileges> ::=
+    ALL PRIVILEGES
+  | <action> [ { <comma> <action> }... ]
+
+<action> ::=
+    SELECT
+  | SELECT <left paren> <privilege column list> <right paren>
+  | SELECT <left paren> <privilege method list> <right paren>
+  | DELETE
+  | INSERT [ <left paren> <privilege column list> <right paren> ]
+  | UPDATE [ <left paren> <privilege column list> <right paren> ]
+  | REFERENCES [ <left paren> <privilege column list> <right paren> ]
+  | USAGE
+  | TRIGGER
+  | UNDER
+  | EXECUTE
+
+<privilege method list> ::=
+  <specific routine designator> [ { <comma> <specific routine designator> }... ]
+
+<privilege column list> ::=
+  <column name list>
+
+<grantee> ::=
+    PUBLIC
+  | <authorization identifier>
+
+<grantor> ::=
+    CURRENT_USER
+  | CURRENT_ROLE
+-}
+
+     (TestStatement ansi2011
+      "grant all privileges on tbl1 to role1"
+     $ GrantPrivilege [PrivAll]
+       (PrivTable [Name Nothing "tbl1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+
+    ,(TestStatement ansi2011
+      "grant all privileges on tbl1 to role1,role2"
+     $ GrantPrivilege [PrivAll]
+       (PrivTable [Name Nothing "tbl1"])
+       [Name Nothing "role1",Name Nothing "role2"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant all privileges on tbl1 to role1 with grant option"
+     $ GrantPrivilege [PrivAll]
+       (PrivTable [Name Nothing "tbl1"])
+       [Name Nothing "role1"] WithGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant all privileges on table tbl1 to role1"
+     $ GrantPrivilege [PrivAll]
+       (PrivTable [Name Nothing "tbl1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant all privileges on domain mydom to role1"
+     $ GrantPrivilege [PrivAll]
+       (PrivDomain [Name Nothing "mydom"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant all privileges on type t1 to role1"
+     $ GrantPrivilege [PrivAll]
+       (PrivType [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant all privileges on sequence s1 to role1"
+     $ GrantPrivilege [PrivAll]
+       (PrivSequence [Name Nothing "s1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+
+    ,(TestStatement ansi2011
+      "grant select on table t1 to role1"
+     $ GrantPrivilege [PrivSelect []]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant select(a,b) on table t1 to role1"
+     $ GrantPrivilege [PrivSelect [Name Nothing "a", Name Nothing "b"]]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant delete on table t1 to role1"
+     $ GrantPrivilege [PrivDelete]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant insert on table t1 to role1"
+     $ GrantPrivilege [PrivInsert []]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant insert(a,b) on table t1 to role1"
+     $ GrantPrivilege [PrivInsert [Name Nothing "a", Name Nothing "b"]]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant update on table t1 to role1"
+     $ GrantPrivilege [PrivUpdate []]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant update(a,b) on table t1 to role1"
+     $ GrantPrivilege [PrivUpdate [Name Nothing "a", Name Nothing "b"]]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant references on table t1 to role1"
+     $ GrantPrivilege [PrivReferences []]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant references(a,b) on table t1 to role1"
+     $ GrantPrivilege [PrivReferences [Name Nothing "a", Name Nothing "b"]]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant usage on table t1 to role1"
+     $ GrantPrivilege [PrivUsage]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant trigger on table t1 to role1"
+     $ GrantPrivilege [PrivTrigger]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+
+    ,(TestStatement ansi2011
+      "grant execute on specific function f to role1"
+     $ GrantPrivilege [PrivExecute]
+       (PrivFunction [Name Nothing "f"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+    ,(TestStatement ansi2011
+      "grant select,delete on table t1 to role1"
+     $ GrantPrivilege [PrivSelect [], PrivDelete]
+       (PrivTable [Name Nothing "t1"])
+       [Name Nothing "role1"] WithoutGrantOption)
+
+{-
+skipping for now:
+
+what is 'under' action?
+
+collation, character set, translation, member thing, methods
+
+for review
+
+some pretty big things missing in the standard:
+
+schema, database
+
+functions, etc., by argument types since they can be overloaded
+
+
+
+12.4 <role definition>
+
+<role definition> ::=
+  CREATE ROLE <role name> [ WITH ADMIN <grantor> ]
+-}
+
+    ,(TestStatement ansi2011
+      "create role rolee"
+     $ CreateRole (Name Nothing "rolee"))
+
+
+{-
+12.5 <grant role statement>
+
+<grant role statement> ::=
+  GRANT <role granted> [ { <comma> <role granted> }... ]
+      TO <grantee> [ { <comma> <grantee> }... ]
+      [ WITH ADMIN OPTION ]
+      [ GRANTED BY <grantor> ]
+
+<role granted> ::=
+  <role name>
+-}
+
+    ,(TestStatement ansi2011
+      "grant role1 to public"
+     $ GrantRole [Name Nothing "role1"] [Name Nothing "public"] WithoutAdminOption)
+
+    ,(TestStatement ansi2011
+      "grant role1,role2 to role3,role4"
+     $ GrantRole [Name Nothing "role1",Name Nothing "role2"]
+                 [Name Nothing "role3", Name Nothing "role4"] WithoutAdminOption)
+
+    ,(TestStatement ansi2011
+      "grant role1 to role3 with admin option"
+     $ GrantRole [Name Nothing "role1"] [Name Nothing "role3"] WithAdminOption)
+
+
+{-
+12.6 <drop role statement>
+
+<drop role statement> ::=
+  DROP ROLE <role name>
+-}
+
+    ,(TestStatement ansi2011
+      "drop role rolee"
+     $ DropRole (Name Nothing "rolee"))
+
+
+{-
+12.7 <revoke statement>
+
+<revoke statement> ::=
+    <revoke privilege statement>
+  | <revoke role statement>
+
+<revoke privilege statement> ::=
+  REVOKE [ <revoke option extension> ] <privileges>
+      FROM <grantee> [ { <comma> <grantee> }... ]
+      [ GRANTED BY <grantor> ]
+      <drop behavior>
+
+<revoke option extension> ::=
+    GRANT OPTION FOR
+  | HIERARCHY OPTION FOR
+-}
+
+
+    ,(TestStatement ansi2011
+      "revoke select on t1 from role1"
+     $ RevokePrivilege NoGrantOptionFor [PrivSelect []]
+              (PrivTable [Name Nothing "t1"])
+              [Name Nothing "role1"] DefaultDropBehaviour)
+
+    ,(TestStatement ansi2011
+      "revoke grant option for select on t1 from role1,role2 cascade"
+     $ RevokePrivilege GrantOptionFor [PrivSelect []]
+                       (PrivTable [Name Nothing "t1"])
+              [Name Nothing "role1",Name Nothing "role2"] Cascade)
+
+
+{-
+<revoke role statement> ::=
+  REVOKE [ ADMIN OPTION FOR ] <role revoked> [ { <comma> <role revoked> }... ]
+      FROM <grantee> [ { <comma> <grantee> }... ]
+      [ GRANTED BY <grantor> ]
+      <drop behavior>
+
+<role revoked> ::=
+  <role name>
+-}
+
+    ,(TestStatement ansi2011
+      "revoke role1 from role2"
+     $ RevokeRole NoAdminOptionFor [Name Nothing "role1"]
+                  [Name Nothing "role2"] DefaultDropBehaviour)
+
+    ,(TestStatement ansi2011
+      "revoke role1,role2 from role3,role4"
+     $ RevokeRole NoAdminOptionFor [Name Nothing "role1",Name Nothing "role2"]
+                  [Name Nothing "role3",Name Nothing "role4"] DefaultDropBehaviour)
+
+
+    ,(TestStatement ansi2011
+      "revoke admin option for role1 from role2 cascade"
+     $ RevokeRole AdminOptionFor [Name Nothing "role1"] [Name Nothing "role2"] Cascade)
+
+
+   ]
diff --git a/tests/Language/SQL/SimpleSQL/SQL2011Bits.hs b/tests/Language/SQL/SimpleSQL/SQL2011Bits.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/SQL2011Bits.hs
@@ -0,0 +1,234 @@
+
+{-
+Sections 17 and 19 in Foundation
+
+This module covers the tests for transaction management (begin,
+commit, savepoint, etc.), and session management (set).
+-}
+
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.SQL2011Bits (sql2011BitsTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+import Language.SQL.SimpleSQL.Syntax
+
+sql2011BitsTests :: TestItem
+sql2011BitsTests = Group "sql 2011 bits tests" [
+
+{-
+17 Transaction management
+
+17.1 <start transaction statement>
+
+<start transaction statement> ::=
+  START TRANSACTION [ <transaction characteristics> ]
+
+BEGIN is not in the standard!
+-}
+
+     (TestStatement ansi2011
+      "start transaction"
+     $ StartTransaction)
+
+{-
+17.2 <set transaction statement>
+
+<set transaction statement> ::=
+  SET [ LOCAL ] TRANSACTION <transaction characteristics>
+
+17.3 <transaction characteristics>
+
+<transaction characteristics> ::=
+      [ <transaction mode> [ { <comma> <transaction mode> }... ] ]
+
+<transaction mode> ::=
+    <isolation level>
+  | <transaction access mode>
+  | <diagnostics size>
+
+<transaction access mode> ::=
+    READ ONLY
+  | READ WRITE
+
+<isolation level> ::=
+  ISOLATION LEVEL <level of isolation>
+
+<level of isolation> ::=
+    READ UNCOMMITTED
+  | READ COMMITTED
+  | REPEATABLE READ
+  | SERIALIZABLE
+
+<diagnostics size> ::=
+  DIAGNOSTICS SIZE <number of conditions>
+
+<number of conditions> ::=
+  <simple value specification>
+
+17.4 <set constraints mode statement>
+
+<set constraints mode statement> ::=
+  SET CONSTRAINTS <constraint name list> { DEFERRED | IMMEDIATE }
+
+<constraint name list> ::=
+    ALL
+  | <constraint name> [ { <comma> <constraint name> }... ]
+
+17.5 <savepoint statement>
+
+<savepoint statement> ::=
+  SAVEPOINT <savepoint specifier>
+
+<savepoint specifier> ::=
+  <savepoint name>
+-}
+
+    ,(TestStatement ansi2011
+      "savepoint difficult_bit"
+     $ Savepoint $ Name Nothing "difficult_bit")
+
+
+{-
+17.6 <release savepoint statement>
+
+<release savepoint statement> ::=
+  RELEASE SAVEPOINT <savepoint specifier>
+-}
+
+    ,(TestStatement ansi2011
+      "release savepoint difficult_bit"
+     $ ReleaseSavepoint $ Name Nothing "difficult_bit")
+
+
+{-
+17.7 <commit statement>
+
+<commit statement> ::=
+  COMMIT [ WORK ] [ AND [ NO ] CHAIN ]
+-}
+
+    ,(TestStatement ansi2011
+      "commit"
+     $ Commit)
+
+    ,(TestStatement ansi2011
+      "commit work"
+     $ Commit)
+
+
+{-
+17.8 <rollback statement>
+
+<rollback statement> ::=
+  ROLLBACK [ WORK ] [ AND [ NO ] CHAIN ] [ <savepoint clause> ]
+
+<savepoint clause> ::=
+  TO SAVEPOINT <savepoint specifier>
+-}
+
+    ,(TestStatement ansi2011
+      "rollback"
+     $ Rollback Nothing)
+
+    ,(TestStatement ansi2011
+      "rollback work"
+     $ Rollback Nothing)
+
+    ,(TestStatement ansi2011
+      "rollback to savepoint difficult_bit"
+     $ Rollback $ Just $ Name Nothing "difficult_bit")
+
+
+{-
+19 Session management
+
+19.1 <set session characteristics statement>
+
+<set session characteristics statement> ::=
+  SET SESSION CHARACTERISTICS AS <session characteristic list>
+
+<session characteristic list> ::=
+  <session characteristic> [ { <comma> <session characteristic> }... ]
+
+<session characteristic> ::=
+  <session transaction characteristics>
+
+<session transaction characteristics> ::=
+  TRANSACTION <transaction mode> [ { <comma> <transaction mode> }... ]
+
+19.2 <set session user identifier statement>
+
+<set session user identifier statement> ::=
+  SET SESSION AUTHORIZATION <value specification>
+
+19.3 <set role statement>
+
+<set role statement> ::=
+  SET ROLE <role specification>
+
+<role specification> ::=
+    <value specification>
+  | NONE
+
+19.4 <set local time zone statement>
+
+<set local time zone statement> ::=
+  SET TIME ZONE <set time zone value>
+
+<set time zone value> ::=
+    <interval value expression>
+  | LOCAL
+
+19.5 <set catalog statement>
+
+<set catalog statement> ::=
+  SET <catalog name characteristic>
+
+<catalog name characteristic> ::=
+  CATALOG <value specification>
+
+19.6 <set schema statement>
+
+<set schema statement> ::=
+  SET <schema name characteristic>
+
+<schema name characteristic> ::=
+  SCHEMA <value specification>
+
+19.7 <set names statement>
+
+<set names statement> ::=
+  SET <character set name characteristic>
+
+<character set name characteristic> ::=
+  NAMES <value specification>
+
+19.8 <set path statement>
+
+<set path statement> ::=
+  SET <SQL-path characteristic>
+
+<SQL-path characteristic> ::=
+  PATH <value specification>
+
+19.9 <set transform group statement>
+
+<set transform group statement> ::=
+  SET <transform group characteristic>
+
+<transform group characteristic> ::=
+    DEFAULT TRANSFORM GROUP <value specification>
+  | TRANSFORM GROUP FOR TYPE <path-resolved user-defined type name> <value specification>
+
+19.10 <set session collation statement>
+
+<set session collation statement> ::=
+    SET COLLATION <collation specification> [ FOR <character set specification list> ]
+  | SET NO COLLATION [ FOR <character set specification list> ]
+
+<collation specification> ::=
+  <value specification>
+-}
+
+   ]
diff --git a/tests/Language/SQL/SimpleSQL/SQL2011DataManipulation.hs b/tests/Language/SQL/SimpleSQL/SQL2011DataManipulation.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/SQL2011DataManipulation.hs
@@ -0,0 +1,555 @@
+
+-- Section 14 in Foundation
+
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.SQL2011DataManipulation (sql2011DataManipulationTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+import Language.SQL.SimpleSQL.Syntax
+
+sql2011DataManipulationTests :: TestItem
+sql2011DataManipulationTests = Group "sql 2011 data manipulation tests"
+    [
+
+
+{-
+14 Data manipulation
+
+
+14.1 <declare cursor>
+
+<declare cursor> ::=
+  DECLARE <cursor name> <cursor properties>
+      FOR <cursor specification>
+
+14.2 <cursor properties>
+
+<cursor properties> ::=
+  [ <cursor sensitivity> ] [ <cursor scrollability> ] CURSOR
+      [ <cursor holdability> ]
+      [ <cursor returnability> ]
+
+<cursor sensitivity> ::=
+    SENSITIVE
+  | INSENSITIVE
+  | ASENSITIVE
+
+<cursor scrollability> ::=
+    SCROLL
+  | NO SCROLL
+
+<cursor holdability> ::=
+    WITH HOLD
+  | WITHOUT HOLD
+
+<cursor returnability> ::=
+    WITH RETURN
+  | WITHOUT RETURN
+
+14.3 <cursor specification>
+
+<cursor specification> ::=
+  <query expression> [ <updatability clause> ]
+
+<updatability clause> ::=
+  FOR { READ ONLY | UPDATE [ OF <column name list> ] }
+
+14.4 <open statement>
+
+<open statement> ::=
+  OPEN <cursor name>
+
+14.5 <fetch statement>
+
+<fetch statement> ::=
+     FETCH [ [ <fetch orientation> ] FROM ] <cursor name> INTO <fetch target list>
+
+<fetch orientation> ::=
+    NEXT
+  | PRIOR
+  | FIRST
+  | LAST
+  | { ABSOLUTE | RELATIVE } <simple value specification>
+
+<fetch target list> ::=
+     <target specification> [ { <comma> <target specification> }... ]
+
+
+14.6 <close statement>
+
+<close statement> ::=
+  CLOSE <cursor name>
+
+14.7 <select statement: single row>
+
+<select statement: single row> ::=
+  SELECT [ <set quantifier> ] <select list>
+      INTO <select target list>
+      <table expression>
+
+<select target list> ::=
+  <target specification> [ { <comma> <target specification> }... ]
+
+14.8 <delete statement: positioned>
+
+<delete statement: positioned> ::=
+     DELETE FROM <target table> [ [ AS ] <correlation name> ]
+         WHERE CURRENT OF <cursor name>
+
+<target table> ::=
+    <table name>
+  | ONLY <left paren> <table name> <right paren>
+
+14.9 <delete statement: searched>
+
+<delete statement: searched> ::=
+  DELETE FROM <target table>
+      [ FOR PORTION OF <application time period name>
+        FROM <point in time 1> TO <point in time 2> ]
+      [ [ AS ] <correlation name> ]
+      [ WHERE <search condition> ]
+-}
+
+     (TestStatement ansi2011 "delete from t"
+     $ Delete [Name Nothing "t"] Nothing Nothing)
+
+    ,(TestStatement ansi2011 "delete from t as u"
+     $ Delete [Name Nothing "t"] (Just (Name Nothing "u")) Nothing)
+
+    ,(TestStatement ansi2011 "delete from t where x = 5"
+     $ Delete [Name Nothing "t"] Nothing
+       (Just $ BinOp (Iden [Name Nothing "x"]) [Name Nothing "="] (NumLit "5")))
+
+
+    ,(TestStatement ansi2011 "delete from t as u where u.x = 5"
+     $ Delete [Name Nothing "t"] (Just (Name Nothing "u"))
+       (Just $ BinOp (Iden [Name Nothing "u", Name Nothing "x"]) [Name Nothing "="] (NumLit "5")))
+
+{-
+14.10 <truncate table statement>
+
+<truncate table statement> ::=
+  TRUNCATE TABLE <target table> [ <identity column restart option> ]
+
+<identity column restart option> ::=
+    CONTINUE IDENTITY
+  | RESTART IDENTITY
+-}
+
+    ,(TestStatement ansi2011 "truncate table t"
+     $ Truncate [Name Nothing "t"] DefaultIdentityRestart)
+
+    ,(TestStatement ansi2011 "truncate table t continue identity"
+     $ Truncate [Name Nothing "t"] ContinueIdentity)
+
+    ,(TestStatement ansi2011 "truncate table t restart identity"
+     $ Truncate [Name Nothing "t"] RestartIdentity)
+
+
+{-
+14.11 <insert statement>
+
+<insert statement> ::=
+  INSERT INTO <insertion target> <insert columns and source>
+
+<insertion target> ::=
+  <table name>
+
+<insert columns and source> ::=
+    <from subquery>
+  | <from constructor>
+  | <from default>
+
+<from subquery> ::=
+  [ <left paren> <insert column list> <right paren> ]
+      [ <override clause> ]
+      <query expression>
+
+<from constructor> ::=
+  [ <left paren> <insert column list> <right paren> ]
+      [ <override clause> ]
+      <contextually typed table value constructor>
+
+<override clause> ::=
+    OVERRIDING USER VALUE
+  | OVERRIDING SYSTEM VALUE
+
+<from default> ::=
+  DEFAULT VALUES
+
+<insert column list> ::=
+  <column name list>
+-}
+
+    ,(TestStatement ansi2011 "insert into t select * from u"
+     $ Insert [Name Nothing "t"] Nothing
+       $ InsertQuery $ toQueryExpr $ makeSelect
+         {msSelectList = [(Star, Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "u"]]})
+
+    ,(TestStatement ansi2011 "insert into t(a,b,c) select * from u"
+     $ Insert [Name Nothing "t"] (Just [Name Nothing "a", Name Nothing "b", Name Nothing "c"])
+       $ InsertQuery $ toQueryExpr $ makeSelect
+         {msSelectList = [(Star, Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "u"]]})
+
+    ,(TestStatement ansi2011 "insert into t default values"
+     $ Insert [Name Nothing "t"] Nothing DefaultInsertValues)
+
+    ,(TestStatement ansi2011 "insert into t values(1,2)"
+     $ Insert [Name Nothing "t"] Nothing
+       $ InsertQuery $ Values [[NumLit "1", NumLit "2"]])
+
+    ,(TestStatement ansi2011 "insert into t values (1,2),(3,4)"
+     $ Insert [Name Nothing "t"] Nothing
+       $ InsertQuery $ Values [[NumLit "1", NumLit "2"]
+                              ,[NumLit "3", NumLit "4"]])
+
+    ,(TestStatement ansi2011
+      "insert into t values (default,null,array[],multiset[])"
+     $ Insert [Name Nothing "t"] Nothing
+       $ InsertQuery $ Values [[Iden [Name Nothing "default"]
+                               ,Iden [Name Nothing "null"]
+                               ,Array (Iden [Name Nothing "array"]) []
+                               ,MultisetCtor []]])
+
+
+{-
+14.12 <merge statement>
+
+<merge statement> ::=
+  MERGE INTO <target table> [ [ AS ] <merge correlation name> ]
+      USING <table reference>
+      ON <search condition> <merge operation specification>
+
+merge into t
+  using t
+  on a = b
+  merge operation specification
+
+merge into t as u
+using (table factor | joined expression)
+
+ MERGE INTO tablename USING table_reference ON (condition)
+   WHEN MATCHED THEN
+   UPDATE SET column1 = value1 [, column2 = value2 ...]
+   WHEN NOT MATCHED THEN
+   INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...
+
+merge into t23
+using t42
+on t42.id = t23.id
+when matched then
+    update
+    set     t23.col1 = t42.col1
+when not matched then
+    insert  (id, col1)
+    values  (t42.id, t42.col1)
+
+
+
+MERGE INTO TableA u
+
+USING (SELECT b.Key1, b.ColB1, c.ColC1
+
+FROM TableB b
+
+INNER JOIN TableC c ON c.KeyC1 = b.KeyB1
+
+) s
+
+ON (u.KeyA1 = s.KeyA1)
+
+WHEN MATCHED THEN
+
+UPDATE SET u.ColA1 = s.ColB1, u.ColA2 = s.ColC1
+
+
+MERGE INTO Department 
+USING NewDept AS ND 
+ON nd.Department_Number = Department.
+Department_Number 
+WHEN MATCHED THEN UPDATE 
+SET budget_amount = nd.Budget_Amount 
+WHEN NOT MATCHED THEN INSERT 
+VALUES 
+(nd.Department_Number, nd.Department_
+Name, nd.Budget_Amount, 
+ nd.Manager_Employee_Number);
+
+
+MERGE INTO Orders2 
+USING Orders3 
+ON ORDERS3.Order_Number = Orders2.
+Order_Number 
+WHEN NOT MATCHED THEN INSERT 
+Orders3.order_number, Orders3.
+invoice_number, 
+ Orders3.customer_number, Orders3.
+initial_order_date, 
+ Orders3.invoice_date, Orders3.
+invoice_amount);
+
+MERGE INTO Orders2 
+USING Orders3 
+ON ORDERS3.Order_Number = Orders2.
+Order_Number AND 1=0 
+WHEN NOT MATCHED THEN INSERT 
+(Orders3.order_number, Orders3.invoice_number, 
+ Orders3.customer_number, Orders3.
+initial_order_date, 
+ Orders3.invoice_date, Orders3.
+invoice_amount);
+
+MERGE INTO Department 
+USING NewDept AS ND 
+ON nd.Department_Number = Department.
+Department_Number 
+WHEN MATCHED THEN UPDATE 
+SET budget_amount = nd.Budget_Amount 
+LOGGING ALL ERRORS WITH NO LIMIT;
+
+
+MERGE INTO Department 
+USING 
+ (SELECT Department_Number,
+department_name, 
+        Budget_Amount, 
+Manager_Employee_Number 
+    FROM NewDept 
+    WHERE Department_Number IN 
+(SELECT Department_Number 
+        FROM Employee)) AS m
+ON m.Department_Number = Department.
+Department_Number 
+WHEN MATCHED THEN UPDATE 
+SET budget_amount = m.Budget_Amount 
+WHEN NOT MATCHED THEN INSERT 
+(m.Department_Number, m.Department_
+Name, m.Budget_Amount, 
+m.Manager_Employee_Number) 
+LOGGING ALL ERRORS WITH NO LIMIT;
+
+ 
+MERGE INTO Customers AS c
+USING      Moved     AS m
+      ON   m.SSN      = c.SSN
+WHEN MATCHED
+THEN UPDATE
+SET        Street     = m.Street,
+           HouseNo    = m.HouseNo,
+           City       = m.City;
+
+MERGE INTO CentralOfficeAccounts AS C    -- Target
+USING BranchOfficeAccounts AS B          -- Source
+   ON C.account_nbr = B.account_nbr
+WHEN MATCHED THEN                        -- On match update
+     UPDATE SET C.company_name = B.company_name,
+                C.primary_contact = B.primary_contact,
+                C.contact_phone = B.contact_phone
+WHEN NOT MATCHED THEN                    -- Add missing
+     INSERT (account_nbr, company_name, primary_contact, contact_phone)
+     VALUES (B.account_nbr, B.company_name, B.primary_contact, B.contact_phone);
+ 
+SELECT account_nbr, company_name, primary_contact, contact_phone 
+FROM CentralOfficeAccounts;
+
+
+
+MERGE INTO CentralOfficeAccounts AS C   -- Target
+USING BranchOfficeAccounts AS B         -- Source
+   ON C.account_nbr = B.account_nbr
+WHEN MATCHED                            -- On match update
+ AND (C.company_name <> B.company_name  -- Additional search conditions
+   OR C.primary_contact <> B.primary_contact
+   OR C.contact_phone <> B.contact_phone) THEN                        
+     UPDATE SET C.company_name = B.company_name,
+                C.primary_contact = B.primary_contact,
+                C.contact_phone = B.contact_phone
+WHEN NOT MATCHED THEN                   -- Add missing
+     INSERT (account_nbr, company_name, primary_contact, contact_phone)
+     VALUES (B.account_nbr, B.company_name, B.primary_contact, B.contact_phone);
+
+
+
+MERGE INTO CentralOfficeAccounts AS C   -- Target
+USING BranchOfficeAccounts AS B         -- Source
+   ON C.account_nbr = B.account_nbr
+WHEN MATCHED                            -- On match update
+ AND (C.company_name <> B.company_name  -- Additional search conditions
+   OR C.primary_contact <> B.primary_contact
+   OR C.contact_phone <> B.contact_phone) THEN                        
+     UPDATE SET C.company_name = B.company_name,
+                C.primary_contact = B.primary_contact,
+                C.contact_phone = B.contact_phone
+WHEN NOT MATCHED THEN                   -- Add missing
+     INSERT (account_nbr, company_name, primary_contact, contact_phone)
+     VALUES (B.account_nbr, B.company_name, B.primary_contact, B.contact_phone)
+WHEN SOURCE NOT MATCHED THEN            -- Delete missing from source
+     DELETE;
+ 
+SELECT account_nbr, company_name, primary_contact, contact_phone 
+FROM CentralOfficeAccounts; 
+
+
+
+
+<merge correlation name> ::=
+  <correlation name>
+
+<merge operation specification> ::=
+  <merge when clause>...
+
+<merge when clause> ::=
+    <merge when matched clause>
+  | <merge when not matched clause>
+
+<merge when matched clause> ::=
+  WHEN MATCHED [ AND <search condition> ]
+      THEN <merge update or delete specification>
+
+<merge update or delete specification> ::=
+    <merge update specification>
+  | <merge delete specification>
+
+<merge when not matched clause> ::=
+  WHEN NOT MATCHED [ AND <search condition> ]
+      THEN <merge insert specification>
+
+<merge update specification> ::=
+  UPDATE SET <set clause list>
+
+<merge delete specification> ::=
+  DELETE
+
+<merge insert specification> ::=
+  INSERT [ <left paren> <insert column list> <right paren> ]
+      [ <override clause> ]
+      VALUES <merge insert value list>
+
+<merge insert value list> ::=
+  <left paren>
+      <merge insert value element> [ { <comma> <merge insert value element> }... ]
+      <right paren>
+
+<merge insert value element> ::=
+    <value expression>
+  | <contextually typed value specification>
+
+14.13 <update statement: positioned>
+
+<updatestatement: positioned> ::=
+     UPDATE <target table> [ [ AS ] <correlation name> ]
+         SET <set clause list>
+         WHERE CURRENT OF <cursor name>
+
+14.14 <update statement: searched>
+
+<update statement: searched> ::=
+  UPDATE <target table>
+      [ FOR PORTION OF <application time period name>
+        FROM <point in time 1> TO <point in time 2> ]
+      [ [ AS ] <correlation name> ]
+      SET <set clause list>
+      [ WHERE <search condition> ]
+-}
+
+
+    ,(TestStatement ansi2011 "update t set a=b"
+     $ Update [Name Nothing "t"] Nothing
+       [Set [Name Nothing "a"] (Iden [Name Nothing "b"])] Nothing)
+
+    ,(TestStatement ansi2011 "update t set a=b, c=5"
+     $ Update [Name Nothing "t"] Nothing
+       [Set [Name Nothing "a"] (Iden [Name Nothing "b"])
+       ,Set [Name Nothing "c"] (NumLit "5")] Nothing)
+
+
+    ,(TestStatement ansi2011 "update t set a=b where a>5"
+     $ Update [Name Nothing "t"] Nothing
+       [Set [Name Nothing "a"] (Iden [Name Nothing "b"])]
+       $ Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5"))
+
+
+    ,(TestStatement ansi2011 "update t as u set a=b where u.a>5"
+     $ Update [Name Nothing "t"] (Just $ Name Nothing "u")
+       [Set [Name Nothing "a"] (Iden [Name Nothing "b"])]
+       $ Just $ BinOp (Iden [Name Nothing "u",Name Nothing "a"])
+                      [Name Nothing ">"] (NumLit "5"))
+
+    ,(TestStatement ansi2011 "update t set (a,b)=(3,5)"
+     $ Update [Name Nothing "t"] Nothing
+       [SetMultiple [[Name Nothing "a"],[Name Nothing "b"]]
+                    [NumLit "3", NumLit "5"]] Nothing)
+
+
+
+{-
+14.15 <set clause list>
+
+<set clause list> ::=
+  <set clause> [ { <comma> <set clause> }... ]
+
+<set clause> ::=
+    <multiple column assignment>
+  | <set target> <equals operator> <update source>
+
+<set target> ::=
+    <update target>
+  | <mutated set clause>
+
+<multiple column assignment> ::=
+  <set target list> <equals operator> <assigned row>
+
+<set target list> ::=
+  <left paren> <set target> [ { <comma> <set target> }... ] <right paren>
+
+<assigned row> ::=
+  <contextually typed row value expression>
+
+<update target> ::=
+    <object column>
+  | <object column>
+      <left bracket or trigraph> <simple value specification> <right bracket or trigraph>
+
+<object column> ::=
+  <column name>
+
+<mutated set clause> ::=
+  <mutated target> <period> <method name>
+
+<mutated target> ::=
+    <object column>
+  | <mutated set clause>
+
+<update source> ::=
+    <value expression>
+  | <contextually typed value specification>
+
+14.16 <temporary table declaration>
+
+<temporary table declaration> ::=
+  DECLARE LOCAL TEMPORARY TABLE <table name> <table element list>
+      [ ON COMMIT <table commit action> ROWS ]
+
+declare local temporary table t (a int) [on commit {preserve | delete} rows]
+
+14.17 <free locator statement>
+
+<free locator statement> ::=
+  FREE LOCATOR <locator reference> [ { <comma> <locator reference> }... ]
+
+<locator reference> ::=
+    <host parameter name>
+  | <embedded variable name>
+  | <dynamic parameter specification>
+
+14.18 <hold locator statement>
+
+<hold locator statement> ::=
+  HOLD LOCATOR <locator reference> [ { <comma> <locator reference> }... ]
+-}
+
+
+   ]
diff --git a/tests/Language/SQL/SimpleSQL/SQL2011Queries.hs b/tests/Language/SQL/SimpleSQL/SQL2011Queries.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/SQL2011Queries.hs
@@ -0,0 +1,4520 @@
+
+{-
+This file goes through the grammar for SQL 2011 queries (using the
+draft standard).
+
+There are other files which cover some of the other sections from SQL
+2011 (ddl, non-query dml, etc).
+
+Possible sections not in the todo which could
+be covered:
+
+13 modules
+16 control statements
+18 connection management
+20 dynamic
+22 direct
+23 diagnostics
+
+procedural sql
+
+some of the main areas being left for now:
+temporal and versioning stuff
+modules
+ref stuff
+todo: finish this list
+
+
+
+The goal is to create some example tests for each bit of grammar, with
+some areas getting more comprehensive coverage tests, and also to note
+which parts aren't currently supported.
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.SQL2011Queries (sql2011QueryTests) where
+import Language.SQL.SimpleSQL.TestTypes
+import Language.SQL.SimpleSQL.Syntax
+
+import Data.Text (Text)
+
+sql2011QueryTests :: TestItem
+sql2011QueryTests = Group "sql 2011 query tests"
+    [literals
+    ,identifiers
+    ,typeNameTests
+    ,fieldDefinition
+    ,valueExpressions
+    ,queryExpressions
+    ,scalarSubquery
+    ,predicates
+    ,intervalQualifier
+    ,collateClause
+    ,aggregateFunction
+    ,sortSpecificationList
+    ]
+
+{-
+= 5 Lexical elements
+
+The tests don't make direct use of these definitions.
+
+== 5.1 <SQL terminal character>
+
+Function
+
+Define the terminal symbols of the SQL language and the elements of
+strings.
+
+<SQL terminal character> ::= <SQL language character>
+
+<SQL language character> ::=
+    <simple Latin letter>
+  | <digit>
+  | <SQL special character>
+
+<simple Latin letter> ::=
+    <simple Latin upper case letter>
+  | <simple Latin lower case letter>
+
+<simple Latin upper case letter> ::=
+    A | B | C | D | E | F | G | H | I | J | K | L | M | N | O
+  | P | Q | R | S | T | U | V | W | X | Y | Z
+
+<simple Latin lower case letter> ::=
+    a | b | c | d | e | f | g | h | i | j | k | l | m | n | o
+  | p | q | r | s | t | u | v | w | x | y | z
+
+<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
+
+<SQL special character> ::=
+    <space>
+  | <double quote>
+  | <percent>
+  | <ampersand>
+  | <quote>
+  | <left paren>
+  | <right paren>
+  | <asterisk>
+  | <plus sign>
+  | <comma>
+  | <minus sign>
+  | <period>
+  | <solidus>
+  | <colon>
+  | <semicolon>
+  | <less than operator>
+  | <equals operator>
+  | <greater than operator>
+  | <question mark>
+  | <left bracket>
+  | <right bracket>
+  | <circumflex>
+  | <underscore>
+  | <vertical bar>
+  | <left brace>
+  | <right brace>
+
+<space> ::= !! See the Syntax Rules.
+
+<double quote> ::= "
+
+<percent> ::= %
+
+<ampersand> ::= &
+
+<quote> ::= '
+
+<left paren> ::= (
+
+<right paren> ::= )
+
+<asterisk> ::= *
+
+<plus sign> ::= +
+
+<comma> ::= ,
+
+<minus sign> ::= -
+
+<period> ::= .
+
+<solidus> ::= /
+
+<reverse solidus> ::= \
+
+<colon> ::= :
+
+<semicolon> ::= ;
+
+<less than operator> ::= <
+
+<equals operator> ::= =
+
+<greater than operator> ::= >
+
+<question mark> ::= ?
+
+<left bracket or trigraph> ::= <left bracket> | <left bracket trigraph>
+
+<right bracket or trigraph> ::= <right bracket> | <right bracket trigraph>
+
+<left bracket> ::= [
+
+<left bracket trigraph> ::= ??(
+
+<right bracket> ::= ]
+
+<right bracket trigraph> ::= ??)
+
+<circumflex> ::= ^
+
+<underscore> ::= _
+
+<vertical bar> ::= |
+
+<left brace> ::= {
+
+<right brace> ::= }
+
+== 5.2 <token> and <separator>
+
+Function
+
+Specify lexical units (tokens and separators) that participate in SQL
+language.
+
+<token> ::= <nondelimiter token> | <delimiter token>
+
+<nondelimiter token> ::=
+    <regular identifier>
+  | <key word>
+  | <unsigned numeric literal>
+  | <national character string literal>
+  | <binary string literal>
+  | <large object length token>
+  | <Unicode delimited identifier>
+  | <Unicode character string literal>
+  | <SQL language identifier>
+
+<regular identifier> ::= <identifier body>
+
+<identifier body> ::= <identifier start> [ <identifier part>... ]
+
+<identifier part> ::= <identifier start> | <identifier extend>
+
+<identifier start> ::= !! See the Syntax Rules.
+
+<identifier extend> ::= !! See the Syntax Rules.
+
+<large object length token> ::= <digit>... <multiplier>
+
+<multiplier> ::= K | M | G | T | P
+
+<delimited identifier> ::=
+  <double quote> <delimited identifier body> <double quote>
+
+<delimited identifier body> ::= <delimited identifier part>...
+
+<delimited identifier part> ::=
+    <nondoublequote character>
+  | <doublequote symbol>
+
+<Unicode delimited identifier> ::=
+  U <ampersand> <double quote> <Unicode delimiter body> <double quote>
+      <Unicode escape specifier>
+
+<Unicode escape specifier> ::=
+  [ UESCAPE <quote> <Unicode escape character> <quote> ]
+
+<Unicode delimiter body> ::= <Unicode identifier part>...
+
+<Unicode identifier part> ::=
+    <delimited identifier part>
+  | <Unicode escape value>
+
+<Unicode escape value> ::=
+    <Unicode 4 digit escape value>
+  | <Unicode 6 digit escape value>
+  | <Unicode character escape value>
+
+<Unicode 4 digit escape value> ::=
+  <Unicode escape character> <hexit> <hexit> <hexit> <hexit>
+
+<Unicode 6 digit escape value> ::=
+  <Unicode escape character> <plus sign>
+      <hexit> <hexit> <hexit> <hexit> <hexit> <hexit>
+
+<Unicode character escape value> ::=
+  <Unicode escape character> <Unicode escape character>
+
+<Unicode escape character> ::= !! See the Syntax Rules.
+
+<nondoublequote character> ::= !! See the Syntax Rules.
+
+<doublequote symbol> ::= ""!! two consecutive double quote characters
+
+<delimiter token> ::=
+    <character string literal>
+  | <date string>
+  | <time string>
+  | <timestamp string>
+  | <interval string>
+  | <delimited identifier>
+  | <SQL special character>
+  | <not equals operator>
+  | <greater than or equals operator>
+  | <less than or equals operator>
+  | <concatenation operator>
+  | <right arrow>
+  | <left bracket trigraph>
+  | <right bracket trigraph>
+  | <double colon>
+  | <double period>
+  | <named argument assignment token>
+
+<not equals operator> ::= <>
+
+<greater than or equals operator> ::= >=
+
+<less than or equals operator> ::= <=
+
+<concatenation operator> ::= ||
+
+<right arrow> ::= ->
+
+<double colon> ::= ::
+
+<double period> ::= ..
+
+<named argument assignment token> ::= =>
+
+<separator> ::= { <comment> | <white space> }...
+
+<white space> ::= !! See the Syntax Rules.
+
+<comment> ::= <simple comment> | <bracketed comment>
+
+<simple comment> ::=
+  <simple comment introducer> [ <comment character>... ] <newline>
+
+<simple comment introducer> ::= <minus sign> <minus sign>
+
+<bracketed comment> ::=
+  <bracketed comment introducer>
+      <bracketed comment contents>
+      <bracketed comment terminator>
+
+<bracketed comment introducer> ::= /*
+
+<bracketed comment terminator> ::= */
+
+<bracketed comment contents> ::=
+  [ { <comment character> | <separator> }... ]!! See the Syntax Rules.
+
+<comment character> ::= <nonquote character> | <quote>
+
+<newline> ::= !! See the Syntax Rules.
+
+<key word> ::= <reserved word> | <non-reserved word>
+
+<non-reserved word> ::=
+    A | ABSOLUTE | ACTION | ADA | ADD | ADMIN | AFTER | ALWAYS | ASC
+  | ASSERTION | ASSIGNMENT | ATTRIBUTE | ATTRIBUTES
+
+  | BEFORE | BERNOULLI | BREADTH
+
+  | C | CASCADE | CATALOG | CATALOG_NAME | CHAIN | CHARACTER_SET_CATALOG
+  | CHARACTER_SET_NAME | CHARACTER_SET_SCHEMA | CHARACTERISTICS | CHARACTERS
+  | CLASS_ORIGIN | COBOL | COLLATION | COLLATION_CATALOG | COLLATION_NAME | COLLATION_SCHEMA
+  | COLUMN_NAME | COMMAND_FUNCTION | COMMAND_FUNCTION_CODE | COMMITTED
+  | CONDITION_NUMBER | CONNECTION | CONNECTION_NAME | CONSTRAINT_CATALOG | CONSTRAINT_NAME
+  | CONSTRAINT_SCHEMA | CONSTRAINTS | CONSTRUCTOR | CONTINUE | CURSOR_NAME
+
+  | DATA | DATETIME_INTERVAL_CODE | DATETIME_INTERVAL_PRECISION | DEFAULTS | DEFERRABLE
+  | DEFERRED | DEFINED | DEFINER | DEGREE | DEPTH | DERIVED | DESC | DESCRIPTOR
+  | DIAGNOSTICS | DISPATCH | DOMAIN | DYNAMIC_FUNCTION | DYNAMIC_FUNCTION_CODE
+
+  | ENFORCED | EXCLUDE | EXCLUDING | EXPRESSION
+
+  | FINAL | FIRST | FLAG | FOLLOWING | FORTRAN | FOUND
+
+  | G | GENERAL | GENERATED | GO | GOTO | GRANTED
+
+  | HIERARCHY
+
+  | IGNORE | IMMEDIATE | IMMEDIATELY | IMPLEMENTATION | INCLUDING | INCREMENT | INITIALLY
+  | INPUT | INSTANCE | INSTANTIABLE | INSTEAD | INVOKER | ISOLATION
+
+  | K | KEY | KEY_MEMBER | KEY_TYPE
+
+  | LAST | LENGTH | LEVEL | LOCATOR
+
+  | M | MAP | MATCHED | MAXVALUE | MESSAGE_LENGTH | MESSAGE_OCTET_LENGTH
+  | MESSAGE_TEXT | MINVALUE | MORE | MUMPS
+
+  | NAME | NAMES | NESTING | NEXT | NFC | NFD | NFKC | NFKD
+  | NORMALIZED | NULLABLE | NULLS | NUMBER
+
+  | OBJECT | OCTETS | OPTION | OPTIONS | ORDERING | ORDINALITY | OTHERS
+  | OUTPUT | OVERRIDING
+
+  | P | PAD | PARAMETER_MODE | PARAMETER_NAME | PARAMETER_ORDINAL_POSITION
+  | PARAMETER_SPECIFIC_CATALOG | PARAMETER_SPECIFIC_NAME | PARAMETER_SPECIFIC_SCHEMA
+  | PARTIAL | PASCAL | PATH | PLACING | PLI | PRECEDING | PRESERVE | PRIOR
+  | PRIVILEGES | PUBLIC
+
+  | READ | RELATIVE | REPEATABLE | RESPECT | RESTART | RESTRICT | RETURNED_CARDINALITY
+  | RETURNED_LENGTH | RETURNED_OCTET_LENGTH | RETURNED_SQLSTATE | ROLE
+  | ROUTINE | ROUTINE_CATALOG | ROUTINE_NAME | ROUTINE_SCHEMA | ROW_COUNT
+
+  | SCALE | SCHEMA | SCHEMA_NAME | SCOPE_CATALOG | SCOPE_NAME | SCOPE_SCHEMA
+  | SECTION | SECURITY | SELF | SEQUENCE | SERIALIZABLE | SERVER_NAME | SESSION
+  | SETS | SIMPLE | SIZE | SOURCE | SPACE | SPECIFIC_NAME | STATE | STATEMENT
+  | STRUCTURE | STYLE | SUBCLASS_ORIGIN
+
+  | T | TABLE_NAME | TEMPORARY | TIES | TOP_LEVEL_COUNT | TRANSACTION
+  | TRANSACTION_ACTIVE | TRANSACTIONS_COMMITTED | TRANSACTIONS_ROLLED_BACK
+  | TRANSFORM | TRANSFORMS | TRIGGER_CATALOG | TRIGGER_NAME | TRIGGER_SCHEMA | TYPE
+
+  | UNBOUNDED | UNCOMMITTED | UNDER | UNNAMED | USAGE | USER_DEFINED_TYPE_CATALOG
+  | USER_DEFINED_TYPE_CODE | USER_DEFINED_TYPE_NAME | USER_DEFINED_TYPE_SCHEMA
+
+  | VIEW
+
+  | WORK | WRITE
+
+  | ZONE
+
+<reserved word> ::=
+    ABS | ALL | ALLOCATE | ALTER | AND | ANY | ARE | ARRAY | ARRAY_AGG
+  | ARRAY_MAX_CARDINALITY | AS | ASENSITIVE | ASYMMETRIC | AT | ATOMIC | AUTHORIZATION
+  | AVG
+
+  | BEGIN | BEGIN_FRAME | BEGIN_PARTITION | BETWEEN | BIGINT | BINARY
+  | BLOB | BOOLEAN | BOTH | BY
+
+  | CALL | CALLED | CARDINALITY | CASCADED | CASE | CAST | CEIL | CEILING
+  | CHAR | CHAR_LENGTH | CHARACTER | CHARACTER_LENGTH | CHECK | CLOB | CLOSE
+  | COALESCE | COLLATE | COLLECT | COLUMN | COMMIT | CONDITION | CONNECT
+  | CONSTRAINT | CONTAINS | CONVERT | CORR | CORRESPONDING | COUNT | COVAR_POP
+  | COVAR_SAMP | CREATE | CROSS | CUBE | CUME_DIST | CURRENT | CURRENT_CATALOG
+  | CURRENT_DATE | CURRENT_DEFAULT_TRANSFORM_GROUP | CURRENT_PATH | CURRENT_ROLE
+  | CURRENT_ROW | CURRENT_SCHEMA | CURRENT_TIME | CURRENT_TIMESTAMP
+  | CURRENT_TRANSFORM_GROUP_FOR_TYPE | CURRENT_USER | CURSOR | CYCLE
+
+  | DATE | DAY | DEALLOCATE | DEC | DECIMAL | DECLARE | DEFAULT | DELETE
+  | DENSE_RANK | DEREF | DESCRIBE | DETERMINISTIC | DISCONNECT | DISTINCT
+  | DOUBLE | DROP | DYNAMIC
+
+  | EACH | ELEMENT | ELSE | END | END_FRAME | END_PARTITION | END-EXEC
+  | EQUALS | ESCAPE | EVERY | EXCEPT | EXEC | EXECUTE | EXISTS | EXP
+  | EXTERNAL | EXTRACT
+
+  | FALSE | FETCH | FILTER | FIRST_VALUE | FLOAT | FLOOR | FOR | FOREIGN
+  | FRAME_ROW | FREE | FROM | FULL | FUNCTION | FUSION
+
+  | GET | GLOBAL | GRANT | GROUP | GROUPING | GROUPS
+
+  | HAVING | HOLD | HOUR
+
+  | IDENTITY | IN | INDICATOR | INNER | INOUT | INSENSITIVE | INSERT
+  | INT | INTEGER | INTERSECT | INTERSECTION | INTERVAL | INTO | IS
+
+  | JOIN
+
+  | LAG | LANGUAGE | LARGE | LAST_VALUE | LATERAL | LEAD | LEADING | LEFT
+  | LIKE | LIKE_REGEX | LN | LOCAL | LOCALTIME | LOCALTIMESTAMP | LOWER
+
+  | MATCH | MAX | MEMBER | MERGE | METHOD | MIN | MINUTE
+  | MOD | MODIFIES | MODULE | MONTH | MULTISET
+
+  | NATIONAL | NATURAL | NCHAR | NCLOB | NEW | NO | NONE | NORMALIZE | NOT
+  | NTH_VALUE | NTILE | NULL | NULLIF | NUMERIC
+
+  | OCTET_LENGTH | OCCURRENCES_REGEX | OF | OFFSET | OLD | ON | ONLY | OPEN
+  | OR | ORDER | OUT | OUTER | OVER | OVERLAPS | OVERLAY
+
+  | PARAMETER | PARTITION | PERCENT | PERCENT_RANK | PERCENTILE_CONT
+  | PERCENTILE_DISC | PERIOD | PORTION | POSITION | POSITION_REGEX | POWER | PRECEDES
+  | PRECISION | PREPARE | PRIMARY | PROCEDURE
+
+  | RANGE | RANK | READS | REAL | RECURSIVE | REF | REFERENCES | REFERENCING
+  | REGR_AVGX | REGR_AVGY | REGR_COUNT | REGR_INTERCEPT | REGR_R2 | REGR_SLOPE
+  | REGR_SXX | REGR_SXY | REGR_SYY | RELEASE | RESULT | RETURN | RETURNS
+  | REVOKE | RIGHT | ROLLBACK | ROLLUP | ROW | ROW_NUMBER | ROWS
+
+  | SAVEPOINT | SCOPE | SCROLL | SEARCH | SECOND | SELECT
+  | SENSITIVE | SESSION_USER | SET | SIMILAR | SMALLINT | SOME | SPECIFIC
+  | SPECIFICTYPE | SQL | SQLEXCEPTION | SQLSTATE | SQLWARNING | SQRT | START
+  | STATIC | STDDEV_POP | STDDEV_SAMP | SUBMULTISET | SUBSTRING | SUBSTRING_REGEX
+  | SUCCEEDS | SUM | SYMMETRIC | SYSTEM | SYSTEM_TIME | SYSTEM_USER
+
+  | TABLE | TABLESAMPLE | THEN | TIME | TIMESTAMP | TIMEZONE_HOUR | TIMEZONE_MINUTE
+  | TO | TRAILING | TRANSLATE | TRANSLATE_REGEX | TRANSLATION | TREAT
+  | TRIGGER | TRUNCATE | TRIM | TRIM_ARRAY | TRUE
+
+  | UESCAPE | UNION | UNIQUE | UNKNOWN | UNNEST | UPDATE | UPPER | USER | USING
+
+  | VALUE | VALUES | VALUE_OF | VAR_POP | VAR_SAMP | VARBINARY
+  | VARCHAR | VARYING | VERSIONING
+
+  | WHEN | WHENEVER | WHERE | WIDTH_BUCKET | WINDOW | WITH | WITHIN | WITHOUT
+
+  | YEAR
+
+== 5.3 <literal>
+
+Function
+Specify a non-null value.
+-}
+
+literals :: TestItem
+literals = Group "literals"
+    [numericLiterals,generalLiterals]
+
+{-
+<literal> ::= <signed numeric literal> | <general literal>
+
+<unsigned literal> ::= <unsigned numeric literal> | <general literal>
+
+<general literal> ::=
+    <character string literal>
+  | <national character string literal>
+  | <Unicode character string literal>
+  | <binary string literal>
+  | <datetime literal>
+  | <interval literal>
+  | <boolean literal>
+-}
+
+generalLiterals :: TestItem
+generalLiterals = Group "general literals"
+    [characterStringLiterals
+    ,nationalCharacterStringLiterals
+    ,unicodeCharacterStringLiterals
+    ,binaryStringLiterals
+    ,dateTimeLiterals
+    ,intervalLiterals
+    ,booleanLiterals]
+
+{-
+<character string literal> ::=
+  [ <introducer> <character set specification> ]
+      <quote> [ <character representation>... ] <quote>
+      [ { <separator> <quote> [ <character representation>... ] <quote> }... ]
+
+<introducer> ::= <underscore>
+
+<character representation> ::= <nonquote character> | <quote symbol>
+
+<nonquote character> ::= !! See the Syntax Rules.
+
+<quote symbol> ::= <quote> <quote>
+-}
+
+characterStringLiterals :: TestItem
+characterStringLiterals = Group "character string literals"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("'a regular string literal'"
+     ,StringLit "'" "'" "a regular string literal")
+    ,("'something' ' some more' 'and more'"
+     ,StringLit "'" "'" "something some moreand more")
+    ,("'something' \n ' some more' \t 'and more'"
+     ,StringLit "'" "'" "something some moreand more")
+    ,("'something' -- a comment\n ' some more' /*another comment*/ 'and more'"
+     ,StringLit "'" "'" "something some moreand more")
+    ,("'a quote: '', stuff'"
+     ,StringLit "'" "'" "a quote: '', stuff")
+    ,("''"
+     ,StringLit "'" "'" "")
+
+{-
+I'm not sure how this should work. Maybe the parser should reject non
+ascii characters in strings and identifiers unless the current SQL
+character set allows them.
+-}
+
+    ,("_francais 'français'"
+     ,TypedLit (TypeName [Name Nothing "_francais"]) "français")
+    ]
+
+{-
+<national character string literal> ::=
+  N <quote> [ <character representation>... ]
+      <quote> [ { <separator> <quote> [ <character representation>... ] <quote> }... ]
+-}
+
+nationalCharacterStringLiterals :: TestItem
+nationalCharacterStringLiterals = Group "national character string literals"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("N'something'", StringLit "N'" "'" "something")
+    ,("n'something'", StringLit "n'" "'" "something")
+    ]
+
+{-
+<Unicode character string literal> ::=
+  [ <introducer> <character set specification> ]
+      U <ampersand> <quote> [ <Unicode representation>... ] <quote>
+      [ { <separator> <quote> [ <Unicode representation>... ] <quote> }... ]
+      <Unicode escape specifier>
+
+<Unicode representation> ::=
+    <character representation>
+  | <Unicode escape value>
+-}
+
+unicodeCharacterStringLiterals :: TestItem
+unicodeCharacterStringLiterals = Group "unicode character string literals"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("U&'something'", StringLit "U&'" "'" "something")
+    {-,("u&'something' escape ="
+     ,Escape (StringLit "u&'" "'" "something") '=')
+    ,("u&'something' uescape ="
+     ,UEscape (StringLit "u&'" "'" "something") '=')-}
+    ]
+
+{-
+TODO: unicode escape
+
+<binary string literal> ::=
+  X <quote> [ <space>... ] [ { <hexit> [ <space>... ] <hexit> [ <space>... ] }... ] <quote>
+      [ { <separator> <quote> [ <space>... ] [ { <hexit> [ <space>... ]
+      <hexit> [ <space>... ] }... ] <quote> }... ]
+
+<hexit> ::= <digit> | A | B | C | D | E | F | a | b | c | d | e | f
+-}
+
+binaryStringLiterals :: TestItem
+binaryStringLiterals = Group "binary string literals"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [--("B'101010'", CSStringLit "B" "101010")
+     ("X'7f7f7f'", StringLit "X'" "'" "7f7f7f")
+    --,("X'7f7f7f' escape z", Escape (StringLit "X'" "'" "7f7f7f") 'z')
+    ]
+
+{-
+<signed numeric literal> ::= [ <sign> ] <unsigned numeric literal>
+
+<unsigned numeric literal> ::=
+    <exact numeric literal>
+  | <approximate numeric literal>
+
+<exact numeric literal> ::=
+    <unsigned integer> [ <period> [ <unsigned integer> ] ]
+  | <period> <unsigned integer>
+
+<sign> ::= <plus sign> | <minus sign>
+
+<approximate numeric literal> ::= <mantissa> E <exponent>
+
+<mantissa> ::= <exact numeric literal>
+
+<exponent> ::= <signed integer>
+
+<signed integer> ::= [ <sign> ] <unsigned integer>
+
+<unsigned integer> ::= <digit>...
+-}
+
+numericLiterals :: TestItem
+numericLiterals = Group "numeric literals"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("11", NumLit "11")
+    ,("11.11", NumLit "11.11")
+
+    ,("11E23", NumLit "11E23")
+    ,("11E+23", NumLit "11E+23")
+    ,("11E-23", NumLit "11E-23")
+
+    ,("11.11E23", NumLit "11.11E23")
+    ,("11.11E+23", NumLit "11.11E+23")
+    ,("11.11E-23", NumLit "11.11E-23")
+
+    ,("+11E23", PrefixOp [Name Nothing "+"] $ NumLit "11E23")
+    ,("+11E+23", PrefixOp [Name Nothing "+"] $ NumLit "11E+23")
+    ,("+11E-23", PrefixOp [Name Nothing "+"] $ NumLit "11E-23")
+    ,("+11.11E23", PrefixOp [Name Nothing "+"] $ NumLit "11.11E23")
+    ,("+11.11E+23", PrefixOp [Name Nothing "+"] $ NumLit "11.11E+23")
+    ,("+11.11E-23", PrefixOp [Name Nothing "+"] $ NumLit "11.11E-23")
+
+    ,("-11E23", PrefixOp [Name Nothing "-"] $ NumLit "11E23")
+    ,("-11E+23", PrefixOp [Name Nothing "-"] $ NumLit "11E+23")
+    ,("-11E-23", PrefixOp [Name Nothing "-"] $ NumLit "11E-23")
+    ,("-11.11E23", PrefixOp [Name Nothing "-"] $ NumLit "11.11E23")
+    ,("-11.11E+23", PrefixOp [Name Nothing "-"] $ NumLit "11.11E+23")
+    ,("-11.11E-23", PrefixOp [Name Nothing "-"] $ NumLit "11.11E-23")
+
+    ,("11.11e23", NumLit "11.11e23")
+
+    ]
+
+{-
+<datetime literal> ::= <date literal> | <time literal> | <timestamp literal>
+
+<date literal> ::= DATE <date string>
+
+<time literal> ::= TIME <time string>
+
+<timestamp literal> ::= TIMESTAMP <timestamp string>
+
+<date string> ::= <quote> <unquoted date string> <quote>
+
+<time string> ::= <quote> <unquoted time string> <quote>
+
+<timestamp string> ::= <quote> <unquoted timestamp string> <quote>
+
+<time zone interval> ::= <sign> <hours value> <colon> <minutes value>
+
+<date value> ::=
+  <years value> <minus sign> <months value> <minus sign> <days value>
+
+<time value> ::= <hours value> <colon> <minutes value> <colon> <seconds value>
+-}
+
+dateTimeLiterals :: TestItem
+dateTimeLiterals = Group "datetime literals"
+    [-- TODO: datetime literals
+    ]
+
+{-
+<interval literal> ::=
+  INTERVAL [ <sign> ] <interval string> <interval qualifier>
+
+<interval string> ::= <quote> <unquoted interval string> <quote>
+
+<unquoted date string> ::= <date value>
+
+<unquoted time string> ::= <time value> [ <time zone interval> ]
+
+<unquoted timestamp string> ::=
+  <unquoted date string> <space> <unquoted time string>
+
+<unquoted interval string> ::=
+  [ <sign> ] { <year-month literal> | <day-time literal> }
+
+<year-month literal> ::=
+    <years value> [ <minus sign> <months value> ]
+  | <months value>
+
+<day-time literal> ::= <day-time interval> | <time interval>
+
+<day-time interval> ::=
+  <days value> [ <space> <hours value> [ <colon> <minutes value>
+      [ <colon> <seconds value> ] ] ]
+
+<time interval> ::=
+    <hours value> [ <colon> <minutes value> [ <colon> <seconds value> ] ]
+  | <minutes value> [ <colon> <seconds value> ]
+  | <seconds value>
+
+<years value> ::= <datetime value>
+
+<months value> ::= <datetime value>
+
+<days value> ::= <datetime value>
+
+<hours value> ::= <datetime value>
+
+<minutes value> ::= <datetime value>
+
+<seconds value> ::= <seconds integer value> [ <period> [ <seconds fraction> ] ]
+
+<seconds integer value> ::= <unsigned integer>
+
+<seconds fraction> ::= <unsigned integer>
+
+<datetime value> ::= <unsigned integer>
+-}
+
+intervalLiterals :: TestItem
+intervalLiterals = Group "intervalLiterals literals"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("interval '1'", TypedLit (TypeName [Name Nothing "interval"]) "1")
+    ,("interval '1' day"
+     ,IntervalLit Nothing "1" (Itf "day" Nothing) Nothing)
+    ,("interval '1' day(3)"
+     ,IntervalLit Nothing "1" (Itf "day" $ Just (3,Nothing)) Nothing)
+    ,("interval + '1' day(3)"
+     ,IntervalLit (Just Plus) "1" (Itf "day" $ Just (3,Nothing)) Nothing)
+    ,("interval - '1' second(2,2)"
+     ,IntervalLit (Just Minus) "1" (Itf "second" $ Just (2,Just 2)) Nothing)
+    ,("interval '1' year to month"
+     ,IntervalLit Nothing "1" (Itf "year" Nothing)
+                                  (Just $ Itf "month" Nothing))
+
+    ,("interval '1' year(4) to second(2,3) "
+     ,IntervalLit Nothing "1" (Itf "year" $ Just (4,Nothing))
+                            (Just $ Itf "second" $ Just (2, Just 3)))
+    ]
+
+-- <boolean literal> ::= TRUE | FALSE | UNKNOWN
+
+booleanLiterals :: TestItem
+booleanLiterals = Group "boolean literals"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("true", Iden [Name Nothing "true"])
+    ,("false", Iden [Name Nothing "false"])
+    ,("unknown", Iden [Name Nothing "unknown"])
+    ]
+
+{-
+== 5.4 Names and identifiers
+
+Function
+Specify names.
+
+<identifier> ::= <actual identifier>
+
+<actual identifier> ::=
+    <regular identifier>
+  | <delimited identifier>
+  | <Unicode delimited identifier>
+-}
+
+identifiers :: TestItem
+identifiers = Group "identifiers"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("test",Iden [Name Nothing "test"])
+    ,("_test",Iden [Name Nothing "_test"])
+    ,("t1",Iden [Name Nothing "t1"])
+    ,("a.b",Iden [Name Nothing "a", Name Nothing "b"])
+    ,("a.b.c",Iden [Name Nothing "a", Name Nothing "b", Name Nothing "c"])
+    ,("\"quoted iden\"", Iden [Name (Just ("\"","\"")) "quoted iden"])
+    ,("\"quoted \"\" iden\"", Iden [Name (Just ("\"","\"")) "quoted \"\" iden"])
+    ,("U&\"quoted iden\"", Iden [Name (Just ("U&\"","\"")) "quoted iden"])
+    ,("U&\"quoted \"\" iden\"", Iden [Name (Just ("U&\"","\"")) "quoted \"\" iden"])
+    ]
+
+{-
+TODO: more identifiers, e.g. unicode escapes?, mixed quoted/unquoted
+chains
+
+TODO: review below stuff for exact rules
+
+<SQL language identifier> ::=
+  <SQL language identifier start> [ <SQL language identifier part>... ]
+
+<SQL language identifier start> ::= <simple Latin letter>
+
+<SQL language identifier part> ::=
+    <simple Latin letter>
+  | <digit>
+  | <underscore>
+
+<authorization identifier> ::= <role name> | <user identifier>
+
+<table name> ::= <local or schema qualified name>
+
+<domain name> ::= <schema qualified name>
+
+<schema name> ::= [ <catalog name> <period> ] <unqualified schema name>
+
+<unqualified schema name> ::= <identifier>
+
+<catalog name> ::= <identifier>
+
+<schema qualified name> ::= [ <schema name> <period> ] <qualified identifier>
+
+<local or schema qualified name> ::=
+  [ <local or schema qualifier> <period> ] <qualified identifier>
+
+<local or schema qualifier> ::= <schema name> | <local qualifier>
+
+<qualified identifier> ::= <identifier>
+
+<column name> ::= <identifier>
+
+<correlation name> ::= <identifier>
+
+<query name> ::= <identifier>
+
+<SQL-client module name> ::= <identifier>
+
+<procedure name> ::= <identifier>
+
+<schema qualified routine name> ::= <schema qualified name>
+
+<method name> ::= <identifier>
+
+<specific name> ::= <schema qualified name>
+
+<cursor name> ::= <local qualified name>
+
+<local qualified name> ::=
+  [ <local qualifier> <period> ] <qualified identifier>
+
+<local qualifier> ::= MODULE
+
+<host parameter name> ::= <colon> <identifier>
+
+<SQL parameter name> ::= <identifier>
+
+<constraint name> ::= <schema qualified name>
+
+<external routine name> ::= <identifier> | <character string literal>
+
+<trigger name> ::= <schema qualified name>
+
+<collation name> ::= <schema qualified name>
+
+<character set name> ::= [ <schema name> <period> ] <SQL language identifier>
+
+<transliteration name> ::= <schema qualified name>
+
+<transcoding name> ::= <schema qualified name>
+
+<schema-resolved user-defined type name> ::= <user-defined type name>
+
+<user-defined type name> ::= [ <schema name> <period> ] <qualified identifier>
+
+<attribute name> ::= <identifier>
+
+<field name> ::= <identifier>
+
+<savepoint name> ::= <identifier>
+
+<sequence generator name> ::= <schema qualified name>
+
+<role name> ::= <identifier>
+
+<user identifier> ::= <identifier>
+
+<connection name> ::= <simple value specification>
+
+<SQL-server name> ::= <simple value specification>
+
+<connection user name> ::= <simple value specification>
+
+<SQL statement name> ::= <statement name> | <extended statement name>
+
+<statement name> ::= <identifier>
+
+<extended statement name> ::= [ <scope option> ] <simple value specification>
+
+<dynamic cursor name> ::= <cursor name> | <extended cursor name>
+
+<extended cursor name> ::= [ <scope option> ] <simple value specification>
+
+<descriptor name> ::=
+    <non-extended descriptor name>
+  | <extended descriptor name>
+
+<non-extended descriptor name> ::= <identifier>
+
+<extended descriptor name> ::= [ <scope option> ] <simple value specification>
+
+<scope option> ::= GLOBAL | LOCAL
+
+<window name> ::= <identifier>
+
+= 6 Scalar expressions
+
+== 6.1 <data type>
+
+Function
+Specify a data type.
+
+<data type> ::=
+    <predefined type>
+  | <row type>
+  | <path-resolved user-defined type name>
+  | <reference type>
+  | <collection type>
+
+<predefined type> ::=
+    <character string type> [ CHARACTER SET <character set specification> ]
+          [ <collate clause> ]
+  | <national character string type> [ <collate clause> ]
+  | <binary string type>
+  | <numeric type>
+  | <boolean type>
+  | <datetime type>
+  | <interval type>
+
+<character string type> ::=
+    CHARACTER [ <left paren> <character length> <right paren> ]
+  | CHAR [ <left paren> <character length> <right paren> ]
+  | CHARACTER VARYING <left paren> <character length> <right paren>
+  | CHAR VARYING <left paren> <character length> <right paren>
+  | VARCHAR <left paren> <character length> <right paren>
+  | <character large object type>
+
+<character large object type> ::=
+    CHARACTER LARGE OBJECT [ <left paren> <character large object length> <right paren> ]
+  | CHAR LARGE OBJECT [ <left paren> <character large object length> <right paren> ]
+  | CLOB [ <left paren> <character large object length> <right paren> ]
+
+<national character string type> ::=
+    NATIONAL CHARACTER [ <left paren> <character length> <right paren> ]
+  | NATIONAL CHAR [ <left paren> <character length> <right paren> ]
+  | NCHAR [ <left paren> <character length> <right paren> ]
+  | NATIONAL CHARACTER VARYING <left paren> <character length> <right paren>
+  | NATIONAL CHAR VARYING <left paren> <character length> <right paren>
+  | NCHAR VARYING <left paren> <character length> <right paren>
+  | <national character large object type>
+
+<national character large object type> ::=
+    NATIONAL CHARACTER LARGE OBJECT [ <left paren> <character large object length> <right
+    paren> ]
+  | NCHAR LARGE OBJECT [ <left paren> <character large object length> <right paren> ]
+  | NCLOB [ <left paren> <character large object length> <right paren> ]
+
+<binary string type> ::=
+    BINARY [ <left paren> <length> <right paren> ]
+  | BINARY VARYING <left paren> <length> <right paren>
+  | VARBINARY <left paren> <length> <right paren>
+  | <binary large object string type>
+
+<binary large object string type> ::=
+    BINARY LARGE OBJECT [ <left paren> <large object length> <right paren> ]
+  | BLOB [ <left paren> <large object length> <right paren> ]
+
+<numeric type> ::= <exact numeric type> | <approximate numeric type>
+
+<exact numeric type> ::=
+    NUMERIC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+  | DECIMAL [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+  | DEC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+  | SMALLINT
+  | INTEGER
+  | INT
+  | BIGINT
+
+<approximate numeric type> ::=
+    FLOAT [ <left paren> <precision> <right paren> ]
+  | REAL
+  | DOUBLE PRECISION
+
+<length> ::= <unsigned integer>
+
+<character length> ::= <length> [ <char length units> ]
+
+<large object length> ::=
+    <length> [ <multiplier> ]
+  | <large object length token>
+
+<character large object length> ::=
+  <large object length> [ <char length units> ]
+
+<char length units> ::= CHARACTERS | OCTETS
+
+<precision> ::= <unsigned integer>
+
+<scale> ::= <unsigned integer>
+
+<boolean type> ::= BOOLEAN
+
+<datetime type> ::=
+    DATE
+  | TIME [ <left paren> <time precision> <right paren> ] [ <with or without time zone> ]
+  | TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
+      [ <with or without time zone> ]
+
+<with or without time zone> ::= WITH TIME ZONE | WITHOUT TIME ZONE
+
+<time precision> ::= <time fractional seconds precision>
+
+<timestamp precision> ::= <time fractional seconds precision>
+
+<time fractional seconds precision> ::= <unsigned integer>
+
+<interval type> ::= INTERVAL <interval qualifier>
+
+<row type> ::= ROW <row type body>
+
+<row type body> ::=
+  <left paren> <field definition> [ { <comma> <field definition> }... ] <right paren>
+
+<reference type> ::=
+  REF <left paren> <referenced type> <right paren> [ <scope clause> ]
+
+<scope clause> ::= SCOPE <table name>
+
+<referenced type> ::= <path-resolved user-defined type name>
+
+<path-resolved user-defined type name> ::= <user-defined type name>
+
+<collection type> ::= <array type> | <multiset type>
+
+<array type> ::=
+  <data type> ARRAY
+      [ <left bracket or trigraph> <maximum cardinality> <right bracket or trigraph> ]
+
+<maximum cardinality> ::= <unsigned integer>
+
+<multiset type> ::= <data type> MULTISET
+
+TODO: below, add new stuff:
+review the length syntaxes
+binary, binary varying/varbinary
+new multipliers
+
+create a list of type name variations:
+-}
+
+typeNames :: ([(Text,TypeName)],[(Text,TypeName)])
+typeNames =
+    (basicTypes, concatMap makeArray basicTypes
+                 <> map makeMultiset basicTypes)
+  where
+    makeArray (s,t) = [(s <> " array", ArrayTypeName t Nothing)
+                      ,(s <> " array[5]", ArrayTypeName t (Just 5))]
+    makeMultiset (s,t) = (s <> " multiset", MultisetTypeName t)
+    basicTypes :: [(Text, TypeName)]
+    basicTypes =
+        -- example of every standard type name
+        map (\t -> (t,TypeName [Name Nothing t]))
+        ["binary"
+        ,"binary varying"
+        ,"character"
+        ,"char"
+        ,"character varying"
+        ,"char varying"
+        ,"varbinary"
+        ,"varchar"
+        ,"character large object"
+        ,"char large object"
+        ,"clob"
+        ,"national character"
+        ,"national char"
+        ,"nchar"
+        ,"national character varying"
+        ,"national char varying"
+        ,"nchar varying"
+        ,"national character large object"
+        ,"nchar large object"
+        ,"nclob"
+        ,"binary large object"
+        ,"blob"
+        ,"numeric"
+        ,"decimal"
+        ,"dec"
+        ,"smallint"
+        ,"integer"
+        ,"int"
+        ,"bigint"
+        ,"float"
+        ,"real"
+        ,"double precision"
+        ,"boolean"
+        ,"date"
+        ,"time"
+        ,"timestamp"]
+        --interval -- not allowed without interval qualifier
+        --row -- not allowed without row type body
+        -- array -- not allowed on own
+        -- multiset -- not allowed on own
+
+         <>
+         [-- 1 single prec + 1 with multiname
+          ("char(5)", PrecTypeName [Name Nothing "char"] 5)
+         ,("char varying(5)", PrecTypeName [Name Nothing "char varying"] 5)
+         -- 1 scale
+         ,("decimal(15,2)", PrecScaleTypeName [Name Nothing "decimal"] 15 2)
+         ,("char(3 octets)"
+          ,PrecLengthTypeName [Name Nothing "char"] 3 Nothing (Just PrecOctets))
+         ,("varchar(50 characters)"
+          ,PrecLengthTypeName [Name Nothing "varchar"] 50 Nothing (Just PrecCharacters))
+         -- lob prec + with multiname
+         ,("blob(3M)", PrecLengthTypeName [Name Nothing "blob"] 3 (Just PrecM) Nothing)
+         ,("blob(3T)", PrecLengthTypeName [Name Nothing "blob"] 3 (Just PrecT) Nothing)
+         ,("blob(3P)", PrecLengthTypeName [Name Nothing "blob"] 3 (Just PrecP) Nothing)
+         ,("blob(4M characters) "
+          ,PrecLengthTypeName [Name Nothing "blob"] 4 (Just PrecM) (Just PrecCharacters))
+         ,("blob(6G octets) "
+          ,PrecLengthTypeName [Name Nothing "blob"] 6 (Just PrecG) (Just PrecOctets))
+         ,("national character large object(7K) "
+          ,PrecLengthTypeName [Name Nothing "national character large object"]
+               7 (Just PrecK) Nothing)
+         -- 1 with and without tz
+         ,("time with time zone"
+          ,TimeTypeName [Name Nothing "time"] Nothing True)
+         ,("datetime(3) without time zone"
+          ,TimeTypeName [Name Nothing "datetime"] (Just 3) False)
+         -- chars: (single/multiname) x prec x charset x collate
+         -- 1111
+         ,("char varying(5) character set something collate something_insensitive"
+          ,CharTypeName [Name Nothing "char varying"] (Just 5)
+           [Name Nothing "something"] [Name Nothing "something_insensitive"])
+          -- 0111
+         ,("char(5) character set something collate something_insensitive"
+          ,CharTypeName [Name Nothing "char"] (Just 5)
+           [Name Nothing "something"] [Name Nothing "something_insensitive"])
+
+          -- 1011
+         ,("char varying character set something collate something_insensitive"
+          ,CharTypeName [Name Nothing "char varying"] Nothing
+           [Name Nothing "something"] [Name Nothing "something_insensitive"])
+          -- 0011
+         ,("char character set something collate something_insensitive"
+          ,CharTypeName [Name Nothing "char"] Nothing
+           [Name Nothing "something"] [Name Nothing "something_insensitive"])
+
+          -- 1101
+         ,("char varying(5) collate something_insensitive"
+          ,CharTypeName [Name Nothing "char varying"] (Just 5)
+           [] [Name Nothing "something_insensitive"])
+          -- 0101
+         ,("char(5) collate something_insensitive"
+          ,CharTypeName [Name Nothing "char"] (Just 5)
+           [] [Name Nothing "something_insensitive"])
+          -- 1001
+         ,("char varying collate something_insensitive"
+          ,CharTypeName [Name Nothing "char varying"] Nothing
+           [] [Name Nothing "something_insensitive"])
+          -- 0001
+         ,("char collate something_insensitive"
+          ,CharTypeName [Name Nothing "char"] Nothing
+           [] [Name Nothing "something_insensitive"])
+
+         -- 1110
+         ,("char varying(5) character set something"
+          ,CharTypeName [Name Nothing "char varying"] (Just 5)
+           [Name Nothing "something"] [])
+          -- 0110
+         ,("char(5) character set something"
+          ,CharTypeName [Name Nothing "char"] (Just 5)
+           [Name Nothing "something"] [])
+          -- 1010
+         ,("char varying character set something"
+          ,CharTypeName [Name Nothing "char varying"] Nothing
+           [Name Nothing "something"] [])
+          -- 0010
+         ,("char character set something"
+          ,CharTypeName [Name Nothing "char"] Nothing
+           [Name Nothing "something"] [])
+          -- 1100
+         ,("char varying character set something"
+          ,CharTypeName [Name Nothing "char varying"] Nothing
+           [Name Nothing "something"] [])
+
+         -- single row field, two row field
+         ,("row(a int)", RowTypeName [(Name Nothing "a", TypeName [Name Nothing "int"])])
+         ,("row(a int,b char)"
+          ,RowTypeName [(Name Nothing "a", TypeName [Name Nothing "int"])
+                       ,(Name Nothing "b", TypeName [Name Nothing "char"])])
+         -- interval each type raw
+         ,("interval year"
+          ,IntervalTypeName (Itf "year" Nothing) Nothing)
+         -- one type with single suffix
+         -- one type with double suffix
+         ,("interval year(2)"
+          ,IntervalTypeName (Itf "year" $ Just (2,Nothing)) Nothing)
+         ,("interval second(2,5)"
+          ,IntervalTypeName (Itf "second" $ Just (2,Just 5)) Nothing)
+         -- a to b with raw
+         -- a to b with single suffix
+         ,("interval year to month"
+          ,IntervalTypeName (Itf "year" Nothing)
+                            (Just $ Itf "month" Nothing))
+         ,("interval year(4) to second(2,3)"
+          ,IntervalTypeName (Itf "year" $ Just (4,Nothing))
+                            (Just $ Itf "second" $ Just (2, Just 3)))
+         ]
+
+{-
+Now test each variation in both cast expression and typed literal
+expression
+-}
+
+typeNameTests :: TestItem
+typeNameTests = Group "type names"
+    [Group "type names" $ map (uncurry (TestScalarExpr ansi2011))
+                          $ concatMap makeSimpleTests $ fst typeNames
+    ,Group "generated casts" $ map (uncurry (TestScalarExpr ansi2011))
+                          $ concatMap makeCastTests $ fst typeNames
+    ,Group "generated typename" $ map (uncurry (TestScalarExpr ansi2011))
+                          $ concatMap makeTests $ snd typeNames]
+  where
+    makeSimpleTests (ctn, stn) =
+        [(ctn <> " 'test'", TypedLit stn "test")
+        ]
+    makeCastTests (ctn, stn) =
+        [("cast('test' as " <> ctn <> ")", Cast (StringLit "'" "'" "test") stn)
+        ]
+    makeTests a = makeSimpleTests a <> makeCastTests a
+
+
+{-
+== 6.2 <field definition>
+
+Function
+Define a field of a row type.
+
+<field definition> ::= <field name> <data type>
+-}
+
+fieldDefinition :: TestItem
+fieldDefinition = Group "field definition"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("cast('(1,2)' as row(a int,b char))"
+     ,Cast (StringLit "'" "'" "(1,2)")
+     $ RowTypeName [(Name Nothing "a", TypeName [Name Nothing "int"])
+                   ,(Name Nothing "b", TypeName [Name Nothing "char"])])]
+
+{-
+== 6.3 <value expression primary>
+
+Function
+Specify a value that is syntactically self-delimited.
+
+<value expression primary> ::=
+    <parenthesized value expression>
+  | <nonparenthesized value expression primary>
+
+<parenthesized value expression> ::=
+  <left paren> <value expression> <right paren>
+
+<nonparenthesized value expression primary> ::=
+       <unsigned value specification>
+     | <column reference>
+     | <set function specification>
+     | <window function>
+     | <nested window function>
+     | <scalar subquery>
+     | <case expression>
+     | <cast specification>
+     | <field reference>
+     | <subtype treatment>
+     | <method invocation>
+     | <static method invocation>
+     | <new specification>
+     | <attribute or method reference>
+     | <reference resolution>
+     | <collection value constructor>
+     | <array element reference>
+     | <multiset element reference>
+     | <next value expression>
+     | <routine invocation>
+
+<collection value constructor> ::=
+    <array value constructor>
+  | <multiset value constructor>
+-}
+
+valueExpressions :: TestItem
+valueExpressions = Group "value expressions"
+    [generalValueSpecification
+    ,parameterSpecification
+    ,contextuallyTypedValueSpecification
+    ,identifierChain
+    ,columnReference
+    ,setFunctionSpecification
+    ,windowFunction
+    ,nestedWindowFunction
+    ,caseExpression
+    ,castSpecification
+    ,nextScalarExpression
+    ,fieldReference
+    ,arrayElementReference
+    ,multisetElementReference
+    ,numericScalarExpression
+    ,numericValueFunction
+    ,stringScalarExpression
+    ,stringValueFunction
+    ,datetimeScalarExpression
+    ,datetimeValueFunction
+    ,intervalScalarExpression
+    ,intervalValueFunction
+    ,booleanScalarExpression
+    ,arrayScalarExpression
+    ,arrayValueFunction
+    ,arrayValueConstructor
+    ,multisetScalarExpression
+    ,multisetValueFunction
+    ,multisetValueConstructor
+    ,parenthesizedScalarExpression
+    ]
+
+parenthesizedScalarExpression :: TestItem
+parenthesizedScalarExpression = Group "parenthesized value expression"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("(3)", Parens (NumLit "3"))
+    ,("((3))", Parens $ Parens (NumLit "3"))
+    ]
+
+{-
+== 6.4 <value specification> and <target specification>
+
+Function
+Specify one or more values, host parameters, SQL parameters, dynamic parameters, or host variables.
+
+<value specification> ::= <literal> | <general value specification>
+
+<unsigned value specification> ::=
+    <unsigned literal>
+  | <general value specification>
+
+ <general value specification> ::=
+    <host parameter specification>
+  | <SQL parameter reference>
+  | <dynamic parameter specification>
+  | <embedded variable specification>
+  | <current collation specification>
+  | CURRENT_CATALOG
+  | CURRENT_DEFAULT_TRANSFORM_GROUP
+  | CURRENT_PATH
+  | CURRENT_ROLE
+  | CURRENT_SCHEMA
+  | CURRENT_TRANSFORM_GROUP_FOR_TYPE <path-resolved user-defined type name>
+  | CURRENT_USER
+  | SESSION_USER
+  | SYSTEM_USER
+  | USER
+  | VALUE
+-}
+
+generalValueSpecification :: TestItem
+generalValueSpecification = Group "general value specification"
+    $ map (uncurry (TestScalarExpr ansi2011)) $
+    map mkIden ["CURRENT_DEFAULT_TRANSFORM_GROUP"
+               ,"CURRENT_PATH"
+               ,"CURRENT_ROLE"
+               ,"CURRENT_USER"
+               ,"SESSION_USER"
+               ,"SYSTEM_USER"
+               ,"USER"
+               ,"VALUE"]
+  where
+    mkIden nm = (nm,Iden [Name Nothing nm])
+
+{-
+TODO: add the missing bits
+
+<simple value specification> ::=
+    <literal>
+  | <host parameter name>
+  | <SQL parameter reference>
+  | <embedded variable name>
+
+<target specification> ::=
+    <host parameter specification>
+  | <SQL parameter reference>
+  | <column reference>
+  | <target array element specification>
+  | <dynamic parameter specification>
+  | <embedded variable specification>
+
+<simple target specification> ::=
+    <host parameter name>
+  | <SQL parameter reference>
+  | <column reference>
+  | <embedded variable name>
+
+<host parameter specification> ::=
+  <host parameter name> [ <indicator parameter> ]
+
+<dynamic parameter specification> ::= <question mark>
+
+<embedded variable specification> ::=
+  <embedded variable name> [ <indicator variable> ]
+
+<indicator variable> ::= [ INDICATOR ] <embedded variable name>
+
+<indicator parameter> ::= [ INDICATOR ] <host parameter name>
+
+<target array element specification> ::=
+  <target array reference>
+      <left bracket or trigraph> <simple value specification> <right bracket or trigraph>
+
+<target array reference> ::= <SQL parameter reference> | <column reference>
+-}
+
+parameterSpecification :: TestItem
+parameterSpecification = Group "parameter specification"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [(":hostparam", HostParameter ":hostparam" Nothing)
+    ,(":hostparam indicator :another_host_param"
+     ,HostParameter ":hostparam" $ Just ":another_host_param")
+    ,("?", Parameter)
+    ,(":h[3]", Array (HostParameter ":h" Nothing) [NumLit "3"])
+    ]
+
+{-
+<current collation specification> ::=
+  COLLATION FOR <left paren> <string value expression> <right paren>
+
+TODO: review the modules stuff
+
+== 6.5 <contextually typed value specification>
+
+Function
+Specify a value whose data type is to be inferred from its context.
+
+<contextually typed value specification> ::=
+    <implicitly typed value specification>
+  | <default specification>
+
+<implicitly typed value specification> ::=
+    <null specification>
+  | <empty specification>
+
+<null specification> ::= NULL
+
+<empty specification> ::=
+    ARRAY <left bracket or trigraph> <right bracket or trigraph>
+  | MULTISET <left bracket or trigraph> <right bracket or trigraph>
+
+<default specification> ::= DEFAULT
+-}
+
+contextuallyTypedValueSpecification :: TestItem
+contextuallyTypedValueSpecification =
+    Group "contextually typed value specification"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("null", Iden [Name Nothing "null"])
+    ,("array[]", Array (Iden [Name Nothing "array"]) [])
+    ,("multiset[]", MultisetCtor [])
+    ,("default", Iden [Name Nothing "default"])
+    ]
+
+{-
+== 6.6 <identifier chain>
+
+Function
+Disambiguate a <period>-separated chain of identifiers.
+
+<identifier chain> ::= <identifier> [ { <period> <identifier> }... ]
+
+<basic identifier chain> ::= <identifier chain>
+-}
+
+identifierChain :: TestItem
+identifierChain = Group "identifier chain"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("a.b", Iden [Name Nothing "a",Name Nothing "b"])]
+
+{-
+== 6.7 <column reference>
+
+Function
+Reference a column.
+
+<column reference> ::=
+    <basic identifier chain>
+  | MODULE <period> <qualified identifier> <period> <column name>
+-}
+
+columnReference :: TestItem
+columnReference = Group "column reference"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("module.a.b", Iden [Name Nothing "module",Name Nothing "a",Name Nothing "b"])]
+
+{-
+== 6.8 <SQL parameter reference>
+
+Function
+Reference an SQL parameter.
+
+<SQL parameter reference> ::= <basic identifier chain>
+
+== 6.9 <set function specification>
+
+Function
+Specify a value derived by the application of a function to an argument.
+
+<set function specification> ::= <aggregate function> | <grouping operation>
+
+<grouping operation> ::=
+  GROUPING <left paren> <column reference>
+      [ { <comma> <column reference> }... ] <right paren>
+-}
+
+setFunctionSpecification :: TestItem
+setFunctionSpecification = Group "set function specification"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("SELECT SalesQuota, SUM(SalesYTD) TotalSalesYTD,\n\
+      \   GROUPING(SalesQuota) AS Grouping\n\
+      \FROM Sales.SalesPerson\n\
+      \GROUP BY ROLLUP(SalesQuota);"
+     ,toQueryExpr $ makeSelect
+      {msSelectList = [(Iden [Name Nothing "SalesQuota"],Nothing)
+                      ,(App [Name Nothing "SUM"] [Iden [Name Nothing "SalesYTD"]]
+                       ,Just (Name Nothing "TotalSalesYTD"))
+                      ,(App [Name Nothing "GROUPING"] [Iden [Name Nothing "SalesQuota"]]
+                       ,Just (Name Nothing "Grouping"))]
+      ,msFrom = [TRSimple [Name Nothing "Sales",Name Nothing "SalesPerson"]]
+      ,msGroupBy = [Rollup [SimpleGroup (Iden [Name Nothing "SalesQuota"])]]})
+    ]
+
+{-
+== 6.10 <window function>
+
+Function
+Specify a window function.
+
+<window function> ::=
+  <window function type> OVER <window name or specification>
+
+<window function type> ::=
+    <rank function type> <left paren> <right paren>
+  | ROW_NUMBER <left paren> <right paren>
+  | <aggregate function>
+  | <ntile function>
+  | <lead or lag function>
+  | <first or last value function>
+  | <nth value function>
+
+<rank function type> ::= RANK | DENSE_RANK | PERCENT_RANK | CUME_DIST
+
+<ntile function> ::= NTILE <left paren> <number of tiles> <right paren>
+
+<number of tiles> ::=
+    <simple value specification>
+  | <dynamic parameter specification>
+
+<lead or lag function> ::=
+  <lead or lag> <left paren> <lead or lag extent>
+      [ <comma> <offset> [ <comma> <default expression> ] ] <right paren>
+      [ <null treatment> ]
+
+<lead or lag> ::= LEAD | LAG
+
+<lead or lag extent> ::= <value expression>
+
+<offset> ::= <exact numeric literal>
+
+<default expression> ::= <value expression>
+
+<null treatment> ::= RESPECT NULLS | IGNORE NULLS
+
+<first or last value function> ::=
+  <first or last value> <left paren> <value expression> <right paren> [ <null treatment>
+  ]
+
+<first or last value> ::= FIRST_VALUE | LAST_VALUE
+
+<nth value function> ::=
+  NTH_VALUE <left paren> <value expression> <comma> <nth row> <right paren>
+      [ <from first or last> ] [ <null treatment> ]
+
+<nth row> ::= <simple value specification> | <dynamic parameter specification>
+
+<from first or last> ::= FROM FIRST | FROM LAST
+
+<window name or specification> ::=
+    <window name>
+  | <in-line window specification>
+
+<in-line window specification> ::= <window specification>
+-}
+
+windowFunction :: TestItem
+windowFunction = Group "window function"
+    [-- todo: window function
+    ]
+
+{-
+== 6.11 <nested window function>
+
+Function
+
+Specify a function nested in an aggregated argument of an
+<aggregate function> simply contained in a <window function>.
+
+<nested window function> ::=
+    <nested row number function>
+  | <value_of expression at row>
+
+<nested row number function> ::=
+  ROW_NUMBER <left paren> <row marker> <right paren>
+
+<value_of expression at row> ::=
+  VALUE_OF <left paren> <value expression> AT <row marker expression>
+      [ <comma> <value_of default value> ] <right paren>
+
+<row marker> ::=
+    BEGIN_PARTITION
+  | BEGIN_FRAME
+  | CURRENT_ROW
+  | FRAME_ROW
+  | END_FRAME
+  | END_PARTITION
+
+<row marker expression> ::= <row marker> [ <row marker delta> ]
+
+<row marker delta> ::=
+    <plus sign> <row marker offset>
+  | <minus sign> <row marker offset>
+
+<row marker offset> ::=
+    <simple value specification>
+  | <dynamic parameter specification>
+
+<value_of default value> ::= <value expression>
+-}
+
+nestedWindowFunction :: TestItem
+nestedWindowFunction = Group "nested window function"
+    [-- todo: nested window function
+    ]
+
+
+{-
+== 6.12 <case expression>
+
+Function
+Specify a conditional value.
+
+<case expression> ::= <case abbreviation> | <case specification>
+
+<case abbreviation> ::=
+    NULLIF <left paren> <value expression> <comma> <value expression> <right paren>
+  | COALESCE <left paren> <value expression>
+      { <comma> <value expression> }... <right paren>
+
+<case specification> ::= <simple case> | <searched case>
+
+<simple case> ::=
+  CASE <case operand> <simple when clause>... [ <else clause> ] END
+
+<searched case> ::= CASE <searched when clause>... [ <else clause> ] END
+
+<simple when clause> ::= WHEN <when operand list> THEN <result>
+
+<searched when clause> ::= WHEN <search condition> THEN <result>
+
+<else clause> ::= ELSE <result>
+
+<case operand> ::= <row value predicand> | <overlaps predicate part 1>
+
+<when operand list> ::= <when operand> [ { <comma> <when operand> }... ]
+
+<when operand> ::=
+    <row value predicand>
+  | <comparison predicate part 2>
+  | <between predicate part 2>
+  | <in predicate part 2>
+  | <character like predicate part 2>
+  | <octet like predicate part 2>
+  | <similar predicate part 2>
+  | <regex like predicate part 2>
+  | <null predicate part 2>
+  | <quantified comparison predicate part 2>
+  | <normalized predicate part 2>
+  | <match predicate part 2>
+  | <overlaps predicate part 2>
+  | <distinct predicate part 2>
+  | <member predicate part 2>
+  | <submultiset predicate part 2>
+  | <set predicate part 2>
+  | <type predicate part 2>
+
+I haven't seen these part 2 style when operands in the wild. It
+doesn't even allow all the binary operators here. We will allow them
+all, and parser and represent these expressions by considering all the
+binary ops as unary prefix ops.
+
+<result> ::= <result expression> | NULL
+
+<result expression> ::= <value expression>
+-}
+
+caseExpression :: TestItem
+caseExpression = Group "case expression"
+    [-- todo: case expression
+    ]
+
+{-
+== 6.13 <cast specification>
+
+Function
+Specify a data conversion.
+
+<cast specification> ::=
+     CAST <left paren> <cast operand> AS <cast target> <right paren>
+
+<cast operand> ::= <value expression> | <implicitly typed value specification>
+
+<cast target> ::= <domain name> | <data type>
+-}
+
+castSpecification :: TestItem
+castSpecification = Group "cast specification"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("cast(a as int)"
+     ,Cast (Iden [Name Nothing "a"]) (TypeName [Name Nothing "int"]))
+    ]
+
+{-
+== 6.14 <next value expression>
+
+Function
+Return the next value of a sequence generator.
+
+<next value expression> ::= NEXT VALUE FOR <sequence generator name>
+-}
+
+nextScalarExpression :: TestItem
+nextScalarExpression = Group "next value expression"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("next value for a.b", NextValueFor [Name Nothing "a", Name Nothing "b"])
+    ]
+
+{-
+== 6.15 <field reference>
+
+Function
+Reference a field of a row value.
+
+<field reference> ::= <value expression primary> <period> <field name>
+-}
+
+fieldReference :: TestItem
+fieldReference = Group "field reference"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("f(something).a"
+      ,BinOp (App [Name Nothing "f"] [Iden [Name Nothing "something"]])
+       [Name Nothing "."]
+       (Iden [Name Nothing "a"]))
+    ]
+
+{-
+TODO: try all possible value expression syntax variations followed by
+field reference
+
+== 6.16 <subtype treatment>
+
+Function
+Modify the declared type of an expression.
+
+<subtype treatment> ::=
+  TREAT <left paren> <subtype operand> AS <target subtype> <right paren>
+
+<subtype operand> ::= <value expression>
+
+<target subtype> ::= <path-resolved user-defined type name> | <reference type>
+
+todo: subtype treatment
+
+== 6.17 <method invocation>
+
+Function
+Reference an SQL-invoked method of a user-defined type value.
+
+<method invocation> ::= <direct invocation> | <generalized invocation>
+
+<direct invocation> ::=
+  <value expression primary> <period> <method name> [ <SQL argument list> ]
+
+<generalized invocation> ::=
+  <left paren> <value expression primary> AS <data type> <right paren>
+      <period> <method name> [ <SQL argument list> ]
+
+<method selection> ::= <routine invocation>
+
+<constructor method selection> ::= <routine invocation>
+
+todo: method invocation
+
+== 6.18 <static method invocation>
+
+Function
+Invoke a static method.
+
+<static method invocation> ::=
+  <path-resolved user-defined type name> <double colon> <method name>
+      [ <SQL argument list> ]
+
+<static method selection> ::= <routine invocation>
+
+todo: static method invocation
+
+== 6.19 <new specification>
+
+Function
+Invoke a method on a newly-constructed value of a structured type.
+
+<new specification> ::=
+  NEW <path-resolved user-defined type name> <SQL argument list>
+
+<new invocation> ::= <method invocation> | <routine invocation>
+
+todo: new specification
+
+== 6.20 <attribute or method reference>
+
+Function
+Return a value acquired by accessing a column of the row identified by
+a value of a reference type or by invoking an SQL-invoked method.
+
+<attribute or method reference> ::=
+  <value expression primary> <dereference operator> <qualified identifier>
+      [ <SQL argument list> ]
+
+<dereference operator> ::= <right arrow>
+
+todo: attribute of method reference
+
+== 6.21 <dereference operation>
+
+Function
+Access a column of the row identified by a value of a reference type.
+
+<dereference operation> ::=
+  <reference value expression> <dereference operator> <attribute name>
+
+todo: deference operation
+
+== 6.22 <method reference>
+
+Function
+Return a value acquired from invoking an SQL-invoked routine that is a method.
+
+<method reference> ::=
+  <value expression primary> <dereference operator> <method name> <SQL argument list>
+
+todo: method reference
+
+== 6.23 <reference resolution>
+
+Function
+Obtain the value referenced by a reference value.
+
+<reference resolution> ::=
+  DEREF <left paren> <reference value expression> <right paren>
+
+todo: reference resolution
+
+== 6.24 <array element reference>
+
+Function
+Return an element of an array.
+
+<array element reference> ::=
+  <array value expression>
+      <left bracket or trigraph> <numeric value expression> <right bracket or trigraph>
+-}
+
+arrayElementReference :: TestItem
+arrayElementReference = Group "array element reference"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("something[3]"
+     ,Array (Iden [Name Nothing "something"]) [NumLit "3"])
+    ,("(something(a))[x]"
+      ,Array (Parens (App [Name Nothing "something"] [Iden [Name Nothing "a"]]))
+        [Iden [Name Nothing "x"]])
+    ,("(something(a))[x][y] "
+      ,Array (
+        Array (Parens (App [Name Nothing "something"] [Iden [Name Nothing "a"]]))
+        [Iden [Name Nothing "x"]])
+        [Iden [Name Nothing "y"]])
+    ]
+
+{-
+== 6.25 <multiset element reference>
+
+Function
+Return the sole element of a multiset of one element.
+
+<multiset element reference> ::=
+  ELEMENT <left paren> <multiset value expression> <right paren>
+-}
+
+multisetElementReference :: TestItem
+multisetElementReference = Group "multisetElementReference"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("element(something)"
+     ,App [Name Nothing "element"] [Iden [Name Nothing "something"]])
+    ]
+
+{-
+== 6.26 <value expression>
+
+Function
+Specify a value.
+
+<value expression> ::=
+    <common value expression>
+  | <boolean value expression>
+  | <row value expression>
+
+<common value expression> ::=
+       <numeric value expression>
+     | <string value expression>
+     | <datetime value expression>
+     | <interval value expression>
+     | <user-defined type value expression>
+     | <reference value expression>
+     | <collection value expression>
+
+<user-defined type value expression> ::= <value expression primary>
+
+<reference value expression> ::= <value expression primary>
+
+<collection value expression> ::=
+    <array value expression>
+  | <multiset value expression>
+
+== 6.27 <numeric value expression>
+
+Function
+Specify a numeric value.
+
+<numeric value expression> ::=
+    <term>
+  | <numeric value expression> <plus sign> <term>
+  | <numeric value expression> <minus sign> <term>
+
+<term> ::= <factor> | <term> <asterisk> <factor> | <term> <solidus> <factor>
+
+<factor> ::= [ <sign> ] <numeric primary>
+
+<numeric primary> ::= <value expression primary> | <numeric value function>
+-}
+
+numericScalarExpression :: TestItem
+numericScalarExpression = Group "numeric value expression"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("a + b", binOp "+")
+    ,("a - b", binOp "-")
+    ,("a * b", binOp "*")
+    ,("a / b", binOp "/")
+    ,("+a", prefOp "+")
+    ,("-a", prefOp "-")
+    ]
+  where
+    binOp o = BinOp (Iden [Name Nothing "a"]) [Name Nothing o] (Iden [Name Nothing "b"])
+    prefOp o = PrefixOp [Name Nothing o] (Iden [Name Nothing "a"])
+
+{-
+TODO: precedence and associativity tests (need to review all operators
+for what precendence and associativity tests to write)
+
+== 6.28 <numeric value function>
+
+Function
+Specify a function yielding a value of type numeric.
+
+<numeric value function> ::=
+    <position expression>
+  | <regex occurrences function>
+  | <regex position expression>
+  | <extract expression>
+  | <length expression>
+  | <cardinality expression>
+  | <max cardinality expression>
+  | <absolute value expression>
+  | <modulus expression>
+  | <natural logarithm>
+  | <exponential function>
+  | <power function>
+  | <square root>
+  | <floor function>
+  | <ceiling function>
+  | <width bucket function>
+-}
+
+
+numericValueFunction :: TestItem
+numericValueFunction = Group "numeric value function"
+    [-- todo: numeric value function
+    ]
+
+{-
+<position expression> ::=
+    <character position expression>
+  | <binary position expression>
+
+<regex occurrences function> ::=
+  OCCURRENCES_REGEX <left paren>
+      <XQuery pattern> [ FLAG <XQuery option flag> ]
+      IN <regex subject string>
+      [ FROM <start position> ]
+      [ USING <char length units> ]
+      <right paren>
+
+<XQuery pattern> ::= <character value expression>
+
+<XQuery option flag> ::= <character value expression>
+
+<regex subject string> ::= <character value expression>
+
+<regex position expression> ::=
+  POSITION_REGEX <left paren>
+      [ <regex position start or after> ]
+      <XQuery pattern> [ FLAG <XQuery option flag> ]
+      IN <regex subject string>
+      [ FROM <start position> ]
+      [ USING <char length units> ]
+      [ OCCURRENCE <regex occurrence> ]
+      [ GROUP <regex capture group> ]
+      <right paren>
+
+<regex position start or after> ::= START | AFTER
+
+<regex occurrence> ::= <numeric value expression>
+
+<regex capture group> ::= <numeric value expression>
+
+<character position expression> ::=
+  POSITION <left paren> <character value expression 1> IN <character value expression 2>
+      [ USING <char length units> ] <right paren>
+
+<character value expression 1> ::= <character value expression>
+
+<character value expression 2> ::= <character value expression>
+
+<binary position expression> ::=
+  POSITION <left paren> <binary value expression> IN <binary value expression> <right paren>
+
+<length expression> ::= <char length expression> | <octet length expression>
+
+<char length expression> ::=
+  { CHAR_LENGTH | CHARACTER_LENGTH } <left paren> <character value expression>
+      [ USING <char length units> ] <right paren>
+
+<octet length expression> ::=
+  OCTET_LENGTH <left paren> <string value expression> <right paren>
+
+<extract expression> ::=
+  EXTRACT <left paren> <extract field> FROM <extract source> <right paren>
+
+<extract field> ::= <primary datetime field> | <time zone field>
+
+<time zone field> ::= TIMEZONE_HOUR | TIMEZONE_MINUTE
+
+<extract source> ::= <datetime value expression> | <interval value expression>
+
+<cardinality expression> ::=
+  CARDINALITY <left paren> <collection value expression> <right paren>
+
+<max cardinality expression> ::=
+  ARRAY_MAX_CARDINALITY <left paren> <array value expression> <right paren>
+
+<absolute value expression> ::=
+  ABS <left paren> <numeric value expression> <right paren>
+
+<modulus expression> ::=
+  MOD <left paren> <numeric value expression dividend> <comma>
+      <numeric value expression divisor> <right paren>
+
+<numeric value expression dividend> ::= <numeric value expression>
+
+<numeric value expression divisor> ::= <numeric value expression>
+
+<natural logarithm> ::=
+  LN <left paren> <numeric value expression> <right paren>
+
+<exponential function> ::=
+  EXP <left paren> <numeric value expression> <right paren>
+
+<power function> ::=
+  POWER <left paren> <numeric value expression base> <comma>
+      <numeric value expression exponent> <right paren>
+
+<numeric value expression base> ::= <numeric value expression>
+
+<numeric value expression exponent> ::= <numeric value expression>
+
+<square root> ::= SQRT <left paren> <numeric value expression> <right paren>
+
+<floor function> ::=
+  FLOOR <left paren> <numeric value expression> <right paren>
+
+<ceiling function> ::=
+  { CEIL | CEILING } <left paren> <numeric value expression> <right paren>
+
+<width bucket function> ::=
+  WIDTH_BUCKET <left paren> <width bucket operand> <comma> <width bucket bound 1> <comma>
+      <width bucket bound 2> <comma> <width bucket count> <right paren>
+
+<width bucket operand> ::= <numeric value expression>
+
+<width bucket bound 1> ::= <numeric value expression>
+
+<width bucket bound 2> ::= <numeric value expression>
+
+<width bucket count> ::= <numeric value expression>
+
+== 6.29 <string value expression>
+
+Function
+Specify a character string value or a binary string value.
+
+<string value expression> ::=
+    <character value expression>
+  | <binary value expression>
+
+<character value expression> ::= <concatenation> | <character factor>
+
+<concatenation> ::=
+  <character value expression> <concatenation operator> <character factor>
+
+<character factor> ::= <character primary> [ <collate clause> ]
+
+<character primary> ::= <value expression primary> | <string value function>
+
+<binary value expression> ::= <binary concatenation> | <binary factor>
+
+<binary factor> ::= <binary primary>
+
+<binary primary> ::= <value expression primary> | <string value function>
+
+<binary concatenation> ::=
+  <binary value expression> <concatenation operator> <binary factor>
+-}
+
+stringScalarExpression :: TestItem
+stringScalarExpression = Group "string value expression"
+    [-- todo: string value expression
+    ]
+
+{-
+== 6.30 <string value function>
+
+Function
+Specify a function yielding a value of type character string or binary string.
+
+<string value function> ::=
+    <character value function>
+  | <binary value function>
+
+<character value function> ::=
+    <character substring function>
+  | <regular expression substring function>
+  | <regex substring function>
+  | <fold>
+  | <transcoding>
+  | <character transliteration>
+  | <regex transliteration>
+  | <trim function>
+  | <character overlay function>
+  | <normalize function>
+  | <specific type method>
+-}
+
+stringValueFunction :: TestItem
+stringValueFunction = Group "string value function"
+    [-- todo: string value function
+    ]
+
+{-
+<character substring function> ::=
+  SUBSTRING <left paren> <character value expression> FROM <start position>
+      [ FOR <string length> ] [ USING <char length units> ] <right paren>
+
+<regular expression substring function> ::=
+  SUBSTRING <left paren> <character value expression> SIMILAR <character value expression>
+      ESCAPE <escape character> <right paren>
+
+<regex substring function> ::=
+  SUBSTRING_REGEX <left paren>
+      <XQuery pattern> [ FLAG <XQuery option flag> ]
+      IN <regex subject string>
+      [ FROM <start position> ]
+      [ USING <char length units> ]
+      [ OCCURRENCE <regex occurrence> ]
+      [ GROUP <regex capture group> ]
+      <right paren>
+
+<fold> ::=
+  { UPPER | LOWER } <left paren> <character value expression> <right paren>
+
+<transcoding> ::=
+  CONVERT <left paren> <character value expression>
+      USING <transcoding name> <right paren>
+
+<character transliteration> ::=
+  TRANSLATE <left paren> <character value expression>
+      USING <transliteration name> <right paren>
+
+<regex transliteration> ::=
+  TRANSLATE_REGEX <left paren>
+      <XQuery pattern> [ FLAG <XQuery option flag> ]
+      IN <regex subject string>
+      [ WITH <XQuery replacement string> ]
+      [ FROM <start position> ]
+      [ USING <char length units> ]
+      [ OCCURRENCE <regex transliteration occurrence> ]
+      <right paren>
+
+<XQuery replacement string> ::= <character value expression>
+
+<regex transliteration occurrence> ::= <regex occurrence> | ALL
+
+<trim function> ::= TRIM <left paren> <trim operands> <right paren>
+
+<trim operands> ::=
+  [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source>
+
+<trim source> ::= <character value expression>
+
+<trim specification> ::= LEADING | TRAILING | BOTH
+
+<trim character> ::= <character value expression>
+
+<character overlay function> ::=
+  OVERLAY <left paren> <character value expression> PLACING <character value expression>
+      FROM <start position> [ FOR <string length> ]
+      [ USING <char length units> ] <right paren>
+
+<normalize function> ::=
+  NORMALIZE <left paren> <character value expression>
+      [ <comma> <normal form> [ <comma> <normalize function result length> ] ] <right paren>
+
+<normal form> ::= NFC | NFD | NFKC | NFKD
+
+<normalize function result length> ::=
+    <character length>
+  | <character large object length>
+
+<specific type method> ::=
+  <user-defined type value expression> <period> SPECIFICTYPE
+      [ <left paren> <right paren> ]
+
+<binary value function> ::=
+       <binary substring function>
+     | <binary trim function>
+     | <binary overlay function>
+
+<binary substring function> ::=
+  SUBSTRING <left paren> <binary value expression> FROM <start position>
+      [ FOR <string length> ] <right paren>
+
+<binary trim function> ::=
+  TRIM <left paren> <binary trim operands> <right paren>
+
+<binary trim operands> ::=
+  [ [ <trim specification> ] [ <trim octet> ] FROM ] <binary trim source>
+
+<binary trim source> ::= <binary value expression>
+
+<trim octet> ::= <binary value expression>
+
+<binary overlay function> ::=
+  OVERLAY <left paren> <binary value expression> PLACING <binary value expression>
+      FROM <start position> [ FOR <string length> ] <right paren>
+
+<start position> ::= <numeric value expression>
+
+<string length> ::= <numeric value expression>
+
+== 6.31 <datetime value expression>
+
+Function
+Specify a datetime value.
+
+<datetime value expression> ::=
+    <datetime term>
+  | <interval value expression> <plus sign> <datetime term>
+  | <datetime value expression> <plus sign> <interval term>
+  | <datetime value expression> <minus sign> <interval term>
+-}
+
+datetimeScalarExpression :: TestItem
+datetimeScalarExpression = Group "datetime value expression"
+    [-- todo: datetime value expression
+     datetimeValueFunction 
+    ]
+
+{-
+<datetime term> ::= <datetime factor>
+
+<datetime factor> ::= <datetime primary> [ <time zone> ]
+
+<datetime primary> ::= <value expression primary> | <datetime value function>
+
+<time zone> ::= AT <time zone specifier>
+
+<time zone specifier> ::= LOCAL | TIME ZONE <interval primary>
+
+== 6.32 <datetime value function>
+
+Function
+Specify a function yielding a value of type datetime.
+
+<datetime value function> ::=
+    <current date value function>
+  | <current time value function>
+  | <current timestamp value function>
+  | <current local time value function>
+  | <current local timestamp value function>
+-}
+
+datetimeValueFunction :: TestItem
+datetimeValueFunction = Group "datetime value function"
+    [-- todo: datetime value function
+    ]
+
+{-
+<current date value function> ::= CURRENT_DATE
+
+<current time value function> ::=
+  CURRENT_TIME [ <left paren> <time precision> <right paren> ]
+
+<current local time value function> ::=
+  LOCALTIME [ <left paren> <time precision> <right paren> ]
+
+<current timestamp value function> ::=
+  CURRENT_TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
+
+<current local timestamp value function> ::=
+  LOCALTIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
+
+== 6.33 <interval value expression>
+
+Function
+Specify an interval value.
+
+<interval value expression> ::=
+    <interval term>
+  | <interval value expression 1> <plus sign> <interval term 1>
+  | <interval value expression 1> <minus sign> <interval term 1>
+  | <left paren> <datetime value expression> <minus sign> <datetime term> <right paren>
+      <interval qualifier>
+-}
+
+intervalScalarExpression :: TestItem
+intervalScalarExpression = Group "interval value expression"
+    [-- todo: interval value expression
+    ]
+
+
+{-
+<interval term> ::=
+    <interval factor>
+  | <interval term 2> <asterisk> <factor>
+  | <interval term 2> <solidus> <factor>
+  | <term> <asterisk> <interval factor>
+
+<interval factor> ::= [ <sign> ] <interval primary>
+
+<interval primary> ::=
+    <value expression primary> [ <interval qualifier> ]
+  | <interval value function>
+
+<interval value expression 1> ::= <interval value expression>
+
+<interval term 1> ::= <interval term>
+
+<interval term 2> ::= <interval term>
+
+== 6.34 <interval value function>
+
+Function
+Specify a function yielding a value of type interval.
+
+<interval value function> ::= <interval absolute value function>
+
+<interval absolute value function> ::=
+  ABS <left paren> <interval value expression> <right paren>
+-}
+
+intervalValueFunction :: TestItem
+intervalValueFunction = Group "interval value function"
+    [-- todo: interval value function
+    ]
+
+
+{-
+== 6.35 <boolean value expression>
+
+Function
+Specify a boolean value.
+
+<boolean value expression> ::=
+    <boolean term>
+  | <boolean value expression> OR <boolean term>
+
+<boolean term> ::= <boolean factor> | <boolean term> AND <boolean factor>
+
+<boolean factor> ::= [ NOT ] <boolean test>
+
+<boolean test> ::= <boolean primary> [ IS [ NOT ] <truth value> ]
+
+<truth value> ::= TRUE | FALSE | UNKNOWN
+
+<boolean primary> ::= <predicate> | <boolean predicand>
+
+<boolean predicand> ::=
+    <parenthesized boolean value expression>
+  | <nonparenthesized value expression primary>
+
+<parenthesized boolean value expression> ::=
+  <left paren> <boolean value expression> <right paren>
+-}
+
+
+booleanScalarExpression :: TestItem
+booleanScalarExpression = Group "booleab value expression"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("a or b", BinOp a [Name Nothing "or"] b)
+    ,("a and b", BinOp a [Name Nothing "and"] b)
+    ,("not a", PrefixOp [Name Nothing "not"] a)
+    ,("a is true", postfixOp "is true")
+    ,("a is false", postfixOp "is false")
+    ,("a is unknown", postfixOp "is unknown")
+    ,("a is not true", postfixOp "is not true")
+    ,("a is not false", postfixOp "is not false")
+    ,("a is not unknown", postfixOp "is not unknown")
+    ,("(a or b)", Parens $ BinOp a [Name Nothing "or"] b)
+    ]
+  where
+    a = Iden [Name Nothing "a"]
+    b = Iden [Name Nothing "b"]
+    postfixOp nm = PostfixOp [Name Nothing nm] a
+
+{-
+TODO: review if more tests are needed. Should at least have
+precendence tests for mixed and, or and not without parens.
+
+== 6.36 <array value expression>
+
+Function
+Specify an array value.
+
+<array value expression> ::= <array concatenation> | <array primary>
+
+<array concatenation> ::=
+  <array value expression 1> <concatenation operator> <array primary>
+
+<array value expression 1> ::= <array value expression>
+
+<array primary> ::= <array value function> | <value expression primary>
+-}
+
+arrayScalarExpression :: TestItem
+arrayScalarExpression = Group "array value expression"
+    [-- todo: array value expression
+    ]
+
+{-
+== 6.37 <array value function>
+
+Function
+Specify a function yielding a value of an array type.
+
+<array value function> ::= <trim array function>
+
+<trim array function> ::=
+  TRIM_ARRAY <left paren> <array value expression> <comma> <numeric value expression>
+    <right paren>
+-}
+
+arrayValueFunction :: TestItem
+arrayValueFunction = Group "array value function"
+    [-- todo: array value function
+    ]
+
+{-
+== 6.38 <array value constructor>
+
+Function
+Specify construction of an array.
+
+<array value constructor> ::=
+    <array value constructor by enumeration>
+  | <array value constructor by query>
+
+<array value constructor by enumeration> ::=
+  ARRAY <left bracket or trigraph> <array element list> <right bracket or trigraph>
+
+<array element list> ::= <array element> [ { <comma> <array element> }... ]
+
+<array element> ::= <value expression>
+
+<array value constructor by query> ::= ARRAY <table subquery>
+-}
+
+arrayValueConstructor :: TestItem
+arrayValueConstructor = Group "array value constructor"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("array[1,2,3]"
+     ,Array (Iden [Name Nothing "array"])
+      [NumLit "1", NumLit "2", NumLit "3"])
+    ,("array[a,b,c]"
+     ,Array (Iden [Name Nothing "array"])
+      [Iden [Name Nothing "a"], Iden [Name Nothing "b"], Iden [Name Nothing "c"]])
+    ,("array(select * from t)"
+      ,ArrayCtor (toQueryExpr $ makeSelect
+                  {msSelectList = [(Star,Nothing)]
+                  ,msFrom = [TRSimple [Name Nothing "t"]]}))
+    ,("array(select * from t order by a)"
+      ,ArrayCtor (toQueryExpr $ makeSelect
+                  {msSelectList = [(Star,Nothing)]
+                  ,msFrom = [TRSimple [Name Nothing "t"]]
+                  ,msOrderBy = [SortSpec (Iden [Name Nothing "a"])
+                                    DirDefault NullsOrderDefault]}))
+    ]
+
+
+{-
+== 6.39 <multiset value expression>
+
+Function
+Specify a multiset value.
+
+<multiset value expression> ::=
+    <multiset term>
+  | <multiset value expression> MULTISET UNION [ ALL | DISTINCT ] <multiset term>
+  | <multiset value expression> MULTISET EXCEPT [ ALL | DISTINCT ] <multiset term>
+
+<multiset term> ::=
+    <multiset primary>
+  | <multiset term> MULTISET INTERSECT [ ALL | DISTINCT ] <multiset primary>
+
+<multiset primary> ::= <multiset value function> | <value expression primary>
+-}
+
+multisetScalarExpression :: TestItem
+multisetScalarExpression = Group "multiset value expression"
+   $ map (uncurry (TestScalarExpr ansi2011))
+   [("a multiset union b"
+    ,MultisetBinOp (Iden [Name Nothing "a"]) Union SQDefault (Iden [Name Nothing "b"]))
+   ,("a multiset union all b"
+    ,MultisetBinOp (Iden [Name Nothing "a"]) Union All (Iden [Name Nothing "b"]))
+   ,("a multiset union distinct b"
+    ,MultisetBinOp (Iden [Name Nothing "a"]) Union Distinct (Iden [Name Nothing "b"]))
+   ,("a multiset except b"
+    ,MultisetBinOp (Iden [Name Nothing "a"]) Except SQDefault (Iden [Name Nothing "b"]))
+   ,("a multiset intersect b"
+    ,MultisetBinOp (Iden [Name Nothing "a"]) Intersect SQDefault (Iden [Name Nothing "b"]))
+   ]
+
+{-
+TODO: check precedence and associativity
+
+== 6.40 <multiset value function>
+
+Function
+Specify a function yielding a value of a multiset type.
+
+<multiset value function> ::= <multiset set function>
+
+<multiset set function> ::=
+  SET <left paren> <multiset value expression> <right paren>
+
+TODO: set is now a reserved keyword. Fix the set parsing with a
+special case term.
+-}
+
+multisetValueFunction :: TestItem
+multisetValueFunction = Group "multiset value function"
+   $ map (uncurry (TestScalarExpr ansi2011))
+   [("set(a)", App [Name Nothing "set"] [Iden [Name Nothing "a"]])
+   ]
+
+{-
+== 6.41 <multiset value constructor>
+
+Function
+Specify construction of a multiset.
+
+<multiset value constructor> ::=
+    <multiset value constructor by enumeration>
+  | <multiset value constructor by query>
+  | <table value constructor by query>
+
+<multiset value constructor by enumeration> ::=
+  MULTISET <left bracket or trigraph> <multiset element list> <right bracket or trigraph>
+
+<multiset element list> ::=
+  <multiset element> [ { <comma> <multiset element> }... ]
+
+<multiset element> ::= <value expression>
+
+<multiset value constructor by query> ::= MULTISET <table subquery>
+
+<table value constructor by query> ::= TABLE <table subquery>
+-}
+
+multisetValueConstructor :: TestItem
+multisetValueConstructor = Group "multiset value constructor"
+   $ map (uncurry (TestScalarExpr ansi2011))
+   [("multiset[a,b,c]", MultisetCtor[Iden [Name Nothing "a"]
+                                    ,Iden [Name Nothing "b"], Iden [Name Nothing "c"]])
+   ,("multiset(select * from t)", MultisetQueryCtor ms)
+   ,("table(select * from t)", MultisetQueryCtor ms)
+   ]
+  where
+    ms = toQueryExpr $ makeSelect {msSelectList = [(Star,Nothing)]
+                    ,msFrom = [TRSimple [Name Nothing "t"]]}
+
+
+-- = 7 Query expressions
+
+queryExpressions :: TestItem
+queryExpressions = Group "query expressions"
+    [rowValueConstructor
+    ,tableValueConstructor
+    ,fromClause
+    ,tableReference
+    ,joinedTable
+    ,whereClause
+    ,groupByClause
+    ,havingClause
+    ,windowClause
+    ,querySpecification
+    ,withQueryExpression
+    ,setOpQueryExpression
+    ,explicitTableQueryExpression
+    ,orderOffsetFetchQueryExpression
+    ,searchOrCycleClause
+    ]
+
+
+{-
+== 7.1 <row value constructor>
+
+Function
+Specify a value or list of values to be constructed into a row.
+
+<row value constructor> ::=
+    <common value expression>
+  | <boolean value expression>
+  | <explicit row value constructor>
+
+<explicit row value constructor> ::=
+    <left paren> <row value constructor element> <comma>
+        <row value constructor element list> <right paren>
+  | ROW <left paren> <row value constructor element list> <right paren>
+  | <row subquery>
+
+<row value constructor element list> ::=
+  <row value constructor element> [ { <comma> <row value constructor element> }... ]
+
+<row value constructor element> ::= <value expression>
+
+<contextually typed row value constructor> ::=
+    <common value expression>
+  | <boolean value expression>
+  | <contextually typed value specification>
+  | <left paren> <contextually typed value specification> <right paren>
+  | <left paren> <contextually typed row value constructor element> <comma>
+      <contextually typed row value constructor element list> <right paren>
+  | ROW <left paren> <contextually typed row value constructor element list> <right paren>
+
+<contextually typed row value constructor element list> ::=
+  <contextually typed row value constructor element>
+      [ { <comma> <contextually typed row value constructor element> }... ]
+
+<contextually typed row value constructor element> ::=
+    <value expression>
+  | <contextually typed value specification>
+
+<row value constructor predicand> ::=
+    <common value expression>
+  | <boolean predicand>
+  | <explicit row value constructor>
+-}
+
+rowValueConstructor :: TestItem
+rowValueConstructor = Group "row value constructor"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("(a,b)"
+     ,SpecialOp [Name Nothing "rowctor"] [Iden [Name Nothing "a"], Iden [Name Nothing "b"]])
+    ,("row(1)",App [Name Nothing "row"] [NumLit "1"])
+    ,("row(1,2)",App [Name Nothing "row"] [NumLit "1",NumLit "2"])
+    ]
+
+{-
+== 7.2 <row value expression>
+
+Function
+Specify a row value.
+
+<row value expression> ::=
+    <row value special case>
+  | <explicit row value constructor>
+
+<table row value expression> ::=
+    <row value special case>
+  | <row value constructor>
+
+<contextually typed row value expression> ::=
+    <row value special case>
+  | <contextually typed row value constructor>
+
+<row value predicand> ::=
+    <row value special case>
+  | <row value constructor predicand>
+
+<row value special case> ::= <nonparenthesized value expression primary>
+
+There is nothing new here.
+
+== 7.3 <table value constructor>
+
+Function
+Specify a set of <row value expression>s to be constructed into a table.
+
+<table value constructor> ::= VALUES <row value expression list>
+
+<row value expression list> ::=
+  <table row value expression> [ { <comma> <table row value expression> }... ]
+
+<contextually typed table value constructor> ::=
+  VALUES <contextually typed row value expression list>
+
+<contextually typed row value expression list> ::=
+  <contextually typed row value expression>
+      [ { <comma> <contextually typed row value expression> }... ]
+-}
+
+tableValueConstructor :: TestItem
+tableValueConstructor = Group "table value constructor"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("values (1,2), (a+b,(select count(*) from t));"
+     ,Values [[NumLit "1", NumLit "2"]
+             ,[BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"]
+                     (Iden [Name Nothing "b"])
+              ,SubQueryExpr SqSq
+               (toQueryExpr $ makeSelect
+                {msSelectList = [(App [Name Nothing "count"] [Star],Nothing)]
+                ,msFrom = [TRSimple [Name Nothing "t"]]})]])
+    ]
+
+{-
+== 7.4 <table expression>
+
+Function
+Specify a table or a grouped table.
+
+<table expression> ::=
+  <from clause>
+      [ <where clause> ]
+      [ <group by clause> ]
+      [ <having clause> ]
+      [ <window clause> ]
+
+== 7.5 <from clause>
+
+Function
+Specify a table derived from one or more tables.
+
+<from clause> ::= FROM <table reference list>
+
+<table reference list> ::=
+  <table reference> [ { <comma> <table reference> }... ]
+-}
+
+fromClause :: TestItem
+fromClause = Group "fromClause"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("select * from tbl1,tbl2"
+     ,toQueryExpr $ makeSelect
+      {msSelectList = [(Star, Nothing)]
+      ,msFrom = [TRSimple [Name Nothing "tbl1"], TRSimple [Name Nothing "tbl2"]]
+      })]
+
+
+{-
+== 7.6 <table reference>
+
+Function
+Reference a table.
+-}
+
+tableReference :: TestItem
+tableReference = Group "table reference"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("select * from t", toQueryExpr sel)
+
+{-
+<table reference> ::= <table factor> | <joined table>
+
+<table factor> ::= <table primary> [ <sample clause> ]
+
+<sample clause> ::=
+  TABLESAMPLE <sample method> <left paren> <sample percentage> <right paren>
+      [ <repeatable clause> ]
+
+<sample method> ::= BERNOULLI | SYSTEM
+
+<repeatable clause> ::= REPEATABLE <left paren> <repeat argument> <right paren>
+
+<sample percentage> ::= <numeric value expression>
+
+<repeat argument> ::= <numeric value expression>
+
+<table primary> ::=
+    <table or query name> [ <query system time period specification> ]
+          [ [ AS ] <correlation name>
+            [ <left paren> <derived column list> <right paren> ] ]
+  | <derived table> [ AS ] <correlation name>
+        [ <left paren> <derived column list> <right paren> ]
+  | <lateral derived table> [ AS ] <correlation name>
+        [ <left paren> <derived column list> <right paren> ]
+  | <collection derived table> [ AS ] <correlation name>
+        [ <left paren> <derived column list> <right paren> ]
+  | <table function derived table> [ AS ] <correlation name>
+        [ <left paren> <derived column list> <right paren> ]
+  | <only spec> [ [ AS ] <correlation name>
+        [ <left paren> <derived column list> <right paren> ] ]
+  | <data change delta table> [ [ AS ] <correlation name>
+        [ <left paren> <derived column list> <right paren> ] ]
+  | <parenthesized joined table>
+
+<query system time period specification> ::=
+    FOR SYSTEM_TIME AS OF <point in time 1>
+  | FOR SYSTEM_TIME BETWEEN [ ASYMMETRIC | SYMMETRIC ]
+      <point in time 1> AND <point in time 2>
+  | FOR SYSTEM_TIME FROM <point in time 1> TO <point in time 2>
+
+TODO: query system time period spec
+
+<point in time 1> ::= <point in time>
+
+<point in time 2> ::= <point in time>
+
+<point in time> ::= <datetime value expression>
+
+<only spec> ::= ONLY <left paren> <table or query name> <right paren>
+
+TODO: only
+
+<lateral derived table> ::= LATERAL <table subquery>
+
+<collection derived table> ::=
+  UNNEST <left paren> <collection value expression>
+      [ { <comma> <collection value expression> }... ] <right paren>
+      [ WITH ORDINALITY ]
+
+<table function derived table> ::=
+  TABLE <left paren> <collection value expression> <right paren>
+
+<derived table> ::= <table subquery>
+
+<table or query name> ::= <table name> | <transition table name> | <query name>
+
+<derived column list> ::= <column name list>
+
+<column name list> ::= <column name> [ { <comma> <column name> }... ]
+
+<data change delta table> ::=
+  <result option> TABLE <left paren> <data change statement> <right paren>
+
+<data change statement> ::=
+    <delete statement: searched>
+  | <insert statement>
+  | <merge statement>
+  | <update statement: searched>
+
+<result option> ::= FINAL | NEW | OLD
+
+<parenthesized joined table> ::=
+    <left paren> <parenthesized joined table> <right paren>
+  | <left paren> <joined table> <right paren>
+-}
+
+
+    -- table or query name
+    ,("select * from t u", toQueryExpr $ a sel)
+    ,("select * from t as u", toQueryExpr $ a sel)
+    ,("select * from t u(a,b)", toQueryExpr sel1 )
+    ,("select * from t as u(a,b)", toQueryExpr sel1)
+    -- derived table TODO: realistic example
+    ,("select * from (select * from t) u"
+     ,toQueryExpr $ a $ sel {msFrom = [TRQueryExpr $ toQueryExpr sel]})
+    -- lateral TODO: realistic example
+    ,("select * from lateral t"
+     ,toQueryExpr $ af TRLateral sel)
+    -- TODO: bug, lateral should bind more tightly than the alias
+    --,("select * from lateral t u"
+    -- ,a $ af sel TRLateral)
+    -- collection TODO: realistic example
+     -- TODO: make it work
+    --,("select * from unnest(a)", undefined)
+    --,("select * from unnest(a,b)", undefined)
+    --,("select * from unnest(a,b) with ordinality", undefined)
+    --,("select * from unnest(a,b) with ordinality u", undefined)
+    --,("select * from unnest(a,b) with ordinality as u", undefined)
+    -- table fn TODO: realistic example
+     -- TODO: make it work
+    --,("select * from table(a)", undefined)
+    -- parens
+    ,("select * from (a join b)", toQueryExpr jsel)
+    ,("select * from (a join b) u", toQueryExpr $ a jsel)
+    ,("select * from ((a join b)) u", toQueryExpr $ a $ af TRParens jsel)
+    ,("select * from ((a join b) u) u", toQueryExpr $ a $ af TRParens $ a jsel)
+    ]
+  where
+    sel = makeSelect
+          {msSelectList = [(Star, Nothing)]
+          ,msFrom = [TRSimple [Name Nothing "t"]]}
+    af f s = s {msFrom = map f (msFrom s)}
+    a s = af (\x -> TRAlias x $ Alias (Name Nothing "u") Nothing) s
+    sel1 = makeSelect
+          {msSelectList = [(Star, Nothing)]
+          ,msFrom = [TRAlias (TRSimple [Name Nothing "t"])
+                     $ Alias (Name Nothing "u") $ Just [Name Nothing "a", Name Nothing "b"]]}
+    jsel = sel {msFrom =
+                [TRParens $ TRJoin (TRSimple [Name Nothing "a"])
+                                   False
+                                   JInner
+                                   (TRSimple [Name Nothing "b"])
+                                   Nothing]}
+
+{-
+== 7.7 <joined table>
+
+Function
+Specify a table derived from a Cartesian product, inner join, or outer join.
+
+<joined table> ::= <cross join> | <qualified join> | <natural join>
+
+<cross join> ::= <table reference> CROSS JOIN <table factor>
+
+<qualified join> ::=
+  { <table reference> | <partitioned join table> }
+      [ <join type> ] JOIN
+      { <table reference> | <partitioned join table> }
+      <join specification>
+
+<partitioned join table> ::=
+  <table factor> PARTITION BY
+      <partitioned join column reference list>
+
+<partitioned join column reference list> ::=
+  <left paren> <partitioned join column reference>
+      [ { <comma> <partitioned join column reference> }... ]
+      <right paren>
+
+<partitioned join column reference> ::= <column reference>
+
+<natural join> ::=
+  { <table reference> | <partitioned join table> }
+      NATURAL [ <join type> ] JOIN
+      { <table factor> | <partitioned join table> }
+
+<join specification> ::= <join condition> | <named columns join>
+
+<join condition> ::= ON <search condition>
+
+<named columns join> ::= USING <left paren> <join column list> <right paren>
+
+<join type> ::= INNER | <outer join type> [ OUTER ]
+
+<outer join type> ::= LEFT | RIGHT | FULL
+
+<join column list> ::= <column name list>
+-}
+
+joinedTable :: TestItem
+joinedTable = Group "joined table"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("select * from a cross join b"
+     ,sel $ TRJoin a False JCross b Nothing)
+    ,("select * from a join b on true"
+     ,sel $ TRJoin a False JInner b
+      (Just $ JoinOn $ Iden [Name Nothing "true"]))
+    ,("select * from a join b using (c)"
+     ,sel $ TRJoin a False JInner b
+      (Just $ JoinUsing [Name Nothing "c"]))
+    ,("select * from a inner join b on true"
+     ,sel $ TRJoin a False JInner b
+      (Just $ JoinOn $ Iden [Name Nothing "true"]))
+    ,("select * from a left join b on true"
+     ,sel $ TRJoin a False JLeft b
+      (Just $ JoinOn $ Iden [Name Nothing "true"]))
+    ,("select * from a left outer join b on true"
+     ,sel $ TRJoin a False JLeft b
+      (Just $ JoinOn $ Iden [Name Nothing "true"]))
+    ,("select * from a right join b on true"
+     ,sel $ TRJoin a False JRight b
+      (Just $ JoinOn $ Iden [Name Nothing "true"]))
+    ,("select * from a full join b on true"
+     ,sel $ TRJoin a False JFull b
+      (Just $ JoinOn $ Iden [Name Nothing "true"]))
+    ,("select * from a natural join b"
+     ,sel $ TRJoin a True JInner b Nothing)
+    ,("select * from a natural inner join b"
+     ,sel $ TRJoin a True JInner b Nothing)
+    ,("select * from a natural left join b"
+     ,sel $ TRJoin a True JLeft b Nothing)
+    ,("select * from a natural left outer join b"
+     ,sel $ TRJoin a True JLeft b Nothing)
+    ,("select * from a natural right join b"
+     ,sel $ TRJoin a True JRight b Nothing)
+    ,("select * from a natural full join b"
+     ,sel $ TRJoin a True JFull b Nothing)
+    ]
+  where
+    sel t = toQueryExpr $ makeSelect
+            {msSelectList = [(Star, Nothing)]
+            ,msFrom = [t]}
+    a = TRSimple [Name Nothing "a"]
+    b = TRSimple [Name Nothing "b"]
+
+{-
+TODO: partitioned joins
+
+== 7.8 <where clause>
+
+Function
+
+Specify a table derived by the application of a <search condition> to
+the result of the preceding <from clause>.
+
+<where clause> ::= WHERE <search condition>
+-}
+
+whereClause :: TestItem
+whereClause = Group "where clause"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("select * from t where a = 5"
+    ,toQueryExpr $ makeSelect
+     {msSelectList = [(Star,Nothing)]
+     ,msFrom = [TRSimple [Name Nothing "t"]]
+     ,msWhere = Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "5")})]
+
+{-
+== 7.9 <group by clause>
+
+Function
+
+Specify a grouped table derived by the application of the <group by
+clause> to the result of the previously specified clause.
+
+<group by clause> ::= GROUP BY [ <set quantifier> ] <grouping element list>
+
+<grouping element list> ::=
+  <grouping element> [ { <comma> <grouping element> }... ]
+
+<grouping element> ::=
+    <ordinary grouping set>
+  | <rollup list>
+  | <cube list>
+  | <grouping sets specification>
+  | <empty grouping set>
+
+<ordinary grouping set> ::=
+    <grouping column reference>
+  | <left paren> <grouping column reference list> <right paren>
+
+<grouping column reference> ::= <column reference> [ <collate clause> ]
+
+<grouping column reference list> ::=
+  <grouping column reference> [ { <comma> <grouping column reference> }... ]
+
+<rollup list> ::=
+  ROLLUP <left paren> <ordinary grouping set list> <right paren>
+
+<ordinary grouping set list> ::=
+  <ordinary grouping set> [ { <comma> <ordinary grouping set> }... ]
+
+<cube list> ::= CUBE <left paren> <ordinary grouping set list> <right paren>
+
+<grouping sets specification> ::=
+  GROUPING SETS <left paren> <grouping set list> <right paren>
+
+<grouping set list> ::= <grouping set> [ { <comma> <grouping set> }... ]
+
+<grouping set> ::=
+    <ordinary grouping set>
+  | <rollup list>
+  | <cube list>
+  | <grouping sets specification>
+  | <empty grouping set>
+
+<empty grouping set> ::= <left paren> <right paren>
+-}
+
+
+groupByClause :: TestItem
+groupByClause = Group "group by clause"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("select a,sum(x) from t group by a"
+     ,toQueryExpr $ ms [SimpleGroup $ Iden [Name Nothing "a"]])
+     ,("select a,sum(x) from t group by a collate c"
+     ,toQueryExpr $ ms [SimpleGroup $ Collate (Iden [Name Nothing "a"]) [Name Nothing "c"]])
+    ,("select a,b,sum(x) from t group by a,b"
+     ,toQueryExpr $ msx [SimpleGroup $ Iden [Name Nothing "a"]
+          ,SimpleGroup $ Iden [Name Nothing "b"]])
+    -- todo: group by set quantifier
+    --,("select a,sum(x) from t group by distinct a"
+    --,undefined)
+    --,("select a,sum(x) from t group by all a"
+    -- ,undefined)
+    ,("select a,b,sum(x) from t group by rollup(a,b)"
+     ,toQueryExpr $ msx [Rollup [SimpleGroup $ Iden [Name Nothing "a"]
+                  ,SimpleGroup $ Iden [Name Nothing "b"]]])
+    ,("select a,b,sum(x) from t group by cube(a,b)"
+     ,toQueryExpr $ msx [Cube [SimpleGroup $ Iden [Name Nothing "a"]
+                ,SimpleGroup $ Iden [Name Nothing "b"]]])
+    ,("select a,b,sum(x) from t group by grouping sets((),(a,b))"
+     ,toQueryExpr $ msx [GroupingSets [GroupingParens []
+                        ,GroupingParens [SimpleGroup $ Iden [Name Nothing "a"]
+                                        ,SimpleGroup $ Iden [Name Nothing "b"]]]])
+    ,("select sum(x) from t group by ()"
+     ,toQueryExpr $ makeSelect
+           {msSelectList = [(App [Name Nothing "sum"] [Iden [Name Nothing "x"]], Nothing)]
+           ,msFrom = [TRSimple [Name Nothing "t"]]
+           ,msGroupBy = [GroupingParens []]})
+    ]
+  where
+    ms g = makeSelect
+           {msSelectList = [(Iden [Name Nothing "a"], Nothing)
+                           ,(App [Name Nothing "sum"] [Iden [Name Nothing "x"]], Nothing)]
+           ,msFrom = [TRSimple [Name Nothing "t"]]
+           ,msGroupBy = g}
+    msx g = makeSelect
+           {msSelectList = [(Iden [Name Nothing "a"], Nothing)
+                           ,(Iden [Name Nothing "b"],Nothing)
+                           ,(App [Name Nothing "sum"] [Iden [Name Nothing "x"]], Nothing)]
+           ,msFrom = [TRSimple [Name Nothing "t"]]
+           ,msGroupBy = g}
+
+{-
+== 7.10 <having clause>
+
+Function
+
+Specify a grouped table derived by the elimination of groups that do
+not satisfy a <search condition>.
+
+<having clause> ::= HAVING <search condition>
+-}
+
+havingClause :: TestItem
+havingClause = Group "having clause"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("select a,sum(x) from t group by a having sum(x) > 1000"
+     ,toQueryExpr $ makeSelect
+      {msSelectList = [(Iden [Name Nothing "a"], Nothing)
+                      ,(App [Name Nothing "sum"] [Iden [Name Nothing "x"]], Nothing)]
+      ,msFrom = [TRSimple [Name Nothing "t"]]
+      ,msGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]]
+      ,msHaving = Just $ BinOp (App [Name Nothing "sum"] [Iden [Name Nothing "x"]])
+                               [Name Nothing ">"]
+                               (NumLit "1000")})
+    ]
+
+{-
+== 7.11 <window clause>
+
+Function
+Specify one or more window definitions.
+
+<window clause> ::= WINDOW <window definition list>
+
+<window definition list> ::=
+  <window definition> [ { <comma> <window definition> }... ]
+
+<window definition> ::= <new window name> AS <window specification>
+
+<new window name> ::= <window name>
+
+<window specification> ::=
+  <left paren> <window specification details> <right paren>
+
+<window specification details> ::=
+  [ <existing window name> ]
+      [ <window partition clause> ]
+      [ <window order clause> ]
+      [ <window frame clause> ]
+
+<existing window name> ::= <window name>
+
+<window partition clause> ::=
+  PARTITION BY <window partition column reference list>
+
+<window partition column reference list> ::=
+  <window partition column reference>
+      [ { <comma> <window partition column reference> }... ]
+
+<window partition column reference> ::= <column reference> [ <collate clause> ]
+
+<window order clause> ::= ORDER BY <sort specification list>
+
+<window frame clause> ::=
+  <window frame units> <window frame extent>
+      [ <window frame exclusion> ]
+
+<window frame units> ::= ROWS | RANGE | GROUPS
+
+<window frame extent> ::= <window frame start> | <window frame between>
+
+<window frame start> ::=
+    UNBOUNDED PRECEDING
+  | <window frame preceding>
+  | CURRENT ROW
+
+<window frame preceding> ::= <unsigned value specification> PRECEDING
+
+<window frame between> ::=
+  BETWEEN <window frame bound 1> AND <window frame bound 2>
+
+<window frame bound 1> ::= <window frame bound>
+
+<window frame bound 2> ::= <window frame bound>
+
+<window frame bound> ::=
+    <window frame start>
+  | UNBOUNDED FOLLOWING
+  | <window frame following>
+
+<window frame following> ::= <unsigned value specification> FOLLOWING
+
+<window frame exclusion> ::=
+    EXCLUDE CURRENT ROW
+  | EXCLUDE GROUP
+  | EXCLUDE TIES
+  | EXCLUDE NO OTHERS
+-}
+
+windowClause :: TestItem
+windowClause = Group "window clause"
+    [-- todo: window clause
+    ]
+
+{-
+== 7.12 <query specification>
+
+Function
+Specify a table derived from the result of a <table expression>.
+
+<query specification> ::=
+  SELECT [ <set quantifier> ] <select list> <table expression>
+
+<select list> ::=
+    <asterisk>
+  | <select sublist> [ { <comma> <select sublist> }... ]
+
+<select sublist> ::= <derived column> | <qualified asterisk>
+
+<qualified asterisk> ::=
+    <asterisked identifier chain> <period> <asterisk>
+  | <all fields reference>
+
+<asterisked identifier chain> ::=
+  <asterisked identifier> [ { <period> <asterisked identifier> }... ]
+
+<asterisked identifier> ::= <identifier>
+
+<derived column> ::= <value expression> [ <as clause> ]
+
+<as clause> ::= [ AS ] <column name>
+
+<all fields reference> ::=
+  <value expression primary> <period> <asterisk>
+      [ AS <left paren> <all fields column name list> <right paren> ]
+
+<all fields column name list> ::= <column name list>
+-}
+
+querySpecification :: TestItem
+querySpecification = Group "query specification"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("select a from t",toQueryExpr ms)
+    ,("select all a from t",toQueryExpr $ ms {msSetQuantifier = All})
+    ,("select distinct a from t",toQueryExpr $ ms {msSetQuantifier = Distinct})
+    ,("select * from t", toQueryExpr $ ms {msSelectList = [(Star,Nothing)]})
+    ,("select a.* from t"
+     ,toQueryExpr $ ms {msSelectList = [(BinOp (Iden [Name Nothing "a"]) [Name Nothing "."] Star
+                           ,Nothing)]})
+    ,("select a b from t"
+     ,toQueryExpr $ ms {msSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "b")]})
+    ,("select a as b from t"
+     ,toQueryExpr $ ms {msSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "b")]})
+    ,("select a,b from t"
+     ,toQueryExpr $ ms {msSelectList = [(Iden [Name Nothing "a"], Nothing)
+                         ,(Iden [Name Nothing "b"], Nothing)]})
+    -- todo: all field reference alias
+    --,("select * as (a,b) from t",undefined)
+    ]
+  where
+    ms = makeSelect
+         {msSelectList = [(Iden [Name Nothing "a"], Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]
+         }
+
+{-
+== 7.13 <query expression>
+
+Function
+Specify a table.
+
+<query expression> ::=
+  [ <with clause> ] <query expression body>
+      [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+
+<with clause> ::= WITH [ RECURSIVE ] <with list>
+
+<with list> ::= <with list element> [ { <comma> <with list element> }... ]
+
+<with list element> ::=
+  <query name> [ <left paren> <with column list> <right paren> ]
+      AS <table subquery> [ <search or cycle clause> ]
+
+<with column list> ::= <column name list>
+-}
+
+withQueryExpression :: TestItem
+withQueryExpression= Group "with query expression"
+    [-- todo: with query expression
+    ]
+
+{-
+<query expression body> ::=
+    <query term>
+  | <query expression body> UNION [ ALL | DISTINCT ]
+      [ <corresponding spec> ] <query term>
+  | <query expression body> EXCEPT [ ALL | DISTINCT ]
+      [ <corresponding spec> ] <query term>
+
+<query term> ::=
+    <query primary>
+  | <query term> INTERSECT [ ALL | DISTINCT ]
+      [ <corresponding spec> ] <query primary>
+
+<query primary> ::=
+    <simple table>
+  | <left paren> <query expression body>
+      [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+      <right paren>
+-}
+
+setOpQueryExpression :: TestItem
+setOpQueryExpression= Group "set operation query expression"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    -- todo: complete setop query expression tests
+    [{-("select * from t union select * from t"
+     ,undefined)
+    ,("select * from t union all select * from t"
+     ,undefined)
+    ,("select * from t union distinct select * from t"
+     ,undefined)
+    ,("select * from t union corresponding select * from t"
+     ,undefined)
+    ,("select * from t union corresponding by (a,b) select * from t"
+     ,undefined)
+    ,("select * from t except select * from t"
+     ,undefined)
+    ,("select * from t in intersect select * from t"
+     ,undefined)-}
+    ]
+
+{-
+TODO: tests for the associativity and precendence
+
+TODO: not sure exactly where parens are allowed, we will allow them
+everywhere
+
+<simple table> ::=
+    <query specification>
+  | <table value constructor>
+  | <explicit table>
+
+<explicit table> ::= TABLE <table or query name>
+
+<corresponding spec> ::=
+  CORRESPONDING [ BY <left paren> <corresponding column list> <right paren> ]
+
+<corresponding column list> ::= <column name list>
+-}
+
+explicitTableQueryExpression :: TestItem
+explicitTableQueryExpression= Group "explicit table query expression"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("table t", Table [Name Nothing "t"])
+    ]
+
+
+{-
+<order by clause> ::= ORDER BY <sort specification list>
+
+<result offset clause> ::= OFFSET <offset row count> { ROW | ROWS }
+
+<fetch first clause> ::=
+  FETCH { FIRST | NEXT } [ <fetch first quantity> ] { ROW | ROWS } { ONLY | WITH TIES }
+
+<fetch first quantity> ::= <fetch first row count> | <fetch first percentage>
+
+<offset row count> ::= <simple value specification>
+
+<fetch first row count> ::= <simple value specification>
+
+<fetch first percentage> ::= <simple value specification> PERCENT
+-}
+
+orderOffsetFetchQueryExpression :: TestItem
+orderOffsetFetchQueryExpression = Group "order, offset, fetch query expression"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [-- todo: finish tests for order offset and fetch
+     ("select a from t order by a"
+     ,toQueryExpr $ ms {msOrderBy = [SortSpec (Iden [Name Nothing "a"])
+                           DirDefault NullsOrderDefault]})
+    ,("select a from t offset 5 row"
+     ,toQueryExpr $ ms {msOffset = Just $ NumLit "5"})
+    ,("select a from t offset 5 rows"
+     ,toQueryExpr $ ms {msOffset = Just $ NumLit "5"})
+    ,("select a from t fetch first 5 row only"
+     ,toQueryExpr $ ms {msFetchFirst = Just $ NumLit "5"})
+    -- todo: support with ties and percent in fetch
+    --,("select a from t fetch next 5 rows with ties"
+    --,("select a from t fetch first 5 percent rows only"
+    ]
+ where
+    ms = makeSelect
+         {msSelectList = [(Iden [Name Nothing "a"], Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]
+         }
+
+
+{-
+== 7.14 <search or cycle clause>
+
+Function
+
+Specify the generation of ordering and cycle detection information in
+the result of recursive query expressions.
+
+<search or cycle clause> ::=
+    <search clause>
+  | <cycle clause>
+  | <search clause> <cycle clause>
+
+<search clause> ::= SEARCH <recursive search order> SET <sequence column>
+
+<recursive search order> ::=
+    DEPTH FIRST BY <column name list>
+  | BREADTH FIRST BY <column name list>
+
+<sequence column> ::= <column name>
+
+<cycle clause> ::=
+  CYCLE <cycle column list> SET <cycle mark column> TO <cycle mark value>
+      DEFAULT <non-cycle mark value> USING <path column>
+
+<cycle column list> ::= <cycle column> [ { <comma> <cycle column> }... ]
+
+<cycle column> ::= <column name>
+
+<cycle mark column> ::= <column name>
+
+<path column> ::= <column name>
+
+<cycle mark value> ::= <value expression>
+
+<non-cycle mark value> ::= <value expression>
+-}
+
+searchOrCycleClause :: TestItem
+searchOrCycleClause = Group "search or cycle clause"
+    [-- todo: search or cycle clause
+    ]
+
+{-
+== 7.15 <subquery>
+
+Function
+
+Specify a scalar value, a row, or a table derived from a <query
+expression>.
+
+<scalar subquery> ::= <subquery>
+
+<row subquery> ::= <subquery>
+
+<table subquery> ::= <subquery>
+
+<subquery> ::= <left paren> <query expression> <right paren>
+-}
+
+scalarSubquery :: TestItem
+scalarSubquery = Group "scalar subquery"
+    [-- todo: scalar subquery
+    ]
+
+{-
+= 8 Predicates
+
+== 8.1 <predicate>
+
+Function
+Specify a condition that can be evaluated to give a boolean value.
+
+<predicate> ::=
+    <comparison predicate>
+  | <between predicate>
+  | <in predicate>
+  | <like predicate>
+  | <similar predicate>
+  | <regex like predicate>
+  | <null predicate>
+  | <quantified comparison predicate>
+  | <exists predicate>
+  | <unique predicate>
+  | <normalized predicate>
+  | <match predicate>
+  | <overlaps predicate>
+  | <distinct predicate>
+  | <member predicate>
+  | <submultiset predicate>
+  | <set predicate>
+  | <type predicate>
+  | <period predicate>
+-}
+
+predicates :: TestItem
+predicates = Group "predicates"
+    [comparisonPredicates
+    ,betweenPredicate
+    ,inPredicate
+    ,likePredicate
+    ,similarPredicate
+    ,regexLikePredicate
+    ,nullPredicate
+    ,quantifiedComparisonPredicate
+    ,existsPredicate
+    ,uniquePredicate
+    ,normalizedPredicate
+    ,matchPredicate
+    ,overlapsPredicate
+    ,distinctPredicate
+    ,memberPredicate
+    ,submultisetPredicate
+    ,setPredicate
+    ,periodPredicate
+    ]
+
+
+{-
+== 8.1 <predicate>
+
+No grammar
+
+== 8.2 <comparison predicate>
+
+Function
+Specify a comparison of two row values.
+
+<comparison predicate> ::= <row value predicand> <comparison predicate part 2>
+
+<comparison predicate part 2> ::= <comp op> <row value predicand>
+
+<comp op> ::=
+    <equals operator>
+  | <not equals operator>
+  | <less than operator>
+  | <greater than operator>
+  | <less than or equals operator>
+  | <greater than or equals operator>
+-}
+
+comparisonPredicates :: TestItem
+comparisonPredicates = Group "comparison predicates"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    $ map mkOp ["=", "<>", "<", ">", "<=", ">="]
+    <> [("ROW(a) = ROW(b)"
+        ,BinOp (App [Name Nothing "ROW"] [a])
+               [Name Nothing "="]
+               (App [Name Nothing "ROW"] [b]))
+       ,("(a,b) = (c,d)"
+        ,BinOp (SpecialOp [Name Nothing "rowctor"] [a,b])
+           [Name Nothing "="]
+           (SpecialOp [Name Nothing "rowctor"] [Iden [Name Nothing "c"], Iden [Name Nothing "d"]]))
+    ]
+  where
+    mkOp nm = ("a " <> nm <> " b"
+              ,BinOp a [Name Nothing nm] b)
+    a = Iden [Name Nothing "a"]
+    b = Iden [Name Nothing "b"]
+
+{-
+TODO: what other tests, more complex expressions with comparisons?
+
+== 8.3 <between predicate>
+
+Function
+Specify a range comparison.
+
+<between predicate> ::= <row value predicand> <between predicate part 2>
+
+<between predicate part 2> ::=
+  [ NOT ] BETWEEN [ ASYMMETRIC | SYMMETRIC ]
+      <row value predicand> AND <row value predicand>
+-}
+
+betweenPredicate :: TestItem
+betweenPredicate = Group "between predicate"
+    [-- todo: between predicate
+    ]
+
+{-
+== 8.4 <in predicate>
+
+Function
+Specify a quantified comparison.
+
+<in predicate> ::= <row value predicand> <in predicate part 2>
+
+<in predicate part 2> ::= [ NOT ] IN <in predicate value>
+
+<in predicate value> ::=
+    <table subquery>
+  | <left paren> <in value list> <right paren>
+
+<in value list> ::=
+  <row value expression> [ { <comma> <row value expression> }... ]
+-}
+
+inPredicate :: TestItem
+inPredicate = Group "in predicate"
+    [-- todo: in predicate
+    ]
+
+{-
+== 8.5 <like predicate>
+
+Function
+Specify a pattern-match comparison.
+
+<like predicate> ::= <character like predicate> | <octet like predicate>
+
+<character like predicate> ::=
+  <row value predicand> <character like predicate part 2>
+
+<character like predicate part 2> ::=
+  [ NOT ] LIKE <character pattern> [ ESCAPE <escape character> ]
+
+<character pattern> ::= <character value expression>
+
+<escape character> ::= <character value expression>
+
+<octet like predicate> ::= <row value predicand> <octet like predicate part 2>
+
+<octet like predicate part 2> ::=
+  [ NOT ] LIKE <octet pattern> [ ESCAPE <escape octet> ]
+
+<octet pattern> ::= <binary value expression>
+
+<escape octet> ::= <binary value expression>
+-}
+
+likePredicate :: TestItem
+likePredicate = Group "like predicate"
+    [-- todo: like predicate
+    ]
+
+{-
+== 8.6 <similar predicate>
+
+Function
+Specify a character string similarity by means of a regular expression.
+
+<similar predicate> ::= <row value predicand> <similar predicate part 2>
+
+<similar predicate part 2> ::=
+  [ NOT ] SIMILAR TO <similar pattern> [ ESCAPE <escape character> ]
+
+<similar pattern> ::= <character value expression>
+
+<regular expression> ::=
+    <regular term>
+  | <regular expression> <vertical bar> <regular term>
+
+<regular term> ::= <regular factor> | <regular term> <regular factor>
+
+<regular factor> ::=
+    <regular primary>
+  | <regular primary> <asterisk>
+  | <regular primary> <plus sign>
+  | <regular primary> <question mark>
+  | <regular primary> <repeat factor>
+
+<repeat factor> ::= <left brace> <low value> [ <upper limit> ] <right brace>
+
+<upper limit> ::= <comma> [ <high value> ]
+
+<low value> ::= <unsigned integer>
+
+<high value> ::= <unsigned integer>
+
+<regular primary> ::=
+    <character specifier>
+  | <percent>
+  | <regular character set>
+  | <left paren> <regular expression> <right paren>
+
+<character specifier> ::= <non-escaped character> | <escaped character>
+
+<non-escaped character> ::= !! See the Syntax Rules.
+
+<escaped character> ::= !! See the Syntax Rules.
+
+<regular character set> ::=
+    <underscore>
+  | <left bracket> <character enumeration>... <right bracket>
+  | <left bracket> <circumflex> <character enumeration>... <right bracket>
+  | <left bracket> <character enumeration include>...
+      <circumflex> <character enumeration exclude>... <right bracket>
+
+<character enumeration include> ::= <character enumeration>
+
+<character enumeration exclude> ::= <character enumeration>
+
+<character enumeration> ::=
+    <character specifier>
+  | <character specifier> <minus sign> <character specifier>
+  | <left bracket> <colon> <regular character set identifier> <colon> <right bracket>
+
+<regular character set identifier> ::= <identifier>
+-}
+
+similarPredicate :: TestItem
+similarPredicate = Group "similar predicate"
+    [-- todo: similar predicate
+    ]
+
+
+{-
+== 8.7 <regex like predicate>
+
+Function
+Specify a pattern-match comparison using an XQuery regular expression.
+
+<regex like predicate> ::= <row value predicand> <regex like predicate part 2>
+
+<regex like predicate part 2> ::=
+  [ NOT ] LIKE_REGEX <XQuery pattern> [ FLAG <XQuery option flag> ]
+-}
+
+regexLikePredicate :: TestItem
+regexLikePredicate = Group "regex like predicate"
+    [-- todo: regex like predicate
+    ]
+
+{-
+== 8.8 <null predicate>
+
+Function
+Specify a test for a null value.
+
+<null predicate> ::= <row value predicand> <null predicate part 2>
+
+<null predicate part 2> ::= IS [ NOT ] NULL
+-}
+
+nullPredicate :: TestItem
+nullPredicate = Group "null predicate"
+    [-- todo: null predicate
+    ]
+
+{-
+== 8.9 <quantified comparison predicate>
+
+Function
+Specify a quantified comparison.
+
+<quantified comparison predicate> ::=
+  <row value predicand> <quantified comparison predicate part 2>
+
+<quantified comparison predicate part 2> ::=
+  <comp op> <quantifier> <table subquery>
+
+<quantifier> ::= <all> | <some>
+
+<all> ::= ALL
+
+<some> ::= SOME | ANY
+-}
+
+quantifiedComparisonPredicate :: TestItem
+quantifiedComparisonPredicate = Group "quantified comparison predicate"
+    $ map (uncurry (TestScalarExpr ansi2011))
+
+    [("a = any (select * from t)"
+     ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "="] CPAny ms)
+    ,("a <= some (select * from t)"
+     ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "<="] CPSome ms)
+    ,("a > all (select * from t)"
+     ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing ">"] CPAll ms)
+    ,("(a,b) <> all (select * from t)"
+     ,QuantifiedComparison
+      (SpecialOp [Name Nothing "rowctor"] [Iden [Name Nothing "a"]
+                                  ,Iden [Name Nothing "b"]]) [Name Nothing "<>"] CPAll ms)
+    ]
+  where
+    ms = toQueryExpr $ makeSelect
+         {msSelectList = [(Star,Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]}
+
+{-
+== 8.10 <exists predicate>
+
+Function
+Specify a test for a non-empty set.
+
+<exists predicate> ::= EXISTS <table subquery>
+-}
+
+existsPredicate :: TestItem
+existsPredicate = Group "exists predicate"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("exists(select * from t where a = 4)"
+     ,SubQueryExpr SqExists
+      $ toQueryExpr $ makeSelect
+        {msSelectList = [(Star,Nothing)]
+        ,msFrom = [TRSimple [Name Nothing "t"]]
+        ,msWhere = Just (BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "4"))
+        }
+     )]
+
+{-
+== 8.11 <unique predicate>
+
+Function
+Specify a test for the absence of duplicate rows.
+
+<unique predicate> ::= UNIQUE <table subquery>
+-}
+
+uniquePredicate :: TestItem
+uniquePredicate = Group "unique predicate"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("unique(select * from t where a = 4)"
+     ,SubQueryExpr SqUnique
+      $ toQueryExpr $ makeSelect
+        {msSelectList = [(Star,Nothing)]
+        ,msFrom = [TRSimple [Name Nothing "t"]]
+        ,msWhere = Just (BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "4"))
+        }
+     )]
+
+{-
+== 8.12 <normalized predicate>
+
+Function
+Determine whether a character string value is normalized.
+
+<normalized predicate> ::= <row value predicand> <normalized predicate part 2>
+
+<normalized predicate part 2> ::= IS [ NOT ] [ <normal form> ] NORMALIZED
+-}
+
+normalizedPredicate :: TestItem
+normalizedPredicate = Group "normalized predicate"
+    [-- todo: normalized predicate
+    ]
+
+{-
+== 8.13 <match predicate>
+
+Function
+Specify a test for matching rows.
+
+<match predicate> ::= <row value predicand> <match predicate part 2>
+
+<match predicate part 2> ::=
+  MATCH [ UNIQUE ] [ SIMPLE | PARTIAL | FULL ] <table subquery>
+-}
+
+matchPredicate :: TestItem
+matchPredicate = Group "match predicate"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("a match (select a from t)"
+     ,Match (Iden [Name Nothing "a"]) False $ toQueryExpr ms)
+    ,("(a,b) match (select a,b from t)"
+     ,Match (SpecialOp [Name Nothing "rowctor"]
+             [Iden [Name Nothing "a"], Iden [Name Nothing "b"]]) False $ toQueryExpr msa)
+    ,("(a,b) match unique (select a,b from t)"
+     ,Match (SpecialOp [Name Nothing "rowctor"]
+             [Iden [Name Nothing "a"], Iden [Name Nothing "b"]]) True $ toQueryExpr msa)
+    ]
+  where
+    ms = makeSelect
+         {msSelectList = [(Iden [Name Nothing "a"],Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]}
+    msa = ms {msSelectList = msSelectList ms
+                             <> [(Iden [Name Nothing "b"],Nothing)]}
+
+{-
+TODO: simple, partial and full
+
+== 8.14 <overlaps predicate>
+
+Function
+Specify a test for an overlap between two datetime periods.
+
+<overlaps predicate> ::=
+  <overlaps predicate part 1> <overlaps predicate part 2>
+
+<overlaps predicate part 1> ::= <row value predicand 1>
+
+<overlaps predicate part 2> ::= OVERLAPS <row value predicand 2>
+
+<row value predicand 1> ::= <row value predicand>
+
+<row value predicand 2> ::= <row value predicand>
+-}
+
+overlapsPredicate :: TestItem
+overlapsPredicate = Group "overlaps predicate"
+    [-- todo: overlaps predicate
+    ]
+
+{-
+== 8.15 <distinct predicate>
+
+Function
+Specify a test of whether two row values are distinct
+
+<distinct predicate> ::= <row value predicand 3> <distinct predicate part 2>
+
+<distinct predicate part 2> ::=
+  IS [ NOT ] DISTINCT FROM <row value predicand 4>
+
+<row value predicand 3> ::= <row value predicand>
+
+<row value predicand 4> ::= <row value predicand>
+-}
+
+distinctPredicate :: TestItem
+distinctPredicate = Group "distinct predicate"
+    [-- todo: distinct predicate
+    ]
+
+{-
+== 8.16 <member predicate>
+
+Function
+Specify a test of whether a value is a member of a multiset.
+
+<member predicate> ::= <row value predicand> <member predicate part 2>
+
+<member predicate part 2> ::= [ NOT ] MEMBER [ OF ] <multiset value expression>
+-}
+
+memberPredicate :: TestItem
+memberPredicate = Group "member predicate"
+    [-- todo: member predicate
+    ]
+
+{-
+== 8.17 <submultiset predicate>
+
+Function
+Specify a test of whether a multiset is a submultiset of another multiset.
+
+<submultiset predicate> ::=
+  <row value predicand> <submultiset predicate part 2>
+
+<submultiset predicate part 2> ::=
+  [ NOT ] SUBMULTISET [ OF ] <multiset value expression>
+-}
+
+submultisetPredicate :: TestItem
+submultisetPredicate = Group "submultiset predicate"
+    [-- todo: submultiset predicate
+    ]
+
+{-
+== 8.18 <set predicate>
+
+Function
+
+Specify a test of whether a multiset is a set (that is, does not
+contain any duplicates).
+
+<set predicate> ::= <row value predicand> <set predicate part 2>
+
+<set predicate part 2> ::= IS [ NOT ] A SET
+-}
+
+setPredicate :: TestItem
+setPredicate = Group "set predicate"
+    [-- todo: set predicate
+    ]
+
+{-
+== 8.19 <type predicate>
+
+Function
+Specify a type test.
+
+<type predicate> ::= <row value predicand> <type predicate part 2>
+
+<type predicate part 2> ::=
+  IS [ NOT ] OF <left paren> <type list> <right paren>
+
+<type list> ::=
+  <user-defined type specification>
+      [ { <comma> <user-defined type specification> }... ]
+
+<user-defined type specification> ::=
+    <inclusive user-defined type specification>
+  | <exclusive user-defined type specification>
+
+<inclusive user-defined type specification> ::=
+  <path-resolved user-defined type name>
+
+<exclusive user-defined type specification> ::=
+  ONLY <path-resolved user-defined type name>
+
+TODO: type predicate
+
+== 8.20 <period predicate>
+
+Function
+Specify a test to determine the relationship between periods.
+
+<period predicate> ::=
+    <period overlaps predicate>
+  | <period equals predicate>
+  | <period contains predicate>
+  | <period precedes predicate>
+  | <period succeeds predicate>
+  | <period immediately precedes predicate>
+  | <period immediately succeeds predicate>
+
+<period overlaps predicate> ::=
+  <period predicand 1> <period overlaps predicate part 2>
+
+<period overlaps predicate part 2> ::= OVERLAPS <period predicand 2>
+
+<period predicand 1> ::= <period predicand>
+
+<period predicand 2> ::= <period predicand>
+
+<period predicand> ::=
+    <period reference>
+  | PERIOD <left paren> <period start value> <comma> <period end value> <right paren>
+
+<period reference> ::= <basic identifier chain>
+
+<period start value> ::= <datetime value expression>
+
+<period end value> ::= <datetime value expression>
+
+<period equals predicate> ::=
+  <period predicand 1> <period equals predicate part 2>
+
+<period equals predicate part 2> ::= EQUALS <period predicand 2>
+
+<period contains predicate> ::=
+  <period predicand 1> <period contains predicate part 2>
+
+<period contains predicate part 2> ::=
+  CONTAINS <period or point-in-time predicand>
+
+<period or point-in-time predicand> ::=
+    <period predicand>
+  | <datetime value expression>
+
+<period precedes predicate> ::=
+  <period predicand 1> <period precedes predicate part 2>
+
+<period precedes predicate part 2> ::= PRECEDES <period predicand 2>
+
+<period succeeds predicate> ::=
+  <period predicand 1> <period succeeds predicate part 2>
+
+<period succeeds predicate part 2> ::= SUCCEEDS <period predicand 2>
+
+<period immediately precedes predicate> ::=
+  <period predicand 1> <period immediately precedes predicate part 2>
+
+<period immediately precedes predicate part 2> ::=
+  IMMEDIATELY PRECEDES <period predicand 2>
+
+<period immediately succeeds predicate> ::=
+  <period predicand 1> <period immediately succeeds predicate part 2>
+
+<period immediately succeeds predicate part 2> ::=
+  IMMEDIATELY SUCCEEDS <period predicand 2>
+-}
+
+periodPredicate :: TestItem
+periodPredicate = Group "period predicate"
+    [-- todo: period predicate
+    ]
+
+{-
+== 8.21 <search condition>
+
+Function
+
+Specify a condition that is True, False, or Unknown, depending on the
+value of a <boolean value expression>.
+
+<search condition> ::= <boolean value expression>
+
+= 10 Additional common elements
+
+== 10.1 <interval qualifier>
+
+Function
+Specify the precision of an interval data type.
+
+<interval qualifier> ::= <start field> TO <end field> | <single datetime field>
+
+<start field> ::=
+  <non-second primary datetime field>
+      [ <left paren> <interval leading field precision> <right paren> ]
+
+<end field> ::=
+    <non-second primary datetime field>
+  | SECOND [ <left paren> <interval fractional seconds precision> <right paren> ]
+
+<single datetime field> ::=
+    <non-second primary datetime field>
+        [ <left paren> <interval leading field precision> <right paren> ]
+  | SECOND [ <left paren> <interval leading field precision>
+      [ <comma> <interval fractional seconds precision> ] <right paren> ]
+
+<primary datetime field> ::= <non-second primary datetime field> | SECOND
+
+<non-second primary datetime field> ::= YEAR | MONTH | DAY | HOUR | MINUTE
+
+<interval fractional seconds precision> ::= <unsigned integer>
+
+<interval leading field precision> ::= <unsigned integer>
+-}
+
+intervalQualifier :: TestItem
+intervalQualifier = Group "interval qualifier"
+    [-- todo: interval qualifier
+    ]
+
+{-
+todo: also test all of these in the typenames and in the interval
+literal tests
+
+== 10.2 <language clause>
+
+Function
+Specify a programming language.
+
+<language clause> ::= LANGUAGE <language name>
+
+<language name> ::= ADA | C | COBOL | FORTRAN | M | MUMPS | PASCAL | PLI | SQL
+
+== 10.3 <path specification>
+
+Function
+Specify an order for searching for an SQL-invoked routine.
+
+<path specification> ::= PATH <schema name list>
+
+<schema name list> ::= <schema name> [ { <comma> <schema name> }... ]
+
+== 10.4 <routine invocation>
+
+Function
+Invoke an SQL-invoked routine.
+
+<routine invocation> ::= <routine name> <SQL argument list>
+
+<routine name> ::= [ <schema name> <period> ] <qualified identifier>
+
+<SQL argument list> ::=
+  <left paren> [ <SQL argument> [ { <comma> <SQL argument> }... ] ] <right paren>
+
+<SQL argument> ::=
+    <value expression>
+  | <generalized expression>
+  | <target specification>
+  | <contextually typed value specification>
+  | <named argument specification>
+
+<generalized expression> ::=
+  <value expression> AS <path-resolved user-defined type name>
+
+<named argument specification> ::=
+  <SQL parameter name> <named argument assignment token>
+      <named argument SQL argument>
+
+<named argument SQL argument> ::=
+    <value expression>
+  | <target specification>
+     | <contextually typed value specification>
+
+== 10.5 <character set specification>
+
+Function
+Identify a character set.
+
+<character set specification> ::=
+    <standard character set name>
+  | <implementation-defined character set name>
+  | <user-defined character set name>
+
+<standard character set name> ::= <character set name>
+
+<implementation-defined character set name> ::= <character set name>
+
+<user-defined character set name> ::= <character set name>
+
+tested in the type names
+
+== 10.6 <specific routine designator>
+
+Function
+Specify an SQL-invoked routine.
+
+<specific routine designator> ::=
+    SPECIFIC <routine type> <specific name>
+  | <routine type> <member name> [ FOR <schema-resolved user-defined type name> ]
+
+<routine type> ::=
+    ROUTINE
+  | FUNCTION
+  | PROCEDURE
+  | [ INSTANCE | STATIC | CONSTRUCTOR ] METHOD
+
+<member name> ::= <member name alternatives> [ <data type list> ]
+
+<member name alternatives> ::= <schema qualified routine name> | <method name>
+
+<data type list> ::=
+  <left paren> [ <data type> [ { <comma> <data type> }... ] ] <right paren>
+
+== 10.7 <collate clause>
+
+Function
+Specify a default collation.
+
+<collate clause> ::= COLLATE <collation name>
+-}
+
+collateClause :: TestItem
+collateClause = Group "collate clause"
+    $ map (uncurry (TestScalarExpr ansi2011))
+    [("a collate my_collation"
+     ,Collate (Iden [Name Nothing "a"]) [Name Nothing "my_collation"])]
+
+{-
+== 10.8 <constraint name definition> and <constraint characteristics>
+
+Function
+Specify the name of a constraint and its characteristics.
+
+<constraint name definition> ::= CONSTRAINT <constraint name>
+
+<constraint characteristics> ::=
+    <constraint check time> [ [ NOT ] DEFERRABLE ] [ <constraint enforcement> ]
+  | [ NOT ] DEFERRABLE [ <constraint check time> ] [ <constraint enforcement> ]
+  | <constraint enforcement>
+
+<constraint check time> ::= INITIALLY DEFERRED | INITIALLY IMMEDIATE
+
+<constraint enforcement> ::= [ NOT ] ENFORCED
+
+== 10.9 <aggregate function>
+
+Function
+Specify a value computed from a collection of rows.
+
+<aggregate function> ::=
+    COUNT <left paren> <asterisk> <right paren> [ <filter clause> ]
+  | <general set function> [ <filter clause> ]
+  | <binary set function> [ <filter clause> ]
+  | <ordered set function> [ <filter clause> ]
+  | <array aggregate function> [ <filter clause> ]
+
+<general set function> ::=
+  <set function type> <left paren> [ <set quantifier> ]
+      <value expression> <right paren>
+
+<set function type> ::= <computational operation>
+
+<computational operation> ::=
+    AVG
+  | MAX
+  | MIN
+  | SUM
+  | EVERY
+  | ANY
+  | SOME
+  | COUNT
+  | STDDEV_POP
+  | STDDEV_SAMP
+  | VAR_SAMP
+  | VAR_POP
+  | COLLECT
+  | FUSION
+  | INTERSECTION
+
+<set quantifier> ::= DISTINCT | ALL
+
+<filter clause> ::= FILTER <left paren> WHERE <search condition> <right paren>
+
+<binary set function> ::=
+  <binary set function type> <left paren> <dependent variable expression> <comma>
+      <independent variable expression> <right paren>
+
+<binary set function type> ::=
+    COVAR_POP
+  | COVAR_SAMP
+  | CORR
+  | REGR_SLOPE
+  | REGR_INTERCEPT
+  | REGR_COUNT
+  | REGR_R2
+  | REGR_AVGX
+  | REGR_AVGY
+  | REGR_SXX
+  | REGR_SYY
+  | REGR_SXY
+
+<dependent variable expression> ::= <numeric value expression>
+
+<independent variable expression> ::= <numeric value expression>
+
+<ordered set function> ::=
+    <hypothetical set function>
+  | <inverse distribution function>
+
+<hypothetical set function> ::=
+  <rank function type> <left paren>
+      <hypothetical set function value expression list> <right paren>
+      <within group specification>
+
+<within group specification> ::=
+  WITHIN GROUP <left paren> ORDER BY <sort specification list> <right paren>
+
+<hypothetical set function value expression list> ::=
+  <value expression> [ { <comma> <value expression> }... ]
+
+<inverse distribution function> ::=
+  <inverse distribution function type> <left paren>
+      <inverse distribution function argument> <right paren>
+      <within group specification>
+
+<inverse distribution function argument> ::= <numeric value expression>
+
+<inverse distribution function type> ::= PERCENTILE_CONT | PERCENTILE_DISC
+
+<array aggregate function> ::=
+  ARRAY_AGG
+      <left paren> <value expression> [ ORDER BY <sort specification list> ] <right paren>
+-}
+
+aggregateFunction :: TestItem
+aggregateFunction = Group "aggregate function"
+    $ map (uncurry (TestScalarExpr ansi2011)) $
+    [("count(*)",App [Name Nothing "count"] [Star])
+    ,("count(*) filter (where something > 5)"
+     ,AggregateApp [Name Nothing "count"] SQDefault [Star] [] fil)
+
+-- gsf
+
+    ,("count(a)",App [Name Nothing "count"] [Iden [Name Nothing "a"]])
+    ,("count(distinct a)"
+     ,AggregateApp [Name Nothing "count"]
+                   Distinct
+                   [Iden [Name Nothing "a"]] [] Nothing)
+    ,("count(all a)"
+     ,AggregateApp [Name Nothing "count"]
+                   All
+                   [Iden [Name Nothing "a"]] [] Nothing)
+    ,("count(all a) filter (where something > 5)"
+     ,AggregateApp [Name Nothing "count"]
+                   All
+                   [Iden [Name Nothing "a"]] [] fil)
+    ] <> concatMap mkSimpleAgg
+         ["avg","max","min","sum"
+         ,"every", "any", "some"
+         ,"stddev_pop","stddev_samp","var_samp","var_pop"
+         ,"collect","fusion","intersection"]
+
+-- bsf
+
+    <> concatMap mkBsf
+         ["COVAR_POP","COVAR_SAMP","CORR","REGR_SLOPE"
+          ,"REGR_INTERCEPT","REGR_COUNT","REGR_R2"
+          ,"REGR_AVGX","REGR_AVGY"
+          ,"REGR_SXX","REGR_SYY","REGR_SXY"]
+
+-- osf
+
+    <>
+    [("rank(a,c) within group (order by b)"
+     ,AggregateAppGroup [Name Nothing "rank"]
+          [Iden [Name Nothing "a"], Iden [Name Nothing "c"]]
+          ob)]
+    <> map mkGp ["dense_rank","percent_rank"
+                ,"cume_dist", "percentile_cont"
+                ,"percentile_disc"]
+    <> [("array_agg(a)", App [Name Nothing "array_agg"] [Iden [Name Nothing "a"]])
+       ,("array_agg(a order by z)"
+        ,AggregateApp [Name Nothing "array_agg"]
+                       SQDefault
+                       [Iden [Name Nothing "a"]]
+                       [SortSpec (Iden [Name Nothing "z"])
+                            DirDefault NullsOrderDefault]
+                       Nothing)]
+
+  where
+    fil = Just $ BinOp (Iden [Name Nothing "something"]) [Name Nothing ">"] (NumLit "5")
+    ob = [SortSpec (Iden [Name Nothing "b"]) DirDefault NullsOrderDefault]
+    mkGp nm = (nm <> "(a) within group (order by b)"
+              ,AggregateAppGroup [Name Nothing nm]
+               [Iden [Name Nothing "a"]]
+               ob)
+
+    mkSimpleAgg nm =
+        [(nm <> "(a)",App [Name Nothing nm] [Iden [Name Nothing "a"]])
+        ,(nm <> "(distinct a)"
+         ,AggregateApp [Name Nothing nm]
+                       Distinct
+                       [Iden [Name Nothing "a"]] [] Nothing)]
+    mkBsf nm =
+        [(nm <> "(a,b)",App [Name Nothing nm] [Iden [Name Nothing "a"],Iden [Name Nothing "b"]])
+        ,(nm <> "(a,b) filter (where something > 5)"
+          ,AggregateApp [Name Nothing nm]
+                        SQDefault
+                        [Iden [Name Nothing "a"],Iden [Name Nothing "b"]] [] fil)]
+
+{-
+== 10.10 <sort specification list>
+
+Function
+Specify a sort order.
+
+<sort specification list> ::=
+  <sort specification> [ { <comma> <sort specification> }... ]
+
+<sort specification> ::=
+  <sort key> [ <ordering specification> ] [ <null ordering> ]
+
+<sort key> ::= <value expression>
+
+<ordering specification> ::= ASC | DESC
+
+<null ordering> ::=
+  | NULLS LAST
+    NULLS FIRST
+-}
+
+sortSpecificationList :: TestItem
+sortSpecificationList = Group "sort specification list"
+    $ map (uncurry (TestQueryExpr ansi2011))
+    [("select * from t order by a"
+     ,toQueryExpr $ ms {msOrderBy = [SortSpec (Iden [Name Nothing "a"])
+                           DirDefault NullsOrderDefault]})
+    ,("select * from t order by a,b"
+     ,toQueryExpr $ ms {msOrderBy = [SortSpec (Iden [Name Nothing "a"])
+                           DirDefault NullsOrderDefault
+                      ,SortSpec (Iden [Name Nothing "b"])
+                           DirDefault NullsOrderDefault]})
+    ,("select * from t order by a asc,b"
+     ,toQueryExpr $ ms {msOrderBy = [SortSpec (Iden [Name Nothing "a"])
+                           Asc NullsOrderDefault
+                      ,SortSpec (Iden [Name Nothing "b"])
+                           DirDefault NullsOrderDefault]})
+    ,("select * from t order by a desc,b"
+     ,toQueryExpr $ ms {msOrderBy = [SortSpec (Iden [Name Nothing "a"])
+                           Desc NullsOrderDefault
+                      ,SortSpec (Iden [Name Nothing "b"])
+                           DirDefault NullsOrderDefault]})
+    ,("select * from t order by a collate x desc,b"
+     ,toQueryExpr $ ms {msOrderBy = [SortSpec
+                           (Collate (Iden [Name Nothing "a"]) [Name Nothing "x"])
+                           Desc NullsOrderDefault
+                      ,SortSpec (Iden [Name Nothing "b"])
+                           DirDefault NullsOrderDefault]})
+    ,("select * from t order by 1,2"
+     ,toQueryExpr $ ms {msOrderBy = [SortSpec (NumLit "1")
+                           DirDefault NullsOrderDefault
+                      ,SortSpec (NumLit "2")
+                           DirDefault NullsOrderDefault]})
+    ]
+  where
+    ms = makeSelect
+         {msSelectList = [(Star,Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]}
diff --git a/tests/Language/SQL/SimpleSQL/SQL2011Schema.hs b/tests/Language/SQL/SimpleSQL/SQL2011Schema.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/SQL2011Schema.hs
@@ -0,0 +1,2158 @@
+
+{-
+Section 11 in Foundation
+
+This module covers the tests for parsing schema and DDL statements.
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.SQL2011Schema (sql2011SchemaTests) where
+
+import Language.SQL.SimpleSQL.TestTypes
+import Language.SQL.SimpleSQL.Syntax
+
+sql2011SchemaTests :: TestItem
+sql2011SchemaTests = Group "sql 2011 schema tests"
+    [
+
+
+{-
+11.1 <schema definition>
+
+<schema definition> ::=
+  CREATE SCHEMA <schema name clause>
+      [ <schema character set or path> ]
+      [ <schema element>... ]
+-}
+
+     (TestStatement ansi2011 "create schema my_schema"
+     $ CreateSchema [Name Nothing "my_schema"])
+
+{-
+todo: schema name can have .
+schema name can be quoted iden or unicode quoted iden
+add schema element support:
+  create a list of schema elements
+  then do pairwise combinations in schema element list to test
+
+
+<schema character set or path> ::=
+    <schema character set specification>
+  | <schema path specification>
+  | <schema character set specification> <schema path specification>
+  | <schema path specification> <schema character set specification>
+
+<schema name clause> ::=
+    <schema name>
+  | AUTHORIZATION <schema authorization identifier>
+  | <schema name> AUTHORIZATION <schema authorization identifier>
+
+<schema authorization identifier> ::=
+  <authorization identifier>
+
+<schema character set specification> ::=
+  DEFAULT CHARACTER SET <character set specification>
+
+<schema path specification> ::=
+  <path specification>
+
+<schema element> ::=
+      <table definition>
+  |   <view definition>
+  |   <domain definition>
+  |   <character set definition>
+  |   <collation definition>
+  |   <transliteration definition>
+  |   <assertion definition>
+  |   <trigger definition>
+  |   <user-defined type definition>
+  |   <user-defined cast definition>
+  |   <user-defined ordering definition>
+  |   <transform definition>
+  |   <schema routine>
+  |   <sequence generator definition>
+  |   <grant statement>
+  |   <role definition>
+
+
+11.2 <drop schema statement>
+
+<drop schema statement> ::=
+  DROP SCHEMA <schema name> <drop behavior>
+
+<drop behavior> ::=
+    CASCADE
+  | RESTRICT
+-}
+
+
+    ,(TestStatement ansi2011 "drop schema my_schema"
+     $ DropSchema [Name Nothing "my_schema"] DefaultDropBehaviour)
+    ,(TestStatement ansi2011 "drop schema my_schema cascade"
+     $ DropSchema [Name Nothing "my_schema"] Cascade)
+    ,(TestStatement ansi2011 "drop schema my_schema restrict"
+     $ DropSchema [Name Nothing "my_schema"] Restrict)
+
+{-
+11.3 <table definition>
+
+
+<table definition> ::=
+  CREATE [ <table scope> ] TABLE <table name> <table contents source>
+      [ WITH <system versioning clause> ]
+      [ ON COMMIT <table commit action> ROWS ]
+-}
+
+    ,(TestStatement ansi2011 "create table t (a int, b int);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []])
+
+
+{-
+<table contents source> ::=
+    <table element list>
+  | <typed table clause>
+  | <as subquery clause>
+
+<table scope> ::=
+  <global or local> TEMPORARY
+
+<global or local> ::=
+    GLOBAL
+  | LOCAL
+
+<system versioning clause> ::=
+  SYSTEM VERSIONING
+
+defintely skip
+
+<table commit action> ::=
+    PRESERVE
+  | DELETE
+
+defintely skip
+
+<table element list> ::=
+  <left paren> <table element> [ { <comma> <table element> }... ] <right paren>
+
+<table element> ::=
+    <column definition>
+  | <table period definition>
+  | <table constraint definition>
+  | <like clause>
+
+<typed table clause> ::=
+  OF <path-resolved user-defined type name> [ <subtable clause> ]
+      [ <typed table element list> ]
+
+defintely skip
+
+<typed table element list> ::=
+  <left paren> <typed table element>
+      [ { <comma> <typed table element> }... ] <right paren>
+
+defintely skip
+
+<typed table element> ::=
+    <column options>
+ | <table constraint definition>
+  | <self-referencing column specification>
+
+defintely skip
+
+<self-referencing column specification> ::=
+  REF IS <self-referencing column name> [ <reference generation> ]
+
+defintely skip
+
+<reference generation> ::=
+    SYSTEM GENERATED
+  | USER GENERATED
+  | DERIVED
+
+defintely skip
+
+<self-referencing column name> ::=
+  <column name>
+
+defintely skip
+
+<column options> ::=
+  <column name> WITH OPTIONS <column option list>
+
+defintely skip
+
+<column option list> ::=
+  [ <scope clause> ] [ <default clause> ] [ <column constraint definition>... ]
+
+defintely skip
+
+<subtable clause> ::=
+  UNDER <supertable clause>
+
+defintely skip
+
+<supertable clause> ::=
+  <supertable name>
+
+defintely skip
+
+<supertable name> ::=
+  <table name>
+
+defintely skip
+
+<like clause> ::=
+  LIKE <table name> [ <like options> ]
+
+<like options> ::=
+  <like option>...
+
+<like option> ::=
+    <identity option>
+  | <column default option>
+  | <generation option>
+
+<identity option> ::=
+    INCLUDING IDENTITY
+  | EXCLUDING IDENTITY
+
+<column default option> ::=
+    INCLUDING DEFAULTS
+  | EXCLUDING DEFAULTS
+
+<generation option> ::=
+    INCLUDING GENERATED
+  | EXCLUDING GENERATED
+
+<as subquery clause> ::=
+  [ <left paren> <column name list> <right paren> ] AS <table subquery>
+      <with or without data>
+
+<with or without data> ::=
+    WITH NO DATA
+  | WITH DATA
+
+<table period definition> ::=
+  <system or application time period specification>
+      <left paren> <period begin column name> <comma> <period end column name> <right paren>
+
+defintely skip
+
+<system or application time period specification> ::=
+    <system time period specification>
+  | <application time period specification>
+
+defintely skip
+
+<system time period specification> ::=
+  PERIOD FOR SYSTEM_TIME
+
+defintely skip
+
+<application time period specification> ::=
+  PERIOD FOR <application time period name>
+
+defintely skip
+
+<application time period name> ::=
+  <identifier>
+
+defintely skip
+
+<period begin column name> ::=
+  <column name>
+
+defintely skip
+
+<period end column name> ::=
+  <column name>
+
+defintely skip
+
+
+11.4 <column definition>
+
+<column definition> ::=
+  <column name> [ <data type or domain name> ]
+      [ <default clause> | <identity column specification> | <generation clause>
+      | <system time period start column specification>
+      | <system time period end column specification> ]
+      [ <column constraint definition>... ]
+      [ <collate clause> ]
+
+<data type or domain name> ::=
+    <data type>
+  | <domain name>
+
+<system time period start column specification> ::=
+  <timestamp generation rule> AS ROW START
+
+defintely skip
+
+<system time period end column specification> ::=
+  <timestamp generation rule> AS ROW END
+
+defintely skip
+
+<timestamp generation rule> ::=
+  GENERATED ALWAYS
+
+defintely skip
+
+<column constraint definition> ::=
+  [ <constraint name definition> ] <column constraint> [ <constraint characteristics> ]
+
+<column constraint> ::=
+    NOT NULL
+  | <unique specification>
+  | <references specification>
+  | <check constraint definition>
+
+
+can have more than one
+whitespace separated
+
+one constratint:
+optional name: constraint [Name]
+not null | unique | references | check
+todo: constraint characteristics
+-}
+
+
+    ,(TestStatement ansi2011
+      "create table t (a int not null);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing ColNotNullConstraint]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int constraint a_not_null not null);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef (Just [Name Nothing "a_not_null"]) ColNotNullConstraint]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int unique);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing ColUniqueConstraint]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int primary key);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing (ColPrimaryKeyConstraint False)]])
+
+    ,(TestStatement ansi2011 { diAutoincrement = True }
+      "create table t (a int primary key autoincrement);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing (ColPrimaryKeyConstraint True)]])
+
+{-
+references t(a,b)
+  [ Full |partial| simepl]
+  [perm: on update [cascade | set null | set default | restrict | no action]
+         on delete ""
+-}
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing DefaultReferenceMatch
+         DefaultReferentialAction DefaultReferentialAction]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u(a));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] (Just $ Name Nothing "a") DefaultReferenceMatch
+         DefaultReferentialAction DefaultReferentialAction]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u match full);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing MatchFull
+         DefaultReferentialAction DefaultReferentialAction]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u match partial);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing MatchPartial
+         DefaultReferentialAction DefaultReferentialAction]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u match simple);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing MatchSimple
+         DefaultReferentialAction DefaultReferentialAction]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u on update cascade );"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing DefaultReferenceMatch
+         RefCascade DefaultReferentialAction]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u on update set null );"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing DefaultReferenceMatch
+         RefSetNull DefaultReferentialAction]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u on update set default );"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing DefaultReferenceMatch
+         RefSetDefault DefaultReferentialAction]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u on update no action );"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing DefaultReferenceMatch
+         RefNoAction DefaultReferentialAction]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u on delete cascade );"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing DefaultReferenceMatch
+         DefaultReferentialAction RefCascade]])
+
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u on update cascade on delete restrict );"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing DefaultReferenceMatch
+         RefCascade RefRestrict]])
+
+    ,(TestStatement ansi2011
+      "create table t (a int references u on delete restrict on update cascade );"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing $ ColReferencesConstraint
+         [Name Nothing "u"] Nothing DefaultReferenceMatch
+         RefCascade RefRestrict]])
+
+{-
+TODO: try combinations and permutations of column constraints and
+options
+-}
+
+
+    ,(TestStatement ansi2011
+      "create table t (a int check (a>5));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
+        [ColConstraintDef Nothing
+         (ColCheckConstraint $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5"))]])
+
+
+
+
+
+{-
+<identity column specification> ::=
+  GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY
+      [ <left paren> <common sequence generator options> <right paren> ]
+-}
+
+    ,(TestStatement ansi2011 "create table t (a int generated always as identity);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
+        (Just $ IdentityColumnSpec GeneratedAlways []) []])
+
+    ,(TestStatement ansi2011 "create table t (a int generated by default as identity);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
+        (Just $ IdentityColumnSpec GeneratedByDefault []) []])
+
+
+    ,(TestStatement ansi2011
+      "create table t (a int generated always as identity\n\
+      \  ( start with 5 increment by 5 maxvalue 500 minvalue 5 cycle ));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
+        (Just $ IdentityColumnSpec GeneratedAlways
+         [SGOStartWith 5
+         ,SGOIncrementBy 5
+         ,SGOMaxValue 500
+         ,SGOMinValue 5
+         ,SGOCycle]) []])
+
+    ,(TestStatement ansi2011
+      "create table t (a int generated always as identity\n\
+      \  ( start with -4 no maxvalue no minvalue no cycle ));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
+        (Just $ IdentityColumnSpec GeneratedAlways
+         [SGOStartWith (-4)
+         ,SGONoMaxValue
+         ,SGONoMinValue
+         ,SGONoCycle]) []])
+
+{-
+I think <common sequence generator options> is supposed to just
+whitespace separated. In db2 it seems to be csv, but the grammar here
+just seems to be whitespace separated, and it is just whitespace
+separated in oracle... Not completely sure though. Usually db2 is
+closer than oracle?
+
+generated always (valueexpr)
+
+<generation clause> ::=
+  <generation rule> AS <generation expression>
+
+<generation rule> ::=
+  GENERATED ALWAYS
+
+<generation expression> ::=
+  <left paren> <value expression> <right paren>
+-}
+
+    ,(TestStatement ansi2011
+      "create table t (a int, \n\
+      \                a2 int generated always as (a * 2));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableColumnDef $ ColumnDef (Name Nothing "a2") (TypeName [Name Nothing "int"])
+        (Just $ GenerationClause
+         (BinOp (Iden [Name Nothing "a"]) [Name Nothing "*"] (NumLit "2"))) []])
+
+
+
+{-
+11.5 <default clause>
+
+<default clause> ::=
+  DEFAULT <default option>
+
+<default option> ::=
+    <literal>
+  | <datetime value function>
+  | USER
+  | CURRENT_USER
+  | CURRENT_ROLE
+  | SESSION_USER
+  | SYSTEM_USER
+  | CURRENT_CATALOG
+  | CURRENT_SCHEMA
+  | CURRENT_PATH
+  | <implicitly typed value specification>
+-}
+
+
+    ,(TestStatement ansi2011 "create table t (a int default 0);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
+        (Just $ DefaultClause $ NumLit "0") []])
+
+
+
+{-
+11.6 <table constraint definition>
+
+<table constraint definition> ::=
+  [ <constraint name definition> ] <table constraint>
+      [ <constraint characteristics> ]
+
+<table constraint> ::=
+    <unique constraint definition>
+  | <referential constraint definition>
+  | <check constraint definition>
+
+11.7 <unique constraint definition>
+
+<unique constraint definition> ::=
+    <unique specification> <left paren> <unique column list> [ <comma> <without overlap
+    specification> ] <right paren>
+  | UNIQUE ( VALUE )
+
+<unique specification> ::=
+    UNIQUE
+  | PRIMARY KEY
+
+<unique column list> ::=
+  <column name list>
+-}
+
+    ,(TestStatement ansi2011
+      "create table t (a int, unique (a));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableConstraintDef Nothing $ TableUniqueConstraint [Name Nothing "a"]
+        ])
+
+    ,(TestStatement ansi2011
+      "create table t (a int, constraint a_unique unique (a));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableConstraintDef (Just [Name Nothing "a_unique"]) $
+            TableUniqueConstraint [Name Nothing "a"]
+        ])
+
+-- todo: test permutations of column defs and table constraints
+
+    ,(TestStatement ansi2011
+      "create table t (a int, b int, unique (a,b));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableConstraintDef Nothing $
+            TableUniqueConstraint [Name Nothing "a", Name Nothing "b"]
+        ])
+
+    ,(TestStatement ansi2011
+      "create table t (a int, b int, primary key (a,b));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableConstraintDef Nothing $
+            TablePrimaryKeyConstraint [Name Nothing "a", Name Nothing "b"]
+        ])
+
+
+{-
+<without overlap specification> ::=
+  <application time period name> WITHOUT OVERLAPS
+
+defintely skip
+
+
+11.8 <referential constraint definition>
+
+<referential constraint definition> ::=
+  FOREIGN KEY <left paren> <referencing column list>
+      [ <comma> <referencing period specification> ] <right paren>
+      <references specification>
+-}
+
+
+    ,(TestStatement ansi2011
+      "create table t (a int, b int,\n\
+      \                foreign key (a,b) references u(c,d) match full on update cascade on delete restrict );"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableConstraintDef Nothing $
+            TableReferencesConstraint
+                [Name Nothing "a", Name Nothing "b"]
+                [Name Nothing "u"]
+                (Just [Name Nothing "c", Name Nothing "d"])
+                MatchFull RefCascade RefRestrict
+       ])
+
+    ,(TestStatement ansi2011
+      "create table t (a int,\n\
+      \                constraint tfku1 foreign key (a) references u);"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableConstraintDef (Just [Name Nothing "tfku1"]) $
+            TableReferencesConstraint
+                [Name Nothing "a"]
+                [Name Nothing "u"]
+                Nothing DefaultReferenceMatch
+                DefaultReferentialAction DefaultReferentialAction
+       ])
+
+    ,(TestStatement ansi2011 { diNonCommaSeparatedConstraints = True }
+      "create table t (a int, b int,\n\
+      \                foreign key (a) references u(c)\n\
+      \                foreign key (b) references v(d));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableConstraintDef Nothing $
+            TableReferencesConstraint
+                [Name Nothing "a"]
+                [Name Nothing "u"]
+                (Just [Name Nothing "c"])
+                DefaultReferenceMatch
+                DefaultReferentialAction DefaultReferentialAction
+       ,TableConstraintDef Nothing $
+            TableReferencesConstraint
+                [Name Nothing "b"]
+                [Name Nothing "v"]
+                (Just [Name Nothing "d"])
+                DefaultReferenceMatch
+                DefaultReferentialAction DefaultReferentialAction
+       ])
+
+
+{-
+<references specification> ::=
+  REFERENCES <referenced table and columns>
+      [ MATCH <match type> ] [ <referential triggered action> ]
+
+<match type> ::=
+    FULL
+  | PARTIAL
+  | SIMPLE
+
+<referencing column list> ::=
+  <column name list>
+
+<referencing period specification> ::=
+  PERIOD <application time period name>
+
+defintely skip
+
+<referenced table and columns> ::=
+  <table name> [ <left paren> <referenced column list>
+      [ <comma> <referenced period specification> ] <right paren> ]
+
+<referenced column list> ::=
+  <column name list>
+
+<referenced period specification> ::=
+  PERIOD <application time period name>
+
+defintely skip
+
+<referential triggered action> ::=
+    <update rule> [ <delete rule> ]
+  | <delete rule> [ <update rule> ]
+
+<update rule> ::=
+  ON UPDATE <referential action>
+
+<delete rule> ::=
+  ON DELETE <referential action>
+
+<referential action> ::=
+    CASCADE
+  | SET NULL
+  | SET DEFAULT
+  | RESTRICT
+  | NO ACTION
+
+
+
+11.9 <check constraint definition>
+
+<check  constraint definition> ::=
+     CHECK <left paren> <search condition> <right paren>
+-}
+
+    ,(TestStatement ansi2011
+      "create table t (a int, b int, \n\
+      \                check (a > b));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableConstraintDef Nothing $
+            TableCheckConstraint
+            (BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (Iden [Name Nothing "b"]))
+       ])
+
+
+    ,(TestStatement ansi2011
+      "create table t (a int, b int, \n\
+      \                constraint agtb check (a > b));"
+     $ CreateTable [Name Nothing "t"]
+       [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
+       ,TableConstraintDef (Just [Name Nothing "agtb"]) $
+            TableCheckConstraint
+            (BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (Iden [Name Nothing "b"]))
+       ])
+
+
+{-
+TODO: lots more combos of table elements
+and types and the other bits in a column def
+
+11.10 <alter table statement>
+
+<alter table statement> ::=
+  ALTER TABLE <table name> <alter table action>
+
+<alter table action> ::=
+    <add column definition>
+  | <alter column definition>
+  | <drop column definition>
+  | <add table constraint definition>
+  | <alter table constraint definition>
+  | <drop table constraint definition>
+  | <add table period definition>
+  | <drop table period definition>
+  | <add system versioning clause>
+  | <drop system versioning clause>
+
+11.11 <add column definition>
+
+<add column definition> ::=
+  ADD [ COLUMN ] <column definition>
+
+alter table t add column a int
+alter table t add a int
+alter table t add a int unique not null check (a>0)
+-}
+
+    ,(TestStatement ansi2011
+      "alter table t add column a int"
+     $ AlterTable [Name Nothing "t"] $ AddColumnDef
+       $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
+       )
+
+{-
+todo: more add column
+
+11.12 <alter column definition>
+
+<alter column definition> ::=
+  ALTER [ COLUMN ] <column name> <alter column action>
+
+<alter column action> ::=
+    <set column default clause>
+  | <drop column default clause>
+  | <set column not null clause>
+  | <drop column not null clause>
+  | <add column scope clause>
+  | <drop column scope clause>
+  | <alter column data type clause>
+  | <alter identity column specification>
+  | <drop identity property clause>
+  | <drop column generation expression clause>
+
+
+11.13 <set column default clause>
+
+<set column default clause> ::=
+  SET <default clause>
+-}
+
+
+    ,(TestStatement ansi2011
+      "alter table t alter column c set default 0"
+     $ AlterTable [Name Nothing "t"] $ AlterColumnSetDefault (Name Nothing "c")
+       $ NumLit "0")
+
+{-
+11.14 <drop column default clause>
+
+<drop column default clause> ::=
+  DROP DEFAULT
+-}
+
+    ,(TestStatement ansi2011
+      "alter table t alter column c drop default"
+     $ AlterTable [Name Nothing "t"] $ AlterColumnDropDefault (Name Nothing "c"))
+
+
+{-
+11.15 <set column not null clause>
+
+<set column not null clause> ::=
+  SET NOT NULL
+-}
+
+    ,(TestStatement ansi2011
+      "alter table t alter column c set not null"
+     $ AlterTable [Name Nothing "t"] $ AlterColumnSetNotNull (Name Nothing "c"))
+
+{-
+11.16 <drop column not null clause>
+
+<drop column not null clause> ::=
+  DROP NOT NULL
+-}
+
+    ,(TestStatement ansi2011
+      "alter table t alter column c drop not null"
+     $ AlterTable [Name Nothing "t"] $ AlterColumnDropNotNull (Name Nothing "c"))
+
+{-
+11.17 <add column scope clause>
+
+<add column scope clause> ::=
+  ADD <scope clause>
+
+11.18 <drop column scope clause>
+
+<drop column scope clause> ::=
+  DROP SCOPE <drop behavior>
+
+11.19 <alter column data type clause>
+
+<alter column data type clause> ::=
+  SET DATA TYPE <data type>
+-}
+
+    ,(TestStatement ansi2011
+      "alter table t alter column c set data type int;"
+     $ AlterTable [Name Nothing "t"] $
+       AlterColumnSetDataType (Name Nothing "c") (TypeName [Name Nothing "int"]))
+
+
+
+{-
+11.20 <alter identity column specification>
+
+<alter identity column specification> ::=
+    <set identity column generation clause> [ <alter identity column option>... ]
+  | <alter identity column option>...
+
+<set identity column generation clause> ::=
+  SET GENERATED { ALWAYS | BY DEFAULT }
+
+so you have to write set generated for alter identity?
+and you have to use always or by default
+
+makes no sense: if you just want to restart you have to explicitly set
+the always or by default? you can't just leave it unchanged?
+
+you don't write as identity like with create table, this is wrong:
+
+alter table t alter column c set generated always as identity
+
+but these are ok?
+
+alter table t alter column c set generated always
+
+alter table t alter column c set generated by default
+
+<alter identity column option> ::=
+    <alter sequence generator restart option>
+  | SET <basic sequence generator option>
+
+alter table t alter column c set generated always restart
+alter table t alter column c set generated always restart with 4
+
+you can just write restart
+
+but to write others you have to repeat set? each time?
+
+alter table t alter column c set generated always set increment by 5 set minvalue 0 set maxvalue 5 set cycle restart with 5
+(no set before the restart
+
+in create table, it looks like this:
+
+c int generated generated always as identity (increment by 5 minvalue 0 maxvalue 5 cycle restart with 5)
+
+why gratuituous differences???
+
+is there no way to do this:
+
+alter table t alter column c set generated as (a * 3)
+??
+
+UPDATE: alter sequence uses same syntax as create sequence, which is
+the same sytnax as identity in create table, so overrule the sql
+standard and use the same syntax in alter identity.
+
+PLAN: TODO
+
+don't implement alter table alter column generated now
+
+review the syntax for generated in db2, oracle, sql server, postgres, others?
+
+observe which features are supported, and the consistency between
+create table and alter table
+
+try to find some people to ask if the standard really is this much of
+a mess or I have misunderstood the grammer, or maybe there is a good
+reason for the inconsistencies?
+
+
+11.21 <drop identity property clause>
+
+<drop identity property clause> ::=
+  DROP IDENTITY
+
+alter table t alter column c drop identity
+
+included in the generated plan above
+
+11.22 <drop column generation expression clause>
+
+<drop column generation expression clause> ::=
+  DROP EXPRESSION
+
+alter table t alter column c drop expression
+
+included in the generated plan above
+
+
+11.23 <drop column definition>
+
+<drop column definition> ::=
+  DROP [ COLUMN ] <column name> <drop behavior>
+-}
+
+    ,(TestStatement ansi2011
+      "alter table t drop column c"
+     $ AlterTable [Name Nothing "t"] $
+       DropColumn (Name Nothing "c") DefaultDropBehaviour)
+
+    ,(TestStatement ansi2011
+      "alter table t drop c cascade"
+     $ AlterTable [Name Nothing "t"] $
+       DropColumn (Name Nothing "c") Cascade)
+
+    ,(TestStatement ansi2011
+      "alter table t drop c restrict"
+     $ AlterTable [Name Nothing "t"] $
+       DropColumn (Name Nothing "c") Restrict)
+
+
+
+{-
+11.24 <add table constraint definition>
+
+<add table constraint definition> ::=
+  ADD <table constraint definition>
+-}
+
+    ,(TestStatement ansi2011
+      "alter table t add constraint c unique (a,b)"
+     $ AlterTable [Name Nothing "t"] $
+       AddTableConstraintDef (Just [Name Nothing "c"])
+            $ TableUniqueConstraint [Name Nothing "a", Name Nothing "b"])
+
+    ,(TestStatement ansi2011
+      "alter table t add unique (a,b)"
+     $ AlterTable [Name Nothing "t"] $
+       AddTableConstraintDef Nothing
+            $ TableUniqueConstraint [Name Nothing "a", Name Nothing "b"])
+
+
+{-
+11.25 <alter table constraint definition>
+<alter table constraint definition> ::=
+  ALTER CONSTRAINT <constraint name> <constraint enforcement>
+
+todo
+
+11.26 <drop table constraint definition>
+
+<drop table constraint definition> ::=
+  DROP CONSTRAINT <constraint name> <drop behavior>
+-}
+
+    ,(TestStatement ansi2011
+      "alter table t drop constraint c"
+     $ AlterTable [Name Nothing "t"] $
+       DropTableConstraintDef [Name Nothing "c"] DefaultDropBehaviour)
+
+    ,(TestStatement ansi2011
+      "alter table t drop constraint c restrict"
+     $ AlterTable [Name Nothing "t"] $
+       DropTableConstraintDef [Name Nothing "c"] Restrict)
+
+{-
+11.27 <add table period definition>
+
+<add table period definition> ::=
+  ADD <table period definition> [ <add system time period column list> ]
+
+defintely skip
+
+<add system time period column list> ::=
+  ADD [ COLUMN ] <column definition 1> ADD [ COLUMN ] <column definition 2>
+
+defintely skip
+
+<column definition 1> ::=
+  <column definition>
+
+defintely skip
+
+<column definition 2> ::=
+  <column definition>
+
+defintely skip
+
+11.28 <drop table period definition>
+
+<drop table period definition> ::=
+  DROP <system or application time period specification> <drop behavior>
+
+defintely skip
+
+11.29 <add system versioning clause>
+
+<add system versioning clause> ::=
+  ADD <system versioning clause>
+
+defintely skip
+
+11.30 <drop system versioning clause>
+
+<drop system versioning clause> ::=
+  DROP SYSTEM VERSIONING <drop behavior>
+
+defintely skip
+
+11.31 <drop table statement>
+
+<drop table statement> ::=
+  DROP TABLE <table name> <drop behavior>
+-}
+
+    ,(TestStatement ansi2011
+      "drop table t"
+     $ DropTable [Name Nothing "t"] DefaultDropBehaviour)
+
+    ,(TestStatement ansi2011
+      "drop table t restrict"
+     $ DropTable [Name Nothing "t"] Restrict)
+
+
+{-
+11.32 <view definition>
+
+<view definition> ::=
+  CREATE [ RECURSIVE ] VIEW <table name> <view specification>
+      AS <query expression> [ WITH [ <levels clause> ] CHECK OPTION ]
+
+<view specification> ::=
+    <regular view specification>
+  | <referenceable view specification>
+
+<regular view specification> ::=
+  [ <left paren> <view column list> <right paren> ]
+
+<referenceable view specification> ::=
+  OF <path-resolved user-defined type name> [ <subview clause> ]
+      [ <view element list> ]
+
+<subview clause> ::=
+  UNDER <table name>
+
+<view element list> ::=
+  <left paren> <view element> [ { <comma> <view element> }... ] <right paren>
+
+<view element> ::=
+    <self-referencing column specification>
+  | <view column option>
+
+<view column option> ::=
+  <column name> WITH OPTIONS <scope clause>
+
+<levels clause> ::=
+    CASCADED
+  | LOCAL
+
+<view column list> ::=
+  <column name list>
+-}
+
+    ,(TestStatement ansi2011
+      "create view v as select * from t"
+     $ CreateView False [Name Nothing "v"] Nothing (toQueryExpr $ makeSelect
+         {msSelectList = [(Star, Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]
+         }) Nothing)
+
+
+    ,(TestStatement ansi2011
+      "create recursive view v as select * from t"
+     $ CreateView True [Name Nothing "v"] Nothing (toQueryExpr $ makeSelect
+         {msSelectList = [(Star, Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]
+         }) Nothing)
+
+    ,(TestStatement ansi2011
+      "create view v(a,b) as select * from t"
+     $ CreateView False [Name Nothing "v"] (Just [Name Nothing "a", Name Nothing "b"])
+         (toQueryExpr $ makeSelect
+         {msSelectList = [(Star, Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]
+         }) Nothing)
+
+
+    ,(TestStatement ansi2011
+      "create view v as select * from t with check option"
+     $ CreateView False [Name Nothing "v"] Nothing (toQueryExpr $ makeSelect
+         {msSelectList = [(Star, Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]
+         }) (Just DefaultCheckOption))
+
+    ,(TestStatement ansi2011
+      "create view v as select * from t with cascaded check option"
+     $ CreateView False [Name Nothing "v"] Nothing (toQueryExpr $ makeSelect
+         {msSelectList = [(Star, Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]
+         }) (Just CascadedCheckOption))
+
+    ,(TestStatement ansi2011
+      "create view v as select * from t with local check option"
+     $ CreateView False [Name Nothing "v"] Nothing
+         (toQueryExpr $ makeSelect
+         {msSelectList = [(Star, Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]
+         }) (Just LocalCheckOption))
+
+
+{-
+11.33 <drop view statement>
+
+<drop view statement> ::=
+  DROP VIEW <table name> <drop behavior>
+-}
+
+
+    ,(TestStatement ansi2011
+      "drop view v"
+     $ DropView [Name Nothing "v"] DefaultDropBehaviour)
+
+    ,(TestStatement ansi2011
+      "drop view v cascade"
+     $ DropView [Name Nothing "v"] Cascade)
+
+
+{-
+11.34 <domain definition>
+
+<domain definition> ::=
+  CREATE DOMAIN <domain name> [ AS ] <predefined type>
+      [ <default clause> ]
+      [ <domain constraint>... ]
+      [ <collate clause> ]
+
+<domain constraint> ::=
+  [ <constraint name definition> ] <check constraint definition> [
+      <constraint characteristics> ]
+-}
+
+    ,(TestStatement ansi2011
+      "create domain my_int int"
+     $ CreateDomain [Name Nothing "my_int"]
+          (TypeName [Name Nothing "int"])
+          Nothing [])
+
+    ,(TestStatement ansi2011
+      "create domain my_int as int"
+     $ CreateDomain [Name Nothing "my_int"]
+          (TypeName [Name Nothing "int"])
+          Nothing [])
+
+    ,(TestStatement ansi2011
+      "create domain my_int int default 0"
+     $ CreateDomain [Name Nothing "my_int"]
+          (TypeName [Name Nothing "int"])
+          (Just (NumLit "0")) [])
+
+    ,(TestStatement ansi2011
+      "create domain my_int int check (value > 5)"
+     $ CreateDomain [Name Nothing "my_int"]
+          (TypeName [Name Nothing "int"])
+          Nothing [(Nothing
+                   ,BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "5"))])
+
+    ,(TestStatement ansi2011
+      "create domain my_int int constraint gt5 check (value > 5)"
+     $ CreateDomain [Name Nothing "my_int"]
+          (TypeName [Name Nothing "int"])
+          Nothing [(Just [Name Nothing "gt5"]
+                   ,BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "5"))])
+
+
+
+{-
+11.35 <alter domain statement>
+
+<alter domain statement> ::=
+  ALTER DOMAIN <domain name> <alter domain action>
+
+<alter domain action> ::=
+    <set domain default clause>
+  | <drop domain default clause>
+  | <add domain constraint definition>
+  | <drop domain constraint definition>
+
+11.36 <set domain default clause>
+
+<set domain default clause> ::=
+  SET <default clause>
+-}
+
+    ,(TestStatement ansi2011
+      "alter domain my_int set default 0"
+     $ AlterDomain [Name Nothing "my_int"]
+       $ ADSetDefault $ NumLit "0")
+
+
+{-
+11.37 <drop domain default clause>
+
+<drop domain default clause> ::=
+  DROP DEFAULT
+-}
+
+    ,(TestStatement ansi2011
+      "alter domain my_int drop default"
+     $ AlterDomain [Name Nothing "my_int"]
+       $ ADDropDefault)
+
+
+{-
+11.38 <add domain constraint definition>
+
+<add domain constraint definition> ::=
+  ADD <domain constraint>
+-}
+
+    ,(TestStatement ansi2011
+      "alter domain my_int add check (value > 6)"
+     $ AlterDomain [Name Nothing "my_int"]
+       $ ADAddConstraint Nothing
+         $ BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "6"))
+
+    ,(TestStatement ansi2011
+      "alter domain my_int add constraint gt6 check (value > 6)"
+     $ AlterDomain [Name Nothing "my_int"]
+       $ ADAddConstraint (Just [Name Nothing "gt6"])
+         $ BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "6"))
+
+
+{-
+11.39 <drop domain constraint definition>
+
+<drop domain constraint definition> ::=
+  DROP CONSTRAINT <constraint name>
+-}
+
+    ,(TestStatement ansi2011
+      "alter domain my_int drop constraint gt6"
+     $ AlterDomain [Name Nothing "my_int"]
+       $ ADDropConstraint [Name Nothing "gt6"])
+
+{-
+11.40 <drop domain statement>
+
+<drop domain statement> ::=
+  DROP DOMAIN <domain name> <drop behavior>
+-}
+
+    ,(TestStatement ansi2011
+      "drop domain my_int"
+     $ DropDomain [Name Nothing "my_int"] DefaultDropBehaviour)
+
+    ,(TestStatement ansi2011
+      "drop domain my_int cascade"
+     $ DropDomain [Name Nothing "my_int"] Cascade)
+
+
+
+{-
+11.41 <character set definition>
+
+<character set definition> ::=
+  CREATE CHARACTER SET <character set name> [ AS ]
+      <character set source> [ <collate clause> ]
+
+<character set source> ::=
+  GET <character set specification>
+
+11.42 <drop character set statement>
+
+<drop character set statement> ::=
+  DROP CHARACTER SET <character set name>
+
+11.43 <collation definition>
+
+<collation definition> ::=
+  CREATE COLLATION <collation name> FOR <character set specification>
+      FROM <existing collation name> [ <pad characteristic> ]
+
+<existing collation name> ::=
+  <collation name>
+
+<pad characteristic> ::=
+    NO PAD
+  | PAD SPACE
+
+11.44 <drop collation statement>
+
+<drop collation statement> ::=
+  DROP COLLATION <collation name> <drop behavior>
+
+11.45 <transliteration definition>
+
+<transliteration definition> ::=
+  CREATE TRANSLATION <transliteration name> FOR <source character set specification>
+      TO <target character set specification> FROM <transliteration source>
+
+<source character set specification> ::=
+  <character set specification>
+
+<target character set specification> ::=
+  <character set specification>
+
+<transliteration source> ::=
+    <existing transliteration name>
+  | <transliteration routine>
+
+<existing transliteration name> ::=
+  <transliteration name>
+
+<transliteration routine> ::=
+  <specific routine designator>
+
+11.46 <drop transliteration statement>
+
+<drop transliteration statement> ::=
+  DROP TRANSLATION <transliteration name>
+
+11.47 <assertion definition>
+
+<assertion definition> ::=
+     CREATE ASSERTION <constraint name>
+         CHECK <left paren> <search condition> <right paren>
+         [ <constraint characteristics> ]
+-}
+
+    ,(TestStatement ansi2011
+      "create assertion t1_not_empty CHECK ((select count(*) from t1) > 0);"
+     $ CreateAssertion [Name Nothing "t1_not_empty"]
+        $ BinOp (SubQueryExpr SqSq $
+                 toQueryExpr $ makeSelect
+                 {msSelectList = [(App [Name Nothing "count"] [Star],Nothing)]
+                 ,msFrom = [TRSimple [Name Nothing "t1"]]
+                 })
+                [Name Nothing ">"] (NumLit "0"))
+
+{-
+11.48 <drop assertion statement>
+
+<drop assertion statement> ::=
+  DROP ASSERTION <constraint name> [ <drop behavior> ]
+-}
+
+    ,(TestStatement ansi2011
+      "drop assertion t1_not_empty;"
+     $ DropAssertion [Name Nothing "t1_not_empty"] DefaultDropBehaviour)
+
+    ,(TestStatement ansi2011
+      "drop assertion t1_not_empty cascade;"
+     $ DropAssertion [Name Nothing "t1_not_empty"] Cascade)
+
+
+{-
+11.49 <trigger definition>
+
+<trigger definition> ::=
+  CREATE TRIGGER <trigger name> <trigger action time> <trigger event>
+      ON <table name> [ REFERENCING <transition table or variable list> ]
+      <triggered action>
+
+<trigger action time> ::=
+    BEFORE
+  | AFTER
+  | INSTEAD OF
+
+<trigger event> ::=
+    INSERT
+  | DELETE
+  | UPDATE [ OF <trigger column list> ]
+
+<trigger column list> ::=
+  <column name list>
+
+<triggered action> ::=
+  [ FOR EACH { ROW | STATEMENT } ]
+      [ <triggered when clause> ]
+      <triggered SQL statement>
+
+<triggered when clause> ::=
+  WHEN <left paren> <search condition> <right paren>
+
+<triggered SQL statement> ::=
+    <SQL procedure statement>
+  | BEGIN ATOMIC { <SQL procedure statement> <semicolon> }... END
+
+<transition table or variable list> ::=
+  <transition table or variable>...
+
+<transition table or variable> ::=
+    OLD [ ROW ] [ AS     ] <old transition variable name>
+  | NEW [ ROW ] [ AS     ] <new transition variable name>
+  | OLD TABLE [ AS ]     <old transition table name>
+  | NEW TABLE [ AS ]     <new transition table name>
+
+<old transition table name> ::=
+  <transition table name>
+
+<new transition table name> ::=
+  <transition table name>
+
+<transition table name> ::=
+  <identifier>
+
+<old transition variable name> ::=
+  <correlation name>
+
+<new transition variable name> ::=
+  <correlation name>
+
+11.50 <drop trigger statement>
+
+<drop trigger statement> ::=
+  DROP TRIGGER <trigger name>
+
+11.51 <user-defined type definition>
+
+<user-defined type definition> ::=
+  CREATE TYPE <user-defined type body>
+
+ <user-defined type body> ::=
+  <schema-resolved user-defined type name>
+      [ <subtype clause> ]
+      [ AS <representation> ]
+      [ <user-defined type option list> ]
+      [ <method specification list> ]
+
+<user-defined type option list> ::=
+  <user-defined type option> [ <user-defined type option>... ]
+
+<user-defined type option> ::=
+    <instantiable clause>
+  | <finality>
+  | <reference type specification>
+  | <cast to ref>
+  | <cast to type>
+  | <cast to distinct>
+  | <cast to source>
+
+<subtype clause> ::=
+  UNDER <supertype name>
+
+<supertype name> ::=
+  <path-resolved user-defined type name>
+
+<representation> ::=
+    <predefined type>
+  | <collection type>
+  | <member list>
+
+<member list> ::=
+  <left paren> <member> [ { <comma> <member> }... ] <right paren>
+
+<member> ::=
+  <attribute definition>
+
+<instantiable clause> ::=
+    INSTANTIABLE
+  | NOT INSTANTIABLE
+
+<finality> ::=
+    FINAL
+  | NOT FINAL
+
+<reference type specification> ::=
+    <user-defined representation>
+  | <derived representation>
+  | <system-generated representation>
+
+<user-defined representation> ::=
+  REF USING <predefined type>
+
+<derived representation> ::=
+  REF FROM <list of attributes>
+
+<system-generated representation> ::=
+  REF IS SYSTEM GENERATED
+
+<cast to ref> ::=
+  CAST <left paren> SOURCE AS REF <right paren> WITH <cast to ref identifier>
+
+<cast to ref identifier> ::=
+  <identifier>
+
+<cast to type> ::=
+  CAST <left paren> REF AS SOURCE <right paren> WITH <cast to type identifier>
+
+<cast to type identifier> ::=
+  <identifier>
+
+<list of attributes> ::=
+  <left paren> <attribute name> [ { <comma> <attribute name> }... ] <right paren>
+
+<cast to distinct> ::=
+  CAST <left paren> SOURCE AS DISTINCT <right paren>
+      WITH <cast to distinct identifier>
+
+<cast to distinct identifier> ::=
+  <identifier>
+
+<cast to source> ::=
+  CAST <left paren> DISTINCT AS SOURCE <right paren>
+      WITH <cast to source identifier>
+
+<cast to source identifier> ::=
+  <identifier>
+
+<method specification list> ::=
+  <method specification> [ { <comma> <method specification> }... ]
+
+<method specification> ::=
+    <original method specification>
+  | <overriding method specification>
+
+<original method specification> ::=
+  <partial method specification> [ SELF AS RESULT ] [ SELF AS LOCATOR ]
+      [ <method characteristics> ]
+
+<overriding method specification> ::=
+  OVERRIDING <partial method specification>
+                                                                                1<partial method specification> ::=
+  [ INSTANCE | STATIC | CONSTRUCTOR ]
+      METHOD <method name> <SQL parameter declaration list>
+      <returns clause>
+      [ SPECIFIC <specific method name> ]
+
+<specific method name> ::=
+  [ <schema name> <period> ] <qualified identifier>
+
+<method characteristics> ::=
+  <method characteristic>...
+
+ <method characteristic> ::=
+         <language clause>
+     |   <parameter style clause>
+     |   <deterministic characteristic>
+     |   <SQL-data access indication>
+     |   <null-call clause>
+
+11.52 <attribute definition>
+
+<attribute definition> ::=
+     <attribute name> <data type>
+         [ <attribute default> ]
+         [ <collate clause> ]
+
+<attribute default> ::=
+  <default clause>
+
+11.53 <alter type statement>
+
+<alter type statement> ::=
+  ALTER TYPE <schema-resolved user-defined type name> <alter type action>
+
+<alter type action> ::=
+    <add attribute definition>
+  | <drop attribute definition>
+  | <add original method specification>
+  | <add overriding method specification>
+  | <drop method specification>
+
+11.54 <add attribute definition>
+
+<add attribute definition> ::=
+  ADD ATTRIBUTE <attribute definition>
+
+11.55 <drop attribute definition>
+
+<drop attribute definition> ::=
+  DROP ATTRIBUTE <attribute name> RESTRICT
+
+11.56 <add original method specification>
+
+<add original method specification> ::=
+  ADD <original method specification>
+
+11.57 <add overriding method specification>
+
+<add overriding method specification> ::=
+  ADD <overriding method specification>
+
+11.58 <drop method specification>
+
+<drop method specification> ::=
+  DROP <specific method specification designator> RESTRICT
+
+<specific method specification designator> ::=
+  [ INSTANCE | STATIC | CONSTRUCTOR ]
+      METHOD <method name> <data type list>
+
+11.59 <drop data type statement>
+
+<drop data type statement> ::=
+  DROP TYPE <schema-resolved user-defined type name> <drop behavior>
+
+11.60 <SQL-invoked routine>
+
+<SQL-invoked routine> ::=
+  <schema routine>
+
+<schema routine> ::=
+    <schema procedure>
+  | <schema function>
+
+<schema procedure> ::=
+  CREATE <SQL-invoked procedure>
+
+<schema function> ::=
+  CREATE <SQL-invoked function>
+
+<SQL-invoked procedure> ::=
+  PROCEDURE <schema qualified routine name> <SQL parameter declaration list>
+      <routine characteristics>
+      <routine body>
+
+<SQL-invoked function> ::=
+  { <function specification> | <method specification designator> } <routine body>
+
+<SQL parameter declaration list> ::=
+  <left paren> [ <SQL parameter declaration>
+      [ { <comma> <SQL parameter declaration> }... ] ] <right paren>
+
+<SQL parameter declaration> ::=
+  [ <parameter mode> ]
+      [ <SQL parameter name> ]
+      <parameter type> [ RESULT ]
+      [ DEFAULT <parameter default> ]
+
+<parameter default> ::=
+    <value expression>
+  | <contextually typed value specification>
+
+<parameter mode> ::=
+    IN
+  | OUT
+  | INOUT
+
+<parameter type> ::=
+  <data type> [ <locator indication> ]
+
+<locator indication> ::=
+  AS LOCATOR
+
+<function specification> ::=
+  FUNCTION <schema qualified routine name> <SQL parameter declaration list>
+      <returns clause>
+      <routine characteristics>
+      [ <dispatch clause> ]
+
+<method specification designator> ::=
+    SPECIFIC METHOD <specific method name>
+  | [ INSTANCE | STATIC | CONSTRUCTOR ]
+      METHOD <method name> <SQL parameter declaration list>
+      [ <returns clause> ]
+      FOR <schema-resolved user-defined type name>
+
+<routine characteristics> ::=
+  [ <routine characteristic>... ]
+
+<routine characteristic> ::=
+    <language clause>
+  | <parameter style clause>
+  | SPECIFIC <specific name>
+  | <deterministic characteristic>
+  | <SQL-data access indication>
+  | <null-call clause>
+  | <returned result sets characteristic>
+  | <savepoint level indication>
+
+<savepoint level indication> ::=
+    NEW SAVEPOINT LEVEL
+  | OLD SAVEPOINT LEVEL
+
+<returned result sets characteristic> ::=
+  DYNAMIC RESULT SETS <maximum returned result sets>
+
+<parameter style clause> ::=
+  PARAMETER STYLE <parameter style>
+
+<dispatch clause> ::=
+  STATIC DISPATCH
+
+<returns clause> ::=
+  RETURNS <returns type>
+
+<returns type> ::=
+    <returns data type> [ <result cast> ]
+  | <returns table type>
+
+<returns table type> ::=
+  TABLE <table function column list>
+
+<table function column list> ::=
+  <left paren> <table function column list element>
+      [ { <comma> <table function column list element> }... ] <right paren>
+
+<table function column list element> ::=
+  <column name> <data type>
+
+<result cast> ::=
+  CAST FROM <result cast from type>
+
+<result cast from type> ::=
+  <data type> [ <locator indication> ]
+
+<returns data type> ::=
+  <data type> [ <locator indication> ]
+
+<routine body> ::=
+    <SQL routine spec>
+  | <external body reference>
+
+<SQL routine spec> ::=
+  [ <rights clause> ] <SQL routine body>
+
+<rights clause> ::=
+    SQL SECURITY INVOKER
+  | SQL SECURITY DEFINER
+
+<SQL routine body> ::=
+  <SQL procedure statement>
+
+<external body reference> ::=
+  EXTERNAL [ NAME <external routine name> ]
+      [ <parameter style clause> ]
+      [ <transform group specification> ]
+      [ <external security clause> ]
+
+<external security clause> ::=
+    EXTERNAL SECURITY DEFINER
+  | EXTERNAL SECURITY INVOKER
+  | EXTERNAL SECURITY IMPLEMENTATION DEFINED
+
+<parameter style> ::=
+    SQL
+  | GENERAL
+
+<deterministic characteristic> ::=
+    DETERMINISTIC
+  | NOT DETERMINISTIC
+
+<SQL-data access indication> ::=
+    NO SQL
+  | CONTAINS SQL
+  | READS SQL DATA
+  | MODIFIES SQL DATA
+
+<null-call clause> ::=
+    RETURNS NULL ON NULL INPUT
+  | CALLED ON NULL INPUT
+
+<maximum returned result sets> ::=
+  <unsigned integer>
+
+<transform group specification> ::=
+  TRANSFORM GROUP { <single group specification> | <multiple group specification> }
+
+<single group specification> ::=
+  <group name>
+
+<multiple group specification> ::=
+  <group specification> [ { <comma> <group specification> }... ]
+
+<group specification> ::=
+  <group name> FOR TYPE <path-resolved user-defined type name>
+
+11.61 <alter routine statement>
+
+<alter routine statement> ::=
+  ALTER <specific routine designator>
+      <alter routine characteristics> <alter routine behavior>
+
+<alter routine characteristics> ::=
+  <alter routine characteristic>...
+
+<alter routine characteristic> ::=
+    <language clause>
+  | <parameter style clause>
+  | <SQL-data access indication>
+  | <null-call clause>
+  | <returned result sets characteristic>
+  | NAME <external routine name>
+
+<alter routine behavior> ::=
+  RESTRICT
+
+11.62 <drop routine statement>
+
+<drop routine statement> ::=
+  DROP <specific routine designator> <drop behavior>
+
+11.63 <user-defined cast definition>
+
+<user-defined cast definition> ::=
+  CREATE CAST <left paren> <source data type> AS <target data type> <right paren>
+      WITH <cast function>
+      [ AS ASSIGNMENT ]
+
+<cast function> ::=
+  <specific routine designator>
+
+<source data type> ::=
+  <data type>
+
+<target data type> ::=
+  <data type>
+
+11.64 <drop user-defined cast statement>
+
+<drop user-defined cast statement> ::=
+  DROP CAST <left paren> <source data type> AS <target data type> <right paren>
+      <drop behavior>
+
+11.65 <user-defined ordering definition>
+
+<user-defined ordering definition> ::=
+  CREATE ORDERING FOR <schema-resolved user-defined type name> <ordering form>
+
+<ordering form> ::=
+    <equals ordering form>
+  | <full ordering form>
+
+<equals ordering form> ::=
+  EQUALS ONLY BY <ordering category>
+
+<full ordering form> ::=
+  ORDER FULL BY <ordering category>
+
+<ordering category> ::=
+    <relative category>
+  | <map category>
+  | <state category>
+
+<relative category> ::=
+  RELATIVE WITH <relative function specification>
+
+<map category> ::=
+  MAP WITH <map function specification>
+
+<state category> ::=
+  STATE [ <specific name> ]
+
+<relative function specification> ::=
+  <specific routine designator>
+
+<map function specification> ::=
+  <specific routine designator>
+
+11.66 <drop user-defined ordering statement>
+
+<drop user-defined ordering statement> ::=
+  DROP ORDERING FOR <schema-resolved user-defined type name> <drop behavior>
+
+11.67 <transform definition>
+
+<transform definition> ::=
+  CREATE { TRANSFORM | TRANSFORMS } FOR
+      <schema-resolved user-defined type name> <transform group>...
+
+<transform group> ::=
+  <group name> <left paren> <transform element list> <right paren>
+
+<group name> ::=
+  <identifier>
+
+<transform element list> ::=
+  <transform element> [ <comma> <transform element> ]
+
+<transform element> ::=
+    <to sql>
+  | <from sql>
+
+<to sql> ::=
+  TO SQL WITH <to sql function>
+
+<from sql> ::=
+  FROM SQL WITH <from sql function>
+
+<to sql function> ::=
+  <specific routine designator>
+
+<from sql function> ::=
+  <specific routine designator>
+
+11.68 <alter transform statement>
+
+<alter transform statement> ::=
+  ALTER { TRANSFORM | TRANSFORMS }
+      FOR <schema-resolved user-defined type name> <alter group>...
+
+<alter group> ::=
+  <group name> <left paren> <alter transform action list> <right paren>
+
+<alter transform action list> ::=
+  <alter transform action> [ { <comma> <alter transform action> }... ]
+
+<alter transform action> ::=
+    <add transform element list>
+  | <drop transform element list>
+
+11.69 <add transform element list>
+
+<add transform element list> ::=
+  ADD <left paren> <transform element list> <right paren>
+
+11.70 <drop transform element list>
+
+<drop transform element list> ::=
+  DROP <left paren> <transform kind>
+      [ <comma> <transform kind> ] <drop behavior> <right paren>
+
+<transform kind> ::=
+    TO SQL
+  | FROM SQL
+
+11.71 <drop transform statement>
+
+<drop transform statement> ::=
+  DROP { TRANSFORM | TRANSFORMS } <transforms to be dropped>
+      FOR <schema-resolved user-defined type name> <drop behavior>
+
+<transforms to be dropped> ::=
+    ALL
+  | <transform group element>
+
+<transform group element> ::=
+  <group name>
+
+11.72 <sequence generator definition>
+
+<sequence generator definition> ::=
+  CREATE SEQUENCE <sequence generator name> [ <sequence generator options> ]
+
+<sequence generator options> ::=
+  <sequence generator option>...
+
+<sequence generator option> ::=
+    <sequence generator data type option>
+  | <common sequence generator options>
+
+<common sequence generator options> ::=
+  <common sequence generator option>...
+
+<common sequence generator option> ::=
+    <sequence generator start with option>
+  | <basic sequence generator option>
+
+<basic sequence generator option> ::=
+    <sequence generator increment by option>
+  | <sequence generator maxvalue option>
+  | <sequence generator minvalue option>
+  | <sequence generator cycle option>
+
+<sequence generator data type option> ::=
+  AS <data type>
+
+<sequence generator start with option> ::=
+  START WITH <sequence generator start value>
+
+<sequence generator start value> ::=
+  <signed numeric literal>
+
+<sequence generator increment by option> ::=
+  INCREMENT BY <sequence generator increment>
+
+<sequence generator increment> ::=
+  <signed numeric literal>
+
+<sequence generator maxvalue option> ::=
+    MAXVALUE <sequence generator max value>
+  | NO MAXVALUE
+
+<sequence generator max value> ::=
+  <signed numeric literal>
+
+<sequence generator minvalue option> ::=
+    MINVALUE <sequence generator min value>
+  | NO MINVALUE
+
+<sequence generator min value> ::=
+  <signed numeric literal>
+
+<sequence generator cycle option> ::=
+    CYCLE
+  | NO CYCLE
+-}
+
+    ,(TestStatement ansi2011
+      "create sequence seq"
+     $ CreateSequence [Name Nothing "seq"] [])
+
+    ,(TestStatement ansi2011
+      "create sequence seq as bigint"
+     $ CreateSequence [Name Nothing "seq"]
+        [SGODataType $ TypeName [Name Nothing "bigint"]])
+
+    ,(TestStatement ansi2011
+      "create sequence seq as bigint start with 5"
+     $ CreateSequence [Name Nothing "seq"]
+        [SGOStartWith 5
+        ,SGODataType $ TypeName [Name Nothing "bigint"]
+        ])
+
+
+{-
+11.73 <alter sequence generator statement>
+
+<alter sequence generator statement> ::=
+  ALTER SEQUENCE <sequence generator name> <alter sequence generator options>
+
+<alter sequence generator options> ::=
+  <alter sequence generator option>...
+
+<alter sequence generator option> ::=
+    <alter sequence generator restart option>
+  | <basic sequence generator option>
+
+<alter sequence generator restart option> ::=
+  RESTART [ WITH <sequence generator restart value> ]
+
+<sequence generator restart value> ::=
+  <signed numeric literal>
+-}
+
+    ,(TestStatement ansi2011
+      "alter sequence seq restart"
+     $ AlterSequence [Name Nothing "seq"]
+        [SGORestart Nothing])
+
+    ,(TestStatement ansi2011
+      "alter sequence seq restart with 5"
+     $ AlterSequence [Name Nothing "seq"]
+        [SGORestart $ Just 5])
+
+    ,(TestStatement ansi2011
+      "alter sequence seq restart with 5 increment by 5"
+     $ AlterSequence [Name Nothing "seq"]
+        [SGORestart $ Just 5
+        ,SGOIncrementBy 5])
+
+
+{-
+11.74 <drop sequence generator statement>
+
+<drop sequence generator statement> ::=
+  DROP SEQUENCE <sequence generator name> <drop behavior>
+-}
+
+    ,(TestStatement ansi2011
+      "drop sequence seq"
+     $ DropSequence [Name Nothing "seq"] DefaultDropBehaviour)
+
+    ,(TestStatement ansi2011
+      "drop sequence seq restrict"
+     $ DropSequence [Name Nothing "seq"] Restrict)
+
+
+    ]
diff --git a/tests/Language/SQL/SimpleSQL/ScalarExprs.hs b/tests/Language/SQL/SimpleSQL/ScalarExprs.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/ScalarExprs.hs
@@ -0,0 +1,433 @@
+
+-- 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
+    ,parameter
+    ,dots
+    ,app
+    ,caseexp
+    ,convertfun     
+    ,operators
+    ,parens
+    ,subqueries
+    ,aggregates
+    ,windowFunctions
+    ,functionsWithReservedNames
+    ]
+
+literals :: TestItem
+literals = Group "literals" $ map (uncurry (TestScalarExpr ansi2011))
+    [("3", NumLit "3")
+     ,("3.", NumLit "3.")
+     ,("3.3", NumLit "3.3")
+     ,(".3", NumLit ".3")
+     ,("3.e3", NumLit "3.e3")
+     ,("3.3e3", NumLit "3.3e3")
+     ,(".3e3", NumLit ".3e3")
+     ,("3e3", NumLit "3e3")
+     ,("3e+3", NumLit "3e+3")
+     ,("3e-3", NumLit "3e-3")
+     ,("'string'", StringLit "'" "'" "string")
+     ,("'string with a '' quote'", StringLit "'" "'" "string with a '' quote")
+     ,("'1'", StringLit "'" "'" "1")
+     ,("interval '3' day"
+      ,IntervalLit Nothing "3" (Itf "day" Nothing) Nothing)
+     ,("interval '3' day (3)"
+      ,IntervalLit Nothing "3" (Itf "day" $ Just (3,Nothing)) Nothing)
+     ,("interval '3 weeks'", TypedLit (TypeName [Name Nothing "interval"]) "3 weeks")
+    ]
+
+identifiers :: TestItem
+identifiers = Group "identifiers" $ map (uncurry (TestScalarExpr ansi2011))
+    [("iden1", Iden [Name Nothing "iden1"])
+    --,("t.a", Iden2 "t" "a")
+    ,("\"quoted identifier\"", Iden [Name (Just ("\"","\"")) "quoted identifier"])
+    ,("\"from\"", Iden [Name (Just ("\"","\"")) "from"])
+    ]
+
+star :: TestItem
+star = Group "star" $ map (uncurry (TestScalarExpr ansi2011))
+    [("*", Star)
+    --,("t.*", Star2 "t")
+    --,("ROW(t.*,42)", App "ROW" [Star2 "t", NumLit "42"])
+    ]
+
+parameter :: TestItem
+parameter = Group "parameter"
+    [TestScalarExpr ansi2011 "?" Parameter
+    ,TestScalarExpr postgres "$13" $ PositionalArg 13]
+
+
+dots :: TestItem
+dots = Group "dot" $ map (uncurry (TestScalarExpr ansi2011))
+    [("t.a", Iden [Name Nothing "t",Name Nothing "a"])
+    ,("t.*", BinOp (Iden [Name Nothing "t"]) [Name Nothing "."] Star)
+    ,("a.b.c", Iden [Name Nothing "a",Name Nothing "b",Name Nothing "c"])
+    ,("ROW(t.*,42)", App [Name Nothing "ROW"] [BinOp (Iden [Name Nothing "t"]) [Name Nothing "."] Star, NumLit "42"])
+    ]
+
+app :: TestItem
+app = Group "app" $ map (uncurry (TestScalarExpr ansi2011))
+    [("f()", App [Name Nothing "f"] [])
+    ,("f(a)", App [Name Nothing "f"] [Iden [Name Nothing "a"]])
+    ,("f(a,b)", App [Name Nothing "f"] [Iden [Name Nothing "a"], Iden [Name Nothing "b"]])
+    ]
+
+caseexp :: TestItem
+caseexp = Group "caseexp" $ map (uncurry (TestScalarExpr ansi2011))
+    [("case a when 1 then 2 end"
+     ,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"]
+                              ,NumLit "2")] Nothing)
+
+    ,("case a when 1 then 2 when 3 then 4 end"
+     ,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"], NumLit "2")
+                             ,([NumLit "3"], NumLit "4")] Nothing)
+
+    ,("case a when 1 then 2 when 3 then 4 else 5 end"
+     ,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"], NumLit "2")
+                             ,([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 [Name Nothing "a"]) [Name Nothing "="] (NumLit "1")], NumLit "2")
+                   ,([BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "3")], NumLit "4")]
+                   (Just $ NumLit "5"))
+
+    ,("case a when 1,2 then 10 when 3,4 then 20 end"
+     ,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1",NumLit "2"]
+                               ,NumLit "10")
+                             ,([NumLit "3",NumLit "4"]
+                               ,NumLit "20")]
+                             Nothing)
+
+    ]
+
+convertfun :: TestItem 
+convertfun = Group "convert" $ map (uncurry (TestScalarExpr sqlserver))
+    [("CONVERT(varchar, 25.65)"
+     ,Convert (TypeName [Name Nothing "varchar"]) (NumLit "25.65") Nothing)
+    ,("CONVERT(datetime, '2017-08-25')"
+     ,Convert (TypeName [Name Nothing "datetime"]) (StringLit "'" "'" "2017-08-25") Nothing)
+    ,("CONVERT(varchar, '2017-08-25', 101)"
+     ,Convert (TypeName [Name Nothing "varchar"]) (StringLit "'" "'" "2017-08-25") (Just 101))
+    ]
+
+operators :: TestItem
+operators = Group "operators"
+    [binaryOperators
+    ,unaryOperators
+    ,casts
+    ,miscOps]
+
+binaryOperators :: TestItem
+binaryOperators = Group "binaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
+    [("a + b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"] (Iden [Name Nothing "b"]))
+     -- sanity check fixities
+     -- todo: add more fixity checking
+
+    ,("a + b * c"
+     ,BinOp  (Iden [Name Nothing "a"]) [Name Nothing "+"]
+             (BinOp (Iden [Name Nothing "b"]) [Name Nothing "*"] (Iden [Name Nothing "c"])))
+
+    ,("a * b + c"
+     ,BinOp (BinOp (Iden [Name Nothing "a"]) [Name Nothing "*"] (Iden [Name Nothing "b"]))
+            [Name Nothing "+"] (Iden [Name Nothing "c"]))
+    ]
+
+unaryOperators :: TestItem
+unaryOperators = Group "unaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
+    [("not a", PrefixOp [Name Nothing "not"] $ Iden [Name Nothing "a"])
+    ,("not not a", PrefixOp [Name Nothing "not"] $ PrefixOp [Name Nothing "not"] $ Iden [Name Nothing "a"])
+    ,("+a", PrefixOp [Name Nothing "+"] $ Iden [Name Nothing "a"])
+    ,("-a", PrefixOp [Name Nothing "-"] $ Iden [Name Nothing "a"])
+    ]
+
+
+casts :: TestItem
+casts = Group "operators" $ map (uncurry (TestScalarExpr ansi2011))
+    [("cast('1' as int)"
+     ,Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "int"])
+
+    ,("int '3'"
+     ,TypedLit (TypeName [Name Nothing "int"]) "3")
+
+    ,("cast('1' as double precision)"
+     ,Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "double precision"])
+
+    ,("cast('1' as float(8))"
+     ,Cast (StringLit "'" "'" "1") $ PrecTypeName [Name Nothing "float"] 8)
+
+    ,("cast('1' as decimal(15,2))"
+     ,Cast (StringLit "'" "'" "1") $ PrecScaleTypeName [Name Nothing "decimal"] 15 2)
+
+
+    ,("double precision '3'"
+     ,TypedLit (TypeName [Name Nothing "double precision"]) "3")
+    ]
+
+subqueries :: TestItem
+subqueries = Group "unaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
+    [("exists (select a from t)", SubQueryExpr SqExists ms)
+    ,("(select a from t)", SubQueryExpr SqSq ms)
+
+    ,("a in (select a from t)"
+     ,In True (Iden [Name Nothing "a"]) (InQueryExpr ms))
+
+    ,("a not in (select a from t)"
+     ,In False (Iden [Name Nothing "a"]) (InQueryExpr ms))
+
+    ,("a > all (select a from t)"
+     ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing ">"] CPAll ms)
+
+    ,("a = some (select a from t)"
+     ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "="] CPSome ms)
+
+    ,("a <= any (select a from t)"
+     ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "<="] CPAny ms)
+    ]
+  where
+    ms = toQueryExpr $ makeSelect
+         {msSelectList = [(Iden [Name Nothing "a"],Nothing)]
+         ,msFrom = [TRSimple [Name Nothing "t"]]
+         }
+
+miscOps :: TestItem
+miscOps = Group "unaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
+    [("a in (1,2,3)"
+     ,In True (Iden [Name Nothing "a"]) $ InList $ map NumLit ["1","2","3"])
+
+    ,("a is null", PostfixOp [Name Nothing "is null"] (Iden [Name Nothing "a"]))
+    ,("a is not null", PostfixOp [Name Nothing "is not null"] (Iden [Name Nothing "a"]))
+    ,("a is true", PostfixOp [Name Nothing "is true"] (Iden [Name Nothing "a"]))
+    ,("a is not true", PostfixOp [Name Nothing "is not true"] (Iden [Name Nothing "a"]))
+    ,("a is false", PostfixOp [Name Nothing "is false"] (Iden [Name Nothing "a"]))
+    ,("a is not false", PostfixOp [Name Nothing "is not false"] (Iden [Name Nothing "a"]))
+    ,("a is unknown", PostfixOp [Name Nothing "is unknown"] (Iden [Name Nothing "a"]))
+    ,("a is not unknown", PostfixOp [Name Nothing "is not unknown"] (Iden [Name Nothing "a"]))
+    ,("a is distinct from b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "is distinct from"] (Iden [Name Nothing "b"]))
+
+    ,("a is not distinct from b"
+     ,BinOp (Iden [Name Nothing "a"]) [Name Nothing "is not distinct from"] (Iden [Name Nothing "b"]))
+
+    ,("a like b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "like"] (Iden [Name Nothing "b"]))
+    ,("a not like b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "not like"] (Iden [Name Nothing "b"]))
+    ,("a is similar to b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "is similar to"] (Iden [Name Nothing "b"]))
+
+    ,("a is not similar to b"
+     ,BinOp (Iden [Name Nothing "a"]) [Name Nothing "is not similar to"] (Iden [Name Nothing "b"]))
+
+    ,("a overlaps b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "overlaps"] (Iden [Name Nothing "b"]))
+
+
+-- special operators
+
+    ,("a between b and c", SpecialOp [Name Nothing "between"] [Iden [Name Nothing "a"]
+                                               ,Iden [Name Nothing "b"]
+                                               ,Iden [Name Nothing "c"]])
+
+    ,("a not between b and c", SpecialOp [Name Nothing "not between"] [Iden [Name Nothing "a"]
+                                                       ,Iden [Name Nothing "b"]
+                                                       ,Iden [Name Nothing "c"]])
+    ,("(1,2)"
+     ,SpecialOp [Name Nothing "rowctor"] [NumLit "1", NumLit "2"])
+
+
+-- keyword special operators
+
+    ,("extract(day from t)"
+     , SpecialOpK [Name Nothing "extract"] (Just $ Iden [Name Nothing "day"]) [("from", Iden [Name Nothing "t"])])
+
+    ,("substring(x from 1 for 2)"
+     ,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("from", NumLit "1")
+                                               ,("for", NumLit "2")])
+
+    ,("substring(x from 1)"
+     ,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("from", NumLit "1")])
+
+    ,("substring(x for 2)"
+     ,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("for", NumLit "2")])
+
+    ,("substring(x from 1 for 2 collate C)"
+     ,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"])
+          [("from", NumLit "1")
+          ,("for", Collate (NumLit "2") [Name Nothing "C"])])
+
+-- this doesn't work because of a overlap in the 'in' parser
+
+    ,("POSITION( string1 IN string2 )"
+     ,SpecialOpK [Name Nothing "position"] (Just $ Iden [Name Nothing "string1"]) [("in", Iden [Name Nothing "string2"])])
+
+    ,("CONVERT(char_value USING conversion_char_name)"
+     ,SpecialOpK [Name Nothing "convert"] (Just $ Iden [Name Nothing "char_value"])
+          [("using", Iden [Name Nothing "conversion_char_name"])])
+
+    ,("TRANSLATE(char_value USING translation_name)"
+     ,SpecialOpK [Name Nothing "translate"] (Just $ Iden [Name Nothing "char_value"])
+          [("using", Iden [Name Nothing "translation_name"])])
+
+{-
+OVERLAY(string PLACING embedded_string FROM start
+[FOR length])
+-}
+
+    ,("OVERLAY(string PLACING embedded_string FROM start)"
+     ,SpecialOpK [Name Nothing "overlay"] (Just $ Iden [Name Nothing "string"])
+          [("placing", Iden [Name Nothing "embedded_string"])
+          ,("from", Iden [Name Nothing "start"])])
+
+    ,("OVERLAY(string PLACING embedded_string FROM start FOR length)"
+     ,SpecialOpK [Name Nothing "overlay"] (Just $ Iden [Name Nothing "string"])
+          [("placing", Iden [Name Nothing "embedded_string"])
+          ,("from", Iden [Name Nothing "start"])
+          ,("for", Iden [Name Nothing "length"])])
+
+{-
+TRIM( [ [{LEADING | TRAILING | BOTH}] [removal_char] FROM ]
+target_string
+[COLLATE collation_name] )
+-}
+
+
+
+    ,("trim(from target_string)"
+     ,SpecialOpK [Name Nothing "trim"] Nothing
+          [("both", StringLit "'" "'" " ")
+          ,("from", Iden [Name Nothing "target_string"])])
+
+    ,("trim(leading from target_string)"
+     ,SpecialOpK [Name Nothing "trim"] Nothing
+          [("leading", StringLit "'" "'" " ")
+          ,("from", Iden [Name Nothing "target_string"])])
+
+    ,("trim(trailing from target_string)"
+     ,SpecialOpK [Name Nothing "trim"] Nothing
+          [("trailing", StringLit "'" "'" " ")
+          ,("from", Iden [Name Nothing "target_string"])])
+
+    ,("trim(both from target_string)"
+     ,SpecialOpK [Name Nothing "trim"] Nothing
+          [("both", StringLit "'" "'" " ")
+          ,("from", Iden [Name Nothing "target_string"])])
+
+
+    ,("trim(leading 'x' from target_string)"
+     ,SpecialOpK [Name Nothing "trim"] Nothing
+          [("leading", StringLit "'" "'" "x")
+          ,("from", Iden [Name Nothing "target_string"])])
+
+    ,("trim(trailing 'y' from target_string)"
+     ,SpecialOpK [Name Nothing "trim"] Nothing
+          [("trailing", StringLit "'" "'" "y")
+          ,("from", Iden [Name Nothing "target_string"])])
+
+    ,("trim(both 'z' from target_string collate C)"
+     ,SpecialOpK [Name Nothing "trim"] Nothing
+          [("both", StringLit "'" "'" "z")
+          ,("from", Collate (Iden [Name Nothing "target_string"]) [Name Nothing "C"])])
+
+    ,("trim(leading from target_string)"
+     ,SpecialOpK [Name Nothing "trim"] Nothing
+          [("leading", StringLit "'" "'" " ")
+          ,("from", Iden [Name Nothing "target_string"])])
+
+
+    ]
+
+aggregates :: TestItem
+aggregates = Group "aggregates" $ map (uncurry (TestScalarExpr ansi2011))
+    [("count(*)",App [Name Nothing "count"] [Star])
+
+    ,("sum(a order by a)"
+    ,AggregateApp [Name Nothing "sum"] SQDefault [Iden [Name Nothing "a"]]
+                  [SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault] Nothing)
+
+    ,("sum(all a)"
+    ,AggregateApp [Name Nothing "sum"] All [Iden [Name Nothing "a"]] [] Nothing)
+
+    ,("count(distinct a)"
+    ,AggregateApp [Name Nothing "count"] Distinct [Iden [Name Nothing "a"]] [] Nothing)
+    ]
+
+windowFunctions :: TestItem
+windowFunctions = Group "windowFunctions" $ map (uncurry (TestScalarExpr ansi2011))
+    [("max(a) over ()", WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [] [] Nothing)
+    ,("count(*) over ()", WindowApp [Name Nothing "count"] [Star] [] [] Nothing)
+
+    ,("max(a) over (partition by b)"
+     ,WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]] [] Nothing)
+
+    ,("max(a) over (partition by b,c)"
+     ,WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"],Iden [Name Nothing "c"]] [] Nothing)
+
+    ,("sum(a) over (order by b)"
+     ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] []
+          [SortSpec (Iden [Name Nothing "b"]) DirDefault NullsOrderDefault] Nothing)
+
+    ,("sum(a) over (order by b desc,c)"
+     ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] []
+          [SortSpec (Iden [Name Nothing "b"]) Desc NullsOrderDefault
+          ,SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault] Nothing)
+
+    ,("sum(a) over (partition by b order by c)"
+     ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
+          [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault] Nothing)
+
+    ,("sum(a) over (partition by b order by c range unbounded preceding)"
+     ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
+      [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
+      $ Just $ FrameFrom FrameRange UnboundedPreceding)
+
+    ,("sum(a) over (partition by b order by c range 5 preceding)"
+     ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
+      [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
+      $ Just $ FrameFrom FrameRange $ Preceding (NumLit "5"))
+
+    ,("sum(a) over (partition by b order by c range current row)"
+     ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
+      [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
+      $ Just $ FrameFrom FrameRange Current)
+
+    ,("sum(a) over (partition by b order by c rows 5 following)"
+     ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
+      [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
+      $ Just $ FrameFrom FrameRows $ Following (NumLit "5"))
+
+    ,("sum(a) over (partition by b order by c range unbounded following)"
+     ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
+      [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
+      $ Just $ FrameFrom FrameRange UnboundedFollowing)
+
+    ,("sum(a) over (partition by b order by c \n\
+      \range between 5 preceding and 5 following)"
+     ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
+      [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
+      $ Just $ FrameBetween FrameRange
+                            (Preceding (NumLit "5"))
+                            (Following (NumLit "5")))
+
+    ]
+
+parens :: TestItem
+parens = Group "parens" $ map (uncurry (TestScalarExpr ansi2011))
+    [("(a)", Parens (Iden [Name Nothing "a"]))
+    ,("(a + b)", Parens (BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"] (Iden [Name Nothing "b"])))
+    ]
+
+functionsWithReservedNames :: TestItem
+functionsWithReservedNames = Group "functionsWithReservedNames" $ map t
+    ["abs"
+    ,"char_length"
+    ]
+  where
+    t fn = TestScalarExpr ansi2011 (fn <> "(a)") $ App [Name Nothing fn] [Iden [Name Nothing "a"]]
+
diff --git a/tests/Language/SQL/SimpleSQL/TableRefs.hs b/tests/Language/SQL/SimpleSQL/TableRefs.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/TableRefs.hs
@@ -0,0 +1,108 @@
+
+{-
+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 ansi2011))
+    [("select a from t"
+     ,ms [TRSimple [Name Nothing "t"]])
+
+     ,("select a from f(a)"
+      ,ms [TRFunction [Name Nothing "f"] [Iden [Name Nothing "a"]]])
+
+    ,("select a from t,u"
+     ,ms [TRSimple [Name Nothing "t"], TRSimple [Name Nothing "u"]])
+
+    ,("select a from s.t"
+     ,ms [TRSimple [Name Nothing "s", Name Nothing "t"]])
+
+-- these lateral queries make no sense but the syntax is valid
+
+    ,("select a from lateral a"
+     ,ms [TRLateral $ TRSimple [Name Nothing "a"]])
+
+    ,("select a from lateral a,b"
+     ,ms [TRLateral $ TRSimple [Name Nothing "a"], TRSimple [Name Nothing "b"]])
+
+    ,("select a from a, lateral b"
+     ,ms [TRSimple [Name Nothing "a"], TRLateral $ TRSimple [Name Nothing "b"]])
+
+    ,("select a from a natural join lateral b"
+     ,ms [TRJoin (TRSimple [Name Nothing "a"]) True JInner
+                 (TRLateral $ TRSimple [Name Nothing "b"])
+                 Nothing])
+
+    ,("select a from lateral a natural join lateral b"
+     ,ms [TRJoin (TRLateral $ TRSimple [Name Nothing "a"]) True JInner
+                 (TRLateral $ TRSimple [Name Nothing "b"])
+                 Nothing])
+
+
+    ,("select a from t inner join u on expr"
+     ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
+                       (Just $ JoinOn $ Iden [Name Nothing "expr"])])
+
+    ,("select a from t join u on expr"
+     ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
+                       (Just $ JoinOn $ Iden [Name Nothing "expr"])])
+
+    ,("select a from t left join u on expr"
+     ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JLeft (TRSimple [Name Nothing "u"])
+                       (Just $ JoinOn $ Iden [Name Nothing "expr"])])
+
+    ,("select a from t right join u on expr"
+     ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JRight (TRSimple [Name Nothing "u"])
+                       (Just $ JoinOn $ Iden [Name Nothing "expr"])])
+
+    ,("select a from t full join u on expr"
+     ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JFull (TRSimple [Name Nothing "u"])
+                       (Just $ JoinOn $ Iden [Name Nothing "expr"])])
+
+    ,("select a from t cross join u"
+     ,ms [TRJoin (TRSimple [Name Nothing "t"]) False
+                       JCross (TRSimple [Name Nothing "u"]) Nothing])
+
+    ,("select a from t natural inner join u"
+     ,ms [TRJoin (TRSimple [Name Nothing "t"]) True JInner (TRSimple [Name Nothing "u"])
+                       Nothing])
+
+    ,("select a from t inner join u using(a,b)"
+     ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
+                       (Just $ JoinUsing [Name Nothing "a", Name Nothing "b"])])
+
+    ,("select a from (select a from t)"
+     ,ms [TRQueryExpr $ ms [TRSimple [Name Nothing "t"]]])
+
+    ,("select a from t as u"
+     ,ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") Nothing)])
+
+    ,("select a from t u"
+     ,ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") Nothing)])
+
+    ,("select a from t u(b)"
+     ,ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") $ Just [Name Nothing "b"])])
+
+    ,("select a from (t cross join u) as u"
+     ,ms [TRAlias (TRParens $
+                   TRJoin (TRSimple [Name Nothing "t"]) False JCross (TRSimple [Name Nothing "u"]) Nothing)
+                          (Alias (Name Nothing "u") Nothing)])
+     -- todo: not sure if the associativity is correct
+
+    ,("select a from t cross join u cross join v",
+       ms [TRJoin
+           (TRJoin (TRSimple [Name Nothing "t"]) False
+                   JCross (TRSimple [Name Nothing "u"]) Nothing)
+           False JCross (TRSimple [Name Nothing "v"]) Nothing])
+    ]
+  where
+    ms f = toQueryExpr $ makeSelect {msSelectList = [(Iden [Name Nothing "a"],Nothing)]
+                                    ,msFrom = f}
diff --git a/tests/Language/SQL/SimpleSQL/TestTypes.hs b/tests/Language/SQL/SimpleSQL/TestTypes.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/TestTypes.hs
@@ -0,0 +1,45 @@
+
+{-
+This is the types used to define the tests as pure data. See the
+Tests.hs module for the 'interpreter'.
+-}
+
+module Language.SQL.SimpleSQL.TestTypes
+    (TestItem(..)
+    ,module Language.SQL.SimpleSQL.Dialect
+    ) where
+
+import Language.SQL.SimpleSQL.Syntax
+import Language.SQL.SimpleSQL.Lex (Token)
+import Language.SQL.SimpleSQL.Dialect
+
+import Data.Text (Text)
+
+{-
+TODO: maybe make the dialect args into [dialect], then each test
+checks all the dialects mentioned work, and all the dialects not
+mentioned give a parse error. Not sure if this will be too awkward due
+to lots of tricky exceptions/variationsx.
+-}
+
+data TestItem = Group Text [TestItem]
+              | TestScalarExpr Dialect Text ScalarExpr
+              | TestQueryExpr Dialect Text QueryExpr
+              | TestStatement Dialect Text Statement
+              | TestStatements Dialect Text [Statement]
+
+{-
+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 Dialect Text
+
+-- check that the string given fails to parse
+
+              | ParseQueryExprFails Dialect Text
+              | ParseScalarExprFails Dialect Text
+              | LexTest Dialect Text [Token]
+              | LexFails Dialect Text
+                deriving (Eq,Show)
diff --git a/tests/Language/SQL/SimpleSQL/Tests.hs b/tests/Language/SQL/SimpleSQL/Tests.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/Tests.hs
@@ -0,0 +1,180 @@
+
+{-
+This is the main tests module which exposes the test data plus the
+Test.Framework tests. It also contains the code which converts the
+test data to the Test.Framework tests.
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.Tests
+    (testData
+    ,tests
+    ,TestItem(..)
+    ) where
+
+import qualified Test.Tasty as T
+import qualified Test.Tasty.HUnit as H
+
+--import Language.SQL.SimpleSQL.Syntax
+import Language.SQL.SimpleSQL.Pretty
+import Language.SQL.SimpleSQL.Parse
+import qualified Language.SQL.SimpleSQL.Lex as Lex
+
+import Language.SQL.SimpleSQL.TestTypes
+
+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.Odbc
+import Language.SQL.SimpleSQL.Tpch
+import Language.SQL.SimpleSQL.LexerTests
+import Language.SQL.SimpleSQL.EmptyStatement
+import Language.SQL.SimpleSQL.CreateIndex
+
+import Language.SQL.SimpleSQL.SQL2011Queries
+import Language.SQL.SimpleSQL.SQL2011AccessControl
+import Language.SQL.SimpleSQL.SQL2011Bits
+import Language.SQL.SimpleSQL.SQL2011DataManipulation
+import Language.SQL.SimpleSQL.SQL2011Schema
+
+import Language.SQL.SimpleSQL.MySQL
+import Language.SQL.SimpleSQL.Oracle
+import Language.SQL.SimpleSQL.CustomDialect
+
+import Data.Text (Text)
+import qualified Data.Text as T
+
+
+{-
+Order the tests to start from the simplest first. This is also the
+order on the generated documentation.
+-}
+
+testData :: TestItem
+testData =
+    Group "parserTest"
+    [lexerTests
+    ,scalarExprTests
+    ,odbcTests
+    ,queryExprComponentTests
+    ,queryExprsTests
+    ,tableRefTests
+    ,groupByTests
+    ,fullQueriesTests
+    ,postgresTests
+    ,tpchTests
+    ,sql2011QueryTests
+    ,sql2011DataManipulationTests
+    ,sql2011SchemaTests
+    ,sql2011AccessControlTests
+    ,sql2011BitsTests
+    ,mySQLTests
+    ,oracleTests
+    ,customDialectTests
+    ,emptyStatementTests
+    ,createIndexTests
+    ]
+
+tests :: T.TestTree
+tests = itemToTest testData
+
+--runTests :: IO ()
+--runTests = void $ H.runTestTT $ itemToTest testData
+
+itemToTest :: TestItem -> T.TestTree
+itemToTest (Group nm ts) =
+    T.testGroup (T.unpack nm) $ map itemToTest ts
+itemToTest (TestScalarExpr d str expected) =
+    toTest parseScalarExpr prettyScalarExpr d str expected
+itemToTest (TestQueryExpr d str expected) =
+    toTest parseQueryExpr prettyQueryExpr d str expected
+itemToTest (TestStatement d str expected) =
+    toTest parseStatement prettyStatement d str expected
+itemToTest (TestStatements d str expected) =
+    toTest parseStatements prettyStatements d str expected
+itemToTest (ParseQueryExpr d str) =
+    toPTest parseQueryExpr prettyQueryExpr d str
+
+itemToTest (ParseQueryExprFails d str) =
+    toFTest parseQueryExpr prettyQueryExpr d str
+
+itemToTest (ParseScalarExprFails d str) =
+    toFTest parseScalarExpr prettyScalarExpr d str
+
+itemToTest (LexTest d s ts) = makeLexerTest d s ts
+itemToTest (LexFails d s) = makeLexingFailsTest d s
+
+makeLexerTest :: Dialect -> Text -> [Lex.Token] -> T.TestTree
+makeLexerTest d s ts = H.testCase (T.unpack s) $ do
+    let ts1 = either (error . T.unpack . Lex.prettyError) id $ Lex.lexSQL d "" Nothing s
+    H.assertEqual "" ts ts1
+    let s' = Lex.prettyTokens d $ ts1
+    H.assertEqual "pretty print" s s'
+
+makeLexingFailsTest :: Dialect -> Text -> T.TestTree
+makeLexingFailsTest d s = H.testCase (T.unpack s) $ do
+    case Lex.lexSQL d "" Nothing s of
+         Right x -> H.assertFailure $ "lexing should have failed: " ++ T.unpack s ++ "\ngot: " ++ show x
+         Left _ -> pure ()
+
+
+toTest :: (Eq a, Show a) =>
+          (Dialect -> Text -> Maybe (Int,Int) -> Text -> Either ParseError a)
+       -> (Dialect -> a -> Text)
+       -> Dialect
+       -> Text
+       -> a
+       -> T.TestTree
+toTest parser pp d str expected = H.testCase (T.unpack str) $ do
+        let egot = parser d "" Nothing str
+        case egot of
+            Left e -> H.assertFailure $ T.unpack $ prettyError e
+            Right got -> H.assertEqual "" expected got
+        
+        let str' = pp d expected
+            egot' = parser d "" Nothing str'
+        case egot' of
+            Left e' ->
+                H.assertFailure $ "pp roundtrip"
+                    ++ "\n" ++ (T.unpack str')
+                    ++ (T.unpack $ prettyError e')
+            Right got' ->
+                H.assertEqual
+                    ("pp roundtrip" ++ "\n" ++ T.unpack str')
+                    expected got'
+
+toPTest :: (Eq a, Show a) =>
+          (Dialect -> Text -> Maybe (Int,Int) -> Text -> Either ParseError a)
+       -> (Dialect -> a -> Text)
+       -> Dialect
+       -> Text
+       -> T.TestTree
+toPTest parser pp d str = H.testCase (T.unpack str) $ do
+        let egot = parser d "" Nothing str
+        case egot of
+            Left e -> H.assertFailure $ T.unpack $ prettyError e
+            Right got -> do
+                let str' = pp d got
+                let egot' = parser d "" Nothing str'
+                case egot' of
+                    Left e' -> H.assertFailure $ "pp roundtrip "
+                                                 ++ "\n" ++ T.unpack str' ++ "\n"
+                                                 ++ T.unpack (prettyError e')
+                    Right _got' -> return ()
+
+toFTest :: (Eq a, Show a) =>
+          (Dialect -> Text -> Maybe (Int,Int) -> Text -> Either ParseError a)
+       -> (Dialect -> a -> Text)
+       -> Dialect
+       -> Text
+       -> T.TestTree
+toFTest parser _pp d str = H.testCase (T.unpack str) $ do
+        let egot = parser d "" Nothing str
+        case egot of
+            Left _e -> return ()
+            Right _got ->
+              H.assertFailure $ "parse didn't fail: " ++ show d ++ "\n" ++ T.unpack str
diff --git a/tests/Language/SQL/SimpleSQL/Tpch.hs b/tests/Language/SQL/SimpleSQL/Tpch.hs
new file mode 100644
--- /dev/null
+++ b/tests/Language/SQL/SimpleSQL/Tpch.hs
@@ -0,0 +1,688 @@
+
+{-
+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
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+module Language.SQL.SimpleSQL.Tpch (tpchTests,tpchQueries) where
+
+import Language.SQL.SimpleSQL.TestTypes
+
+import Data.Text (Text)
+
+tpchTests :: TestItem
+tpchTests =
+    Group "parse tpch"
+    $ map (ParseQueryExpr ansi2011 . snd) tpchQueries
+
+tpchQueries :: [(String,Text)]
+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/tests/RunTests.hs b/tests/RunTests.hs
new file mode 100644
--- /dev/null
+++ b/tests/RunTests.hs
@@ -0,0 +1,8 @@
+
+
+import Test.Tasty
+
+import Language.SQL.SimpleSQL.Tests
+
+main :: IO ()
+main = defaultMain tests
diff --git a/tools/Fixity.lhs b/tools/Fixity.lhs
deleted file mode 100644
--- a/tools/Fixity.lhs
+++ /dev/null
@@ -1,702 +0,0 @@
-
-= Fixity fixups
-
-The point of this code is to be able to take a table of fixity
-information for unary and binary operators, then adjust an ast to
-match these fixities. The standard way of handling this is handling
-fixities at the parsing stage.
-
-For the SQL parser, this is difficult because there is lots of weird
-syntax for operators (such as prefix and postfix multiple keyword
-operators, between, etc.).
-
-An alterative idea which is used in some places is to parse the tree
-regarding all the operators to have the same precedence and left
-associativity, then correct the fixity in a pass over the ast after
-parsing. Would also like to use this to fix the fixity for the join
-trees, and set operations, after parsing them. TODO: anything else?
-
-
-Approach
-
-Really not sure how to get this correct. So: lots of testing
-
-Basic testing idea: create an expression, then write down manually how
-the expression should parse with correct fixity. Can write down the
-expression in concrete syntax, and the correct fixity version using
-parens.
-
-Then can parse the expression, fix it, parse the fixed expression,
-remove the parens and compare them to make sure they are equal.
-
-Second layer of testing. For each source expression parsed, run it
-through a generator which will generate every version of that tree by
-choosing all possibilities of fixities on a token by token basis. This
-will ensure the fixity fixer is robust. An alternative approach is to
-guarantee the parser will produce trees where all the fixities are
-known (e.g. unary operators always bind tighter than binary, binary
-are all left associative, prefix unary bind tighter than postfix. This
-way, the fix code can make some assumptions and have less code. We
-will stick with the full general version which is more robust.
-
-Another testing approach is to parse the tree with our non fixity
-respecting parser then fix it, and also parse it with a fixity
-respecting expression parser, and check the results are the same. This
-is difficult with the parsec build expression parser which doesn't
-handle nested unary operators, so have to find or write another build
-expression parser. We can test the fixer with simple operators (single
-symbol prefix, postfix and binary ops) and then use it on the complex
-sql ast trees.
-
-Can also try to generate trees ala quickcheck/smallcheck, then check
-them with the fixer and the build expression parser.
-
-generate a tree:
-
-start with a term
-then roll dice:
-  add a prefix
-  add a postfix
-  do nothing
-then roll dice
-  add a binary op
-  for the second arg, recurse the algo
-
-
-algorithm:
-
-consider possible cases:
-binop with two binops args
-binop with prefix on left
-binop with postfix on right
-postfix with prefix inside
-prefix with postfix inside
-postfix with binop inside
-prefix with binop inside
-
-write a function to deal with each case and try to compose
-
-Tasks:
-
-write unary op tests: on each other, and with binary ops
-figure out how to generate trees
-do the step one tests (write the fixity with parens)
-check out parsers expression parser
-see if can generate trees using smallcheck
-try to test these trees against expression parser
-  otherwise, generate tree, generate variations, check fixity always
-produces same result
-
-
-
-
-todo:
-
-1. more tests for unary operators with each other
-2. moving unary operators inside and outside binary operators:
-   have to think about how this will work in general case
-3. ways to generate lots of tests and check them
-   -> what about creating a parser which parses to a list of all possible
-      parses with different fixities for each operator it sees?
-4. ambiguous fixity cases - need position annotation to do these nicely
-5. real sql: how to work with a variety of ast nodes
-6. plug into simple-sql-parser
-7. refactor the simple-sql-parser parsing code
-8. simple-sql-parser todo for sqream: add other dml, dialects,
-   procedural?
-9. testing idea: write big expressions with explicit parens everywhere
-   parse this
-   remove the parens
-   pretty print, then parse and fixfixity to see if same
-   then generate all variations of tree as if the fixities are different
-     and then fixfixity to check it restores the original
-
-
-write fixity tests
-write code to do the fixing
-add error cases: put it in the either monad to report these
-
-check the descend
-then: move to real sql
-  different abstract representations of binops, etc.
-  what is the best way to deal with this? typeclass? conversion to and
-  from a generic tree?
-
-
-
-
-
-can the binops be fixed on their own (precedence and assocativity)
-and then the prefix and postfix ops in separate passes
-
-what about a pass which puts the tree into canonical form:
-all left associative, all unary ops tight as possible?
-then the fixer can be easier?
-
-
-
-
-
-> {-# LANGUAGE DeriveDataTypeable,TupleSections #-}
-> import Data.Data
-
-> import Text.Parsec.String (Parser)
-> import Text.Parsec (try)
-> import Text.Parsec.Char
-> import Text.Parsec.Combinator
-> import Text.Parsec (parse,ParseError)
-> import Control.Applicative ((<|>),many) -- ((<**>),(<$>),(<*), (*>),(<*>), (<$), (<|>), many)
-> --import qualified Text.Parsec.String.Expr as E
-> import Control.Monad
-> --import Data.List (intercalate)
-> import Data.Maybe ()
-> --import qualified Test.HUnit as H
-> --import FunctionsAndTypesForParsing
-> import Debug.Trace
-> import Text.Show.Pretty
-> import Data.List
-> import Control.Applicative
-
-> import qualified Test.Tasty as T
-> import qualified Test.Tasty.HUnit as H
-
-
-> data Expr = BinOp Expr String Expr
->           | PrefOp String Expr
->           | PostOp String Expr
->           | Iden String
->           | Lit String
->           | App String [Expr]
->           | Parens Expr
->             deriving (Eq,Show,Data,Typeable)
-
---------
-
-quick parser
-
-> parensValue :: Parser Expr
-> parensValue = Parens <$> parens valueExpr
-
-> idenApp :: Parser Expr
-> idenApp = try $ do
->     i <- identifier
->     guard (i `notElem` ["not", "and", "or", "is"])
->     choice [do
->             args <- parens (commaSep valueExpr)
->             return $ App i args
->            ,return $ Iden i
->            ]
-
-> lit :: Parser Expr
-> lit = stringLit <|> numLit
->     where
->       stringLit = Lit <$> lexeme (char '\'' *> manyTill anyChar (char '\''))
->       numLit = do
->         x <- lexeme (many1 digit)
->         let y :: Integer
->             y = read x
->         return $ Lit $ show y
-
-> prefOp :: Parser Expr
-> prefOp = sym <|> kw
->    where
->       sym = do
->             let prefOps = ["+", "-"]
->             s <- choice $ map symbol prefOps
->             v <- term
->             return $ PrefOp s v
->       kw = do
->            let prefOps = ["not"]
->            i <- identifier
->            guard (i `elem` prefOps)
->            v <- term
->            return $ PrefOp i v
-
-> postOp :: Parser (Expr -> Expr)
-> postOp = try $ do
->     let kws = ["is null"]
->         kwsp = map (\a -> try $ do
->                        let x :: [String]
->                            x = words a
->                        mapM_ keyword_ x
->                        return $ PostOp a
->                        ) kws
->     choice kwsp
-
-> binOp :: Parser (Expr -> Expr -> Expr)
-> binOp = symbolBinOp <|> kwBinOp
->   where
->     symbolBinOp = do
->                   let binOps = ["+", "-", "*", "/"]
->                   s <- choice $ map symbol binOps
->                   return $ \a b -> BinOp a s b
->     kwBinOp = do
->               let kwBinOps = ["and", "or"]
->               i <- identifier
->               guard (i `elem` kwBinOps)
->               return $ \a b -> BinOp a i b
-
-> term :: Parser Expr
-> term = (parensValue
->        <|> try prefOp
->        <|> idenApp
->        <|> lit)
->        <??*> postOp
-
-> -- (<??>) :: Parser a -> Parser (a -> a) -> Parser a
-> -- p <??> q = p <**> option id q
-
-> (<??*>) :: Parser a -> Parser (a -> a) -> Parser a
-> p <??*> q = foldr ($) <$> p <*> (reverse <$> many q)
-
-> valueExpr :: Parser Expr
-> valueExpr = chainl1 term binOp
-
-
-> parens :: Parser a -> Parser a
-> parens = between openParen closeParen
-
-> openParen :: Parser Char
-> openParen = lexeme $ char '('
-> closeParen :: Parser Char
-> closeParen = lexeme $ char ')'
-
-> symbol :: String -> Parser String
-> symbol s = try $ lexeme $ do
->     u <- many1 (oneOf "<>=+-^%/*!|")
->     guard (s == u)
->     return s
-
-> identifier :: Parser String
-> identifier = lexeme ((:) <$> firstChar <*> many nonFirstChar)
->   where
->     firstChar = letter <|> char '_'
->     nonFirstChar = digit <|> firstChar
-
-> keyword :: String -> Parser String
-> keyword k = try $ do
->     i <- identifier
->     guard (i == k)
->     return k
-
-> keyword_ :: String -> Parser ()
-> keyword_ = void . keyword
-
-> whitespace :: Parser ()
-> whitespace =
->     choice [simpleWhitespace *> whitespace
->            ,lineComment *> whitespace
->            ,blockComment *> whitespace
->            ,return ()]
->   where
->     lineComment = try (string "--")
->                   *> manyTill anyChar (void (char '\n') <|> eof)
->     blockComment = try (string "/*")
->                    *> manyTill anyChar (try $ string "*/")
->     simpleWhitespace = void $ many1 (oneOf " \t\n")
-> lexeme :: Parser a -> Parser a
-> lexeme p = p <* whitespace
-> comma :: Parser Char
-> comma = lexeme $ char ','
-
-> commaSep :: Parser a -> Parser [a]
-> commaSep = (`sepBy` comma)
-
-> parseExpr :: String -> Either ParseError Expr
-> parseExpr = parse (whitespace *> valueExpr <* eof) ""
-
---------------
-
-> data Assoc = AssocLeft | AssocRight | AssocNone deriving (Eq,Show)
-
-> type Fixities = [(String, (Int, Assoc))]
-
-> fixFixity :: Fixities -> Expr -> Expr
-> fixFixity fixities = fixBinOpPrecedence . fixBinOpAssociativity . fixNestedPrefPostPrec
->   where
->     fixBinOpAssociativity e = case e of
->         BinOp a op b ->
->             let a' = fixBinOpAssociativity a
->                 b' = fixBinOpAssociativity b
->                 def = BinOp a' op b'
->             in case (a',b') of
->                 -- both
->                 -- a1 op1 a2 op b1 op2 b2
->                 (BinOp a1 op1 a2
->                  ,BinOp b1 op2 b2)
->                   | Just (_p,opa) <- lookupFixity op
->                   , Just (_p,op1a) <- lookupFixity op1
->                   , Just (_p,op2a) <- lookupFixity op2
->                   -> case (opa, op1a, op2a) of
->                        (AssocRight, AssocRight, AssocRight) ->
->                            BinOp a1 op1 (BinOp a2 op (BinOp b1 op2 b2))
->                        (AssocLeft, AssocLeft, AssocLeft) ->
->                            BinOp (BinOp (BinOp a1 op1 a2) op b1) op2 b2
->                        --todo: other cases
->                        _ -> def
->                 -- just left side
->                 (BinOp a1 op1 a2, _)
->                 -- a1 op1 a2 op b'
->                   | Just (_p,opa) <- lookupFixity op
->                   , Just (_p,op1a) <- lookupFixity op1
->                   -> case (opa, op1a) of
->                        (AssocRight, AssocRight) ->
->                            BinOp a1 op1 (BinOp a2 op b')
->                        (AssocLeft, AssocLeft) ->
->                            BinOp (BinOp a1 op1 a2) op b'
->                        _ -> def
-
->                 -- just right side
->                 (_, BinOp b1 op2 b2)
->                 -- e op b1 op2 b2
->                   | Just (_p,opa) <- lookupFixity op
->                   , Just (_p,op2a) <- lookupFixity op2
->                   -> case (opa, op2a) of
->                        (AssocRight, AssocRight) ->
->                            BinOp a' op (BinOp b1 op2 b2)
->                        (AssocLeft, AssocLeft) ->
->                            BinOp (BinOp a' op b1) op2 b2
->                        _ -> def
->                 _ -> def
->         _ -> e
-
->     fixBinOpPrecedence e = case e of
->         BinOp a op b ->
->             let a' = fixBinOpPrecedence a
->                 b' = fixBinOpPrecedence b
->                 def = BinOp a' op b'
->             in case (a',b') of
->                 -- both
->                 -- a1 op1 a2 op b1 op2 b2
->                 -- all equal
->                 -- p > or < p1 == p2
->                 -- p == p1 < or > p2
->                 (BinOp a1 op1 a2
->                  ,BinOp b1 op2 b2)
->                   | Just (p,_opa) <- lookupFixity op
->                   , Just (p1,_op1a) <- lookupFixity op1
->                   , Just (p2,_op2a) <- lookupFixity op2
->                   -> case () of
->                         -- _ | trace ("both prec " ++ show (p,p1,p2)) False -> undefined
->                         _ | p == p1 && p1 == p2 -> def
->                         _ | p > p1 && p1 == p2 -> BinOp a1 op1 b'
->                         _ | p < p1 && p1 == p2 -> BinOp (BinOp a1 op1 a2) op b'
->                         _ | p == p1 && p2 > p1 -> BinOp a' op (BinOp b1 op2 b2)
->                         _ | p == p1 && p2 < p1 -> def -- todo
->                         _ | otherwise -> def
->                 -- just left side
->                 (BinOp a1 op1 a2, _)
->                 -- a1 op1 a2 op b'
->                   | Just (p,_opa) <- lookupFixity op
->                   , Just (p1,_op1a) <- lookupFixity op1
->                   -> case () of
->                         --  _ | trace ("left prec " ++ show (p,p1)) False -> undefined
->                         _ | p < p1 -> {-trace "b1" $ -}BinOp (BinOp a1 op1 a2) op b'
->                           | p > p1 -> {-trace "b2" $ -}BinOp a1 op1 (BinOp a2 op b')
->                           | otherwise -> def
-
->                 -- just right side
->                 (_, BinOp b1 op2 b2)
->                 -- a' op b1 op2 b2
->                   | Just (p,_opa) <- lookupFixity op
->                   , Just (p2,_op1a) <- lookupFixity op2
->                   -> case () of
->                         --  _ | trace ("right prec " ++ show (p,p2)) False -> undefined
->                         _ | p > p2 -> {-trace "b1" $ -}BinOp (BinOp a' op b1) op2 b2
->                           | p < p2 -> {-trace "b2" $ -}BinOp a' op (BinOp b1 op2 b2)
->                           | otherwise -> {-trace "def" $ -} def
->                 _ -> def
->         _ -> e
-
->     fixNestedPrefPostPrec e = case e of
->         PrefOp op a ->
->             let a' = fixNestedPrefPostPrec a
->             in case a' of
->                  PostOp op1 b | Just (p,_) <- lookupFixity op
->                               , Just (p1,_) <- lookupFixity op1
->                               , p > p1 -> PostOp op1 (PrefOp op b)
->                  _ -> PrefOp op a'
->         PostOp op a ->
->             let a' = fixNestedPrefPostPrec a
->             in case a' of
->                  PrefOp op1 b | Just (p,_) <- lookupFixity op
->                               , Just (p1,_) <- lookupFixity op1
->                               , p > p1 -> PrefOp op1 (PostOp op b)
->                  _ -> PostOp op a'
->         _ -> e
-
-
-
->     lookupFixity :: String -> Maybe (Int,Assoc)
->     lookupFixity s = maybe (trace ("didn't find " ++ s ++ "\n" ++ ppShow fixities) Nothing)
->                      Just $ lookup s fixities
-
-
-> sqlFixity :: [(String, (Int, Assoc))]
-> sqlFixity = [(".", (13, AssocLeft))
->             ,("[]", (12, AssocNone))
-
-unary + -
-todo: split the fixity table into prefix, binary and postfix
-
-todo: don't have explicit precedence numbers in the table??
-
->             ,("^", (10, AssocNone))]
->             ++ m ["*", "/", "%"] (9, AssocLeft)
->             ++ m ["+","-"] (8, AssocLeft)
->             ++ m ["<", ">", "=", "<=", ">=", "<>"] (4, AssocNone)
->             ++ [("is null", (3, AssocNone))
->                ,("not", (2, AssocRight))
->                ,("and", (1, AssocLeft))
->                ,("or", (0, AssocLeft))]
-
->   where
->     m l a = map (,a) l
-
--------
-
-some simple parser tests
-
-> data Test = Group String [Test]
->           | ParserTest String Expr
->           | FixityTest Fixities Expr Expr
-
-> parserTests :: Test
-> parserTests = Group "parserTests" $ map (uncurry ParserTest) $
->     [("a", Iden "a")
->     ,("'test'", Lit "test")
->     ,("34", Lit "34")
->     ,("f()", App "f" [])
->     ,("f(3)", App "f" [Lit "3"])
->     ,("(7)", Parens (Lit "7"))
->     ,("a + 3", BinOp (Iden "a") "+" (Lit "3"))
->     ,("1 + 2 + 3", BinOp (BinOp (Lit "1") "+" (Lit "2")) "+" (Lit "3"))
-
->     ,("a or b", BinOp (Iden "a") "or" (Iden "b"))
->     ,("-1", PrefOp "-" (Lit "1"))
->     ,("not a", PrefOp "not" (Iden "a"))
->     ,("not not a", PrefOp "not" (PrefOp "not" (Iden "a")))
->     ,("a is null", PostOp "is null" (Iden "a"))
->     ,("a is null is null", PostOp "is null" (PostOp "is null" (Iden "a")))
->     ,("-a+3", BinOp (PrefOp "-" (Iden "a")) "+" (Lit "3"))
->     ,("a is null and b is null", BinOp (PostOp "is null" (Iden "a"))
->                                  "and"
->                                  (PostOp "is null" (Iden "b")))
->     ]
-
-> makeParserTest :: String -> Expr -> T.TestTree
-> makeParserTest s e = H.testCase s $ do
->     let a = parseExpr s
->     if (Right e == a)
->        then putStrLn $ s ++ " OK"
->        else putStrLn $ "bad parse " ++ s ++ " " ++ show a
-
-------
-
-fixity checks
-
-test cases:
-
-
-> fixityTests :: Test
-> fixityTests = Group "fixityTests" $
->      map (\(f,s,e) -> FixityTest f s e) $
->      [
-
-2 bin ops wrong associativity left + null versions
-
->      (sqlFixity
->      ,i "a" `plus` (i "b" `plus` i "c")
->      ,(i "a" `plus` i "b") `plus` i "c")
->     ,(sqlFixity
->      ,(i "a" `plus` i "b") `plus` i "c"
->      ,(i "a" `plus` i "b") `plus` i "c")
-
-2 bin ops wrong associativity right
-
->     ,(timesRight
->      ,i "a" `times` (i "b" `times` i "c")
->      ,i "a" `times` (i "b" `times` i "c"))
->     ,(timesRight
->      ,(i "a" `times` i "b") `times` i "c"
->      ,i "a" `times` (i "b" `times` i "c"))
-
-
-2 bin ops wrong precedence left
-
->     ,(sqlFixity
->      ,i "a" `plus` (i "b" `times` i "c")
->      ,i "a" `plus` (i "b" `times` i "c"))
-
->     ,(sqlFixity
->      ,(i "a" `plus` i "b") `times` i "c"
->      ,i "a" `plus` (i "b" `times` i "c"))
-
-2 bin ops wrong precedence right
-
->     ,(sqlFixity
->      ,(i "a" `times` i "b") `plus` i "c"
->      ,(i "a" `times` i "b") `plus` i "c")
-
->     ,(sqlFixity
->      ,i "a" `times` (i "b" `plus` i "c")
->      ,(i "a" `times` i "b") `plus` i "c")
-
-a + b * c + d
-a * b + c * d
-
-check all variations
-
->     ] ++
->       (let t = (i "a" `plus` i "b")
->                `times`
->                (i "c" `plus` i "d")
->            trs = generateTrees $ splitTree t
->        in [(sqlFixity, x
->            ,i "a" `plus` (i "b" `times` i "c")
->             `plus` i "d")
->           | x <- trs])
->       ++
->       (let t = (i "a" `times` i "b")
->                `plus`
->                (i "c" `times` i "d")
->            trs = generateTrees $ splitTree t
->        in [(sqlFixity, x
->            ,(i "a" `times` i "b")
->                `plus`
->                (i "c" `times` i "d"))
->           | x <- trs])
-
-
->     ++ [
-
-prefix then postfix wrong precedence
-
->      ([("+", (9, AssocNone))
->       ,("is null", (3, AssocNone))]
->      ,PrefOp "+" (PostOp "is null" (i "a"))
->      ,PostOp "is null" (PrefOp "+" (i "a")))
-
->     ,([("+", (9, AssocNone))
->       ,("is null", (3, AssocNone))]
->      ,PostOp "is null" (PrefOp "+" (i "a"))
->      ,PostOp "is null" (PrefOp "+" (i "a")))
-
->     ,([("+", (3, AssocNone))
->       ,("is null", (9, AssocNone))]
->      ,PrefOp "+" (PostOp "is null" (i "a"))
->      ,PrefOp "+" (PostOp "is null" (i "a")))
-
->     ,([("+", (3, AssocNone))
->       ,("is null", (9, AssocNone))]
->      ,PostOp "is null" (PrefOp "+" (i "a"))
->      ,PrefOp "+" (PostOp "is null" (i "a")))
-
-3-way unary operator movement:
-take a starting point and generate variations
-
-postfix on first arg of binop (cannot move) make sure precedence wants
-  it to move
-
-prefix on second arg of binop (cannot move)
-
-prefix on binop, precedence wrong
-postfix on binop precedence wrong
-prefix on first arg of binop, precedence wrong
-postfix on second arg of binop, precedence wrong
-
-ambiguous fixity tests
-
-sanity check: parens stops rearrangement
-
-check nesting 1 + f(expr)
-
->     ]
->     where
->       plus a b = BinOp a "+" b
->       times a b = BinOp a "*" b
->       i a = Iden a
->       timesRight = [("*", (9, AssocRight))]
-
-testCase
-
-> makeFixityTest :: Fixities -> Expr -> Expr -> T.TestTree
-> makeFixityTest fs s e = H.testCase (show s) $ do
->     let s' = fixFixity fs s
->     H.assertEqual "" s' e
->     {-if (s' == e)
->       then putStrLn $ show s ++ " OK"
->       else putStrLn $ "ERROR\nstart: " ++ show s ++ "\nfixed: " ++ show s' ++ "\nshould be: " ++ show e-}
-
-> tests :: Test
-> tests = Group "Tests" [parserTests, fixityTests]
-
-> makeTest :: Test -> T.TestTree
-> makeTest (Group n ts) = T.testGroup n $ map makeTest ts
-> makeTest (ParserTest s e) = makeParserTest s e
-> makeTest (FixityTest f s e) = makeFixityTest f s e
-
---------
-
- > tests :: T.TestTree
- > tests = T.testGroup "Tests" $ map makeFixityTest fixityTests
-
-> main :: IO ()
-> main = T.defaultMain $ makeTest tests
->   {-do
->        mapM_ checkTest tests
->        mapM_ checkFixity fixityTests
->        let plus a b = BinOp a "+" b
->            times a b = BinOp a "*" b
->            i a = Iden a
->        let t = (i "a" `plus` i "b")
->                `times`
->                (i "c" `plus` i "d")
->            spl = splitTree t
->            trs = generateTrees spl
->        --putStrLn $ "\nSplit\n"
->        --putStrLn $ ppShow (fst spl, length $ snd spl)
->        --putStrLn $ show $ length trs
->        --putStrLn $ "\nTrees\n"
->        --putStrLn $ intercalate "\n" $ map show trs
->        return ()-}
-
-generating trees
-
-1. tree -> list
-val op val op val op ...
-(has to be two lists?
-
-generate variations:
-pick numbers from 0 to n - 1 (n is the number of ops)
-choose the op at this position to be the root
-recurse on the two sides
-
-> splitTree :: Expr -> ([Expr], [Expr->Expr->Expr])
-> splitTree (BinOp a op b) = let (x,y) = splitTree a
->                                (z,w) = splitTree b
->                            in (x++z, y++ [\a b -> BinOp a op b] ++ w)
-> splitTree x = ([x],[])
-
-
-
-> generateTrees :: ([Expr], [Expr->Expr->Expr]) -> [Expr]
-> generateTrees (es,ops) | length es /= length ops + 1 =
->     error $ "mismatch in lengths " ++ show (length es, length ops)
->     ++"\n" ++ ppShow es ++ "\n"
-> generateTrees ([a,b], [op]) = [op a b]
-> generateTrees ([a], []) = [a]
-> generateTrees (vs, ops) =
->     let n = length ops
->     in --trace ("generating " ++ show (length vs, n) ++ "trees\n") $
->        concat $ flip map [0..n-1] $ \m ->
->          let (v1,v2) = splitAt (m + 1) vs
->              (ops1,op':ops2) = splitAt m ops
->              r = [op' t u | t <- generateTrees (v1,ops1)
->                      , u <- generateTrees (v2,ops2)]
->          in -- trace ("generated " ++ show (length r) ++ " trees")
->             r
-> generateTrees ([],[]) = []
-
-
-
diff --git a/tools/Language/SQL/SimpleSQL/CustomDialect.lhs b/tools/Language/SQL/SimpleSQL/CustomDialect.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/CustomDialect.lhs
+++ /dev/null
@@ -1,27 +0,0 @@
-
-> module Language.SQL.SimpleSQL.CustomDialect (customDialectTests) where
-
-> import Language.SQL.SimpleSQL.TestTypes
-
-> customDialectTests :: TestItem
-> customDialectTests = Group "custom dialect tests" (map (uncurry ParseQueryExpr) passTests
->     ++ map (uncurry ParseScalarExprFails) failTests )
->   where
->     failTests = [(ansi2011,"SELECT DATE('2000-01-01')")
->                 ,(ansi2011,"SELECT DATE")
->                 ,(dateApp,"SELECT DATE")
->                 ,(dateIden,"SELECT DATE('2000-01-01')")
->                 -- show this never being allowed as an alias
->                 ,(ansi2011,"SELECT a date")
->                 ,(dateApp,"SELECT a date")
->                 ,(dateIden,"SELECT a date")
->                 ]
->     passTests = [(ansi2011,"SELECT a b")
->                 ,(noDateKeyword,"SELECT DATE('2000-01-01')")
->                 ,(noDateKeyword,"SELECT DATE")
->                 ,(dateApp,"SELECT DATE('2000-01-01')")
->                 ,(dateIden,"SELECT DATE")
->                 ]
->     noDateKeyword = ansi2011 {diKeywords = filter (/="date") (diKeywords ansi2011)}
->     dateIden = ansi2011 {diIdentifierKeywords = "date" : diIdentifierKeywords ansi2011}
->     dateApp = ansi2011 {diAppKeywords = "date" : diAppKeywords ansi2011}
diff --git a/tools/Language/SQL/SimpleSQL/ErrorMessages.lhs b/tools/Language/SQL/SimpleSQL/ErrorMessages.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/ErrorMessages.lhs
+++ /dev/null
@@ -1,150 +0,0 @@
-
-Want to work on the error messages. Ultimately, parsec won't give the
-best error message for a parser combinator library in haskell. Should
-check out the alternatives such as polyparse and uu-parsing.
-
-For now the plan is to try to get the best out of parsec. Skip heavy
-work on this until the parser is more left factored?
-
-Ideas:
-
-1. generate large lists of invalid syntax
-2. create table of the sql source and the error message
-3. save these tables and compare from version to version. Want to
-   catch improvements and regressions and investigate. Have to do this
-   manually
-
-= generating bad sql source
-
-take good sql statements or expressions. Convert them into sequences
-of tokens - want to preserve the whitespace and comments perfectly
-here. Then modify these lists by either adding a token, removing a
-token, or modifying a token (including creating bad tokens of raw
-strings which don't represent anything than can be tokenized.
-
-Now can see the error message for all of these bad strings. Probably
-have to generate and prune this list manually in stages since there
-will be too many.
-
-Contexts:
-
-another area to focus on is contexts: for instance, we have a set of
-e.g. 1000 bad scalar expressions with error messages. Now can put
-those bad scalar expressions into various contexts and see that the
-error messages are still good.
-
-plan:
-
-1. create a list of all the value expression, with some variations for
-   each
-2. manually create some error variations for each expression
-3. create a renderer which will create a csv of the expressions and
-   the errors
-   this is to load as a spreadsheet to investigate more
-4. create a renderer for the csv which will create a markdown file for
-   the website. this is to demonstrate the error messages in the
-   documentation
-
-Then create some contexts for all of these: inside another value
-expression, or inside a query expression. Do the same: render and
-review the error messages.
-
-Then, create some query expressions to focus on the non value
-expression parts.
-
-
-> module Language.SQL.SimpleSQL.ErrorMessages where
-
-> {-import Language.SQL.SimpleSQL.Parser
-> import Data.List
-> import Text.Groom
-
-> valueExpressions :: [String]
-> valueExpressions =
->     ["10.."
->     ,"..10"
->     ,"10e1e2"
->     ,"10e--3"
->     ,"1a"
->     ,"1%"
-
->     ,"'b'ad'"
->     ,"'bad"
->     ,"bad'"
-
->     ,"interval '5' ay"
->     ,"interval '5' day (4.4)"
->     ,"interval '5' day (a)"
->     ,"intervala '5' day"
->     ,"interval 'x' day (3"
->     ,"interval 'x' day 3)"
-
->     ,"1badiden"
->     ,"$"
->     ,"!"
->     ,"*.a"
-
->     ,"??"
->     ,"3?"
->     ,"?a"
-
->     ,"row"
->     ,"row 1,2"
->     ,"row(1,2"
->     ,"row 1,2)"
->     ,"row(1 2)"
-
->     ,"f("
->     ,"f)"
-
->     ,"f(a"
->     ,"f a)"
->     ,"f(a b)"
-
-TODO:
-case
-operators
-
->    ,"a + (b + c"
-
-casts
-subqueries: + whole set of parentheses use
-in list
-'keyword' functions
-aggregates
-window functions
-
-
->    ]
-
-> queryExpressions :: [String]
-> queryExpressions =
->     map sl1 valueExpressions
->     ++ map sl2 valueExpressions
->     ++ map sl3 valueExpressions
->     ++
->     ["select a from t inner jin u"]
->   where
->     sl1 x = "select " ++ x ++ " from t"
->     sl2 x = "select " ++ x ++ ", y from t"
->     sl3 x = "select " ++ x ++ " fom t"
-
-> valExprs :: [String] -> [(String,String)]
-> valExprs = map parseOne
->   where
->     parseOne x = let p = parseValueExpr "" Nothing x
->                  in (x,either peFormattedError (\x -> "ERROR: parsed ok " ++ groom x) p)
-
-
-> queryExprs :: [String] -> [(String,String)]
-> queryExprs = map parseOne
->   where
->     parseOne x = let p = parseQueryExpr "" Nothing x
->                  in (x,either peFormattedError (\x -> "ERROR: parsed ok " ++ groom x) p)
-
-
-> pExprs :: [String] -> [String] -> String
-> pExprs x y =
->     let l = valExprs x ++ queryExprs y
->     in intercalate "\n\n\n\n" $ map (\(a,b) -> a ++ "\n" ++ b) l
-> -}
diff --git a/tools/Language/SQL/SimpleSQL/FullQueries.lhs b/tools/Language/SQL/SimpleSQL/FullQueries.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/FullQueries.lhs
+++ /dev/null
@@ -1,39 +0,0 @@
-
-Some tests for parsing full queries.
-
-> 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 ansi2011))
->     [("select count(*) from t"
->      ,makeSelect
->       {qeSelectList = [(App [Name Nothing "count"] [Star], Nothing)]
->       ,qeFrom = [TRSimple [Name Nothing "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 = [(Iden [Name Nothing "a"], Nothing)
->                       ,(App [Name Nothing "sum"]
->                         [BinOp (Iden [Name Nothing "c"])
->                                [Name Nothing "+"] (Iden [Name Nothing "d"])]
->                        ,Just $ Name Nothing "s")]
->       ,qeFrom = [TRSimple [Name Nothing "t"], TRSimple [Name Nothing "u"]]
->       ,qeWhere = Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5")
->       ,qeGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]]
->       ,qeHaving = Just $ BinOp (App [Name Nothing "count"] [NumLit "1"])
->                                [Name Nothing ">"] (NumLit "5")
->       ,qeOrderBy = [SortSpec (Iden [Name Nothing "s"]) DirDefault NullsOrderDefault]
->       }
->      )
->     ]
diff --git a/tools/Language/SQL/SimpleSQL/GroupBy.lhs b/tools/Language/SQL/SimpleSQL/GroupBy.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/GroupBy.lhs
+++ /dev/null
@@ -1,235 +0,0 @@
-
-Here are the tests for the group by component of query exprs
-
-> 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 ansi2011))
->     [("select a,sum(b) from t group by a"
->      ,makeSelect {qeSelectList = [(Iden [Name Nothing "a"],Nothing)
->                                  ,(App [Name Nothing "sum"] [Iden [Name Nothing "b"]],Nothing)]
->                  ,qeFrom = [TRSimple [Name Nothing "t"]]
->                  ,qeGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]]
->                  })
-
->     ,("select a,b,sum(c) from t group by a,b"
->      ,makeSelect {qeSelectList = [(Iden [Name Nothing "a"],Nothing)
->                                  ,(Iden [Name Nothing "b"],Nothing)
->                                  ,(App [Name Nothing "sum"] [Iden [Name Nothing "c"]],Nothing)]
->                  ,qeFrom = [TRSimple [Name Nothing "t"]]
->                  ,qeGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]
->                               ,SimpleGroup $ Iden [Name Nothing "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 ansi2011))
->     [("select * from t group by ()", ms [GroupingParens []])
->     ,("select * from t group by grouping sets ((), (a))"
->      ,ms [GroupingSets [GroupingParens []
->                        ,GroupingParens [SimpleGroup $ Iden [Name Nothing "a"]]]])
->     ,("select * from t group by cube(a,b)"
->      ,ms [Cube [SimpleGroup $ Iden [Name Nothing "a"], SimpleGroup $ Iden [Name Nothing "b"]]])
->     ,("select * from t group by rollup(a,b)"
->      ,ms [Rollup [SimpleGroup $ Iden [Name Nothing "a"], SimpleGroup $ Iden [Name Nothing "b"]]])
->     ]
->   where
->     ms g = makeSelect {qeSelectList = [(Star,Nothing)]
->                       ,qeFrom = [TRSimple [Name Nothing "t"]]
->                       ,qeGroupBy = g}
-
-> randomGroupBy :: TestItem
-> randomGroupBy = Group "randomGroupBy" $ map (ParseQueryExpr ansi2011)
->     ["select * from t GROUP BY a"
->     ,"select * from t GROUP BY GROUPING SETS((a))"
->     ,"select * from t GROUP BY a,b,c"
->     ,"select * from t GROUP BY GROUPING SETS((a,b,c))"
->     ,"select * from t GROUP BY ROLLUP(a,b)"
->     ,"select * from t GROUP BY GROUPING SETS((a,b),\n\
->      \(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
-
->     -- decimal as a function not allowed due to the reserved keyword
->     -- handling: todo, review if this is ansi standard function or
->     -- if there are places where reserved keywords can still be used
->     ,"SELECT MONTH(SALES_DATE) AS MONTH,\n\
->      \REGION,\n\
->      \SUM(SALES) AS UNITS_SOLD,\n\
->      \MAX(SALES) AS BEST_SALE,\n\
->      \CAST(ROUND(AVG(DECIMALx(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/LexerTests.lhs b/tools/Language/SQL/SimpleSQL/LexerTests.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/LexerTests.lhs
+++ /dev/null
@@ -1,335 +0,0 @@
-
-
-Test for the lexer
-
-> module Language.SQL.SimpleSQL.LexerTests (lexerTests) where
-
-> import Language.SQL.SimpleSQL.TestTypes
-> import Language.SQL.SimpleSQL.Lex (Token(..),tokenListWillPrintAndLex)
-> --import Debug.Trace
-> --import Data.Char (isAlpha)
-> import Data.List
-
-> lexerTests :: TestItem
-> lexerTests = Group "lexerTests" $
->     [Group "lexer token tests" [ansiLexerTests
->                                ,postgresLexerTests
->                                ,sqlServerLexerTests
->                                ,oracleLexerTests
->                                ,mySqlLexerTests
->                                ,odbcLexerTests]]
-
-> ansiLexerTable :: [(String,[Token])]
-> ansiLexerTable =
->     -- single char symbols
->     map (\s -> ([s],[Symbol [s]])) "+-^*/%~&|?<>[]=,;()"
->     -- multi char symbols
->     ++ map (\s -> (s,[Symbol s])) [">=","<=","!=","<>","||"]
->     ++ (let idens = ["a", "_a", "test", "table", "Stuff", "STUFF"]
->         -- simple identifiers
->         in map (\i -> (i, [Identifier Nothing i])) idens
->            ++ map (\i -> ("\"" ++ i ++ "\"", [Identifier (Just ("\"","\"")) i])) idens
->            -- todo: in order to make lex . pretty id, need to
->            -- preserve the case of the u
->            ++ map (\i -> ("u&\"" ++ i ++ "\"", [Identifier (Just ("u&\"","\"")) i])) idens
->            -- host param
->            ++ map (\i -> (':':i, [PrefixedVariable ':' i])) idens
->        )
->     -- quoted identifiers with embedded double quotes
->     -- the lexer doesn't unescape the quotes
->     ++ [("\"normal \"\" iden\"", [Identifier (Just ("\"","\"")) "normal \"\" iden"])]
->     -- strings
->     -- the lexer doesn't apply escapes at all
->     ++ [("'string'", [SqlString "'" "'" "string"])
->        ,("'normal '' quote'", [SqlString "'" "'" "normal '' quote"])
->        ,("'normalendquote '''", [SqlString "'" "'" "normalendquote ''"])
->        ,("'\n'", [SqlString "'" "'" "\n"])]
->     -- csstrings
->     ++ map (\c -> (c ++ "'test'", [SqlString (c ++ "'") "'" "test"]))
->        ["n", "N","b", "B","x", "X", "u&"]
->     -- numbers
->     ++ [("10", [SqlNumber "10"])
->        ,(".1", [SqlNumber ".1"])
->        ,("5e3", [SqlNumber "5e3"])
->        ,("5e+3", [SqlNumber "5e+3"])
->        ,("5e-3", [SqlNumber "5e-3"])
->        ,("10.2", [SqlNumber "10.2"])
->        ,("10.2e7", [SqlNumber "10.2e7"])]
->     -- whitespace
->     ++ concat [[([a],[Whitespace [a]])
->                ,([a,b], [Whitespace [a,b]])]
->               | a <- " \n\t", b <- " \n\t"]
->     -- line comment
->     ++ map (\c -> (c, [LineComment c]))
->        ["--", "-- ", "-- this is a comment", "-- line com\n"]
->     -- block comment
->     ++ map (\c -> (c, [BlockComment c]))
->        ["/**/", "/* */","/* this is a comment */"
->        ,"/* this *is/ a comment */"
->        ]
-
-> ansiLexerTests :: TestItem
-> ansiLexerTests = Group "ansiLexerTests" $
->     [Group "ansi lexer token tests" $ [LexTest ansi2011 s t |  (s,t) <- ansiLexerTable]
->     ,Group "ansi generated combination lexer tests" $
->     [ LexTest ansi2011 (s ++ s1) (t ++ t1)
->     | (s,t) <- ansiLexerTable
->     , (s1,t1) <- ansiLexerTable
->     , tokenListWillPrintAndLex ansi2011 $ t ++ t1
-
->     ]
->     ,Group "ansiadhoclexertests" $
->        map (uncurry $ LexTest ansi2011)
->        [("", [])
->        ,("-- line com\nstuff", [LineComment "-- line com\n",Identifier Nothing "stuff"])
->        ] ++
->        [-- want to make sure this gives a parse error
->         LexFails ansi2011 "*/"
->         -- combinations of pipes: make sure they fail because they could be
->         -- ambiguous and it is really unclear when they are or not, and
->         -- what the result is even when they are not ambiguous
->        ,LexFails ansi2011 "|||"
->        ,LexFails ansi2011 "||||"
->        ,LexFails ansi2011 "|||||"
->        -- another user experience thing: make sure extra trailing
->        -- number chars are rejected rather than attempting to parse
->        -- if the user means to write something that is rejected by this code,
->        -- then they can use whitespace to make it clear and then it will parse
->        ,LexFails ansi2011 "12e3e4"
->        ,LexFails ansi2011 "12e3e4"
->        ,LexFails ansi2011 "12e3e4"
->        ,LexFails ansi2011 "12e3.4"
->        ,LexFails ansi2011 "12.4.5"
->        ,LexFails ansi2011 "12.4e5.6"
->        ,LexFails ansi2011 "12.4e5e7"]
->      ]
-
-todo: lexing tests
-do quickcheck testing:
-can try to generate valid tokens then check they parse
-
-same as above: can also try to pair tokens, create an accurate
-  function to say which ones can appear adjacent, and test
-
-I think this plus the explicit lists of tokens like above which do
-basic sanity + explicit edge casts will provide a high level of
-assurance.
-
-
-
-> postgresLexerTable :: [(String,[Token])]
-> postgresLexerTable =
->     -- single char symbols
->     map (\s -> ([s],[Symbol [s]])) "+-^*/%~&|?<>[]=,;():"
->     -- multi char symbols
->     ++ map (\s -> (s,[Symbol s])) [">=","<=","!=","<>","||", "::","..",":="]
->     -- generic symbols
-
->     ++ (let idens = ["a", "_a", "test", "table", "Stuff", "STUFF"]
->         -- simple identifiers
->         in map (\i -> (i, [Identifier Nothing i])) idens
->            ++ map (\i -> ("\"" ++ i ++ "\"", [Identifier (Just ("\"","\"")) i])) idens
->            -- todo: in order to make lex . pretty id, need to
->            -- preserve the case of the u
->            ++ map (\i -> ("u&\"" ++ i ++ "\"", [Identifier (Just ("u&\"","\"")) i])) idens
->            -- host param
->            ++ map (\i -> (':':i, [PrefixedVariable ':' i])) idens
->        )
->     -- positional var
->     ++ [("$1", [PositionalArg 1])]
->     -- quoted identifiers with embedded double quotes
->     ++ [("\"normal \"\" iden\"", [Identifier (Just ("\"","\"")) "normal \"\" iden"])]
->     -- strings
->     ++ [("'string'", [SqlString "'" "'" "string"])
->        ,("'normal '' quote'", [SqlString "'" "'" "normal '' quote"])
->        ,("'normalendquote '''", [SqlString "'" "'" "normalendquote ''"])
->        ,("'\n'", [SqlString "'" "'" "\n"])
->        ,("E'\n'", [SqlString "E'" "'" "\n"])
->        ,("e'this '' quote'", [SqlString "e'" "'" "this '' quote"])
->        ,("e'this \\' quote'", [SqlString "e'" "'" "this \\' quote"])
->        ,("'not this \\' quote", [SqlString "'" "'" "not this \\"
->                                 ,Whitespace " "
->                                 ,Identifier Nothing "quote"])
->        ,("$$ string 1 $$", [SqlString "$$" "$$" " string 1 "])
->        ,("$$ string $ 2 $$", [SqlString "$$" "$$" " string $ 2 "])
->        ,("$a$ $$string 3$$ $a$", [SqlString "$a$" "$a$" " $$string 3$$ "])
->        ]
->     -- csstrings
->     ++ map (\c -> (c ++ "'test'", [SqlString (c ++ "'") "'" "test"]))
->        ["n", "N","b", "B","x", "X", "u&", "e", "E"]
->     -- numbers
->     ++ [("10", [SqlNumber "10"])
->        ,(".1", [SqlNumber ".1"])
->        ,("5e3", [SqlNumber "5e3"])
->        ,("5e+3", [SqlNumber "5e+3"])
->        ,("5e-3", [SqlNumber "5e-3"])
->        ,("10.2", [SqlNumber "10.2"])
->        ,("10.2e7", [SqlNumber "10.2e7"])]
->     -- whitespace
->     ++ concat [[([a],[Whitespace [a]])
->                ,([a,b], [Whitespace [a,b]])]
->               | a <- " \n\t", b <- " \n\t"]
->     -- line comment
->     ++ map (\c -> (c, [LineComment c]))
->        ["--", "-- ", "-- this is a comment", "-- line com\n"]
->     -- block comment
->     ++ map (\c -> (c, [BlockComment c]))
->        ["/**/", "/* */","/* this is a comment */"
->        ,"/* this *is/ a comment */"
->        ]
-
-An operator name is a sequence of up to NAMEDATALEN-1 (63 by default) characters from the following list:
-
-+ - * / < > = ~ ! @ # % ^ & | ` ?
-
-There are a few restrictions on operator names, however:
--- and /* cannot appear anywhere in an operator name, since they will be taken as the start of a comment.
-
-A multiple-character operator name cannot end in + or -, unless the name also contains at least one of these characters:
-
-~ ! @ # % ^ & | ` ?
-
-todo: 'negative' tests
-symbol then --
-symbol then /*
-operators without one of the exception chars
-  followed by + or - without whitespace
-
-also: do the testing for the ansi compatibility special cases
-
-> postgresShortOperatorTable :: [(String,[Token])]
-> postgresShortOperatorTable =
->     [ (x, [Symbol x]) | x <- someValidPostgresOperators 2]
-
-
-> postgresExtraOperatorTable :: [(String,[Token])]
-> postgresExtraOperatorTable =
->     [ (x, [Symbol x]) | x <- someValidPostgresOperators 4]
-
-
-> someValidPostgresOperators :: Int -> [String]
-> someValidPostgresOperators l =
->        [ x
->        | n <- [1..l]
->        , x <- combos "+-*/<>=~!@#%^&|`?" n
->        , not ("--" `isInfixOf` x || "/*" `isInfixOf` x || "*/" `isInfixOf` x)
->        , not (last x `elem` "+-")
->          || or (map (`elem` x) "~!@#%^&|`?")
->        ]
-
-These are postgres operators, which if followed immediately by a + or
--, will lex as separate operators rather than one operator including
-the + or -.
-
-> somePostgresOpsWhichWontAddTrailingPlusMinus :: Int -> [String]
-> somePostgresOpsWhichWontAddTrailingPlusMinus l =
->        [ x
->        | n <- [1..l]
->        , x <- combos "+-*/<>=" n
->        , not ("--" `isInfixOf` x || "/*" `isInfixOf` x || "*/" `isInfixOf` x)
->        , not (last x `elem` "+-")
->        ]
-
-
-> postgresLexerTests :: TestItem
-> postgresLexerTests = Group "postgresLexerTests" $
->     [Group "postgres lexer token tests" $
->      [LexTest postgres s t | (s,t) <- postgresLexerTable]
->     ,Group "postgres generated lexer token tests" $
->      [LexTest postgres s t | (s,t) <- postgresShortOperatorTable ++ postgresExtraOperatorTable]
->     ,Group "postgres generated combination lexer tests" $
->     [ LexTest postgres (s ++ s1) (t ++ t1)
->     | (s,t) <- postgresLexerTable ++ postgresShortOperatorTable
->     , (s1,t1) <- postgresLexerTable ++ postgresShortOperatorTable
->     , tokenListWillPrintAndLex postgres $ t ++ t1
-
->     ]
->     ,Group "generated postgres edgecase lexertests" $
->      [LexTest postgres s t
->      | (s,t) <- edgeCaseCommentOps
->                 ++ edgeCasePlusMinusOps
->                 ++ edgeCasePlusMinusComments]
-
->     ,Group "adhoc postgres lexertests" $
->       -- need more tests for */ to make sure it is caught if it is in the middle of a
->       -- sequence of symbol letters
->         [LexFails postgres "*/"
->         ,LexFails postgres ":::"
->         ,LexFails postgres "::::"
->         ,LexFails postgres ":::::"
->         ,LexFails postgres "@*/"
->         ,LexFails postgres "-*/"
->         ,LexFails postgres "12e3e4"
->         ,LexFails postgres "12e3e4"
->         ,LexFails postgres "12e3e4"
->         ,LexFails postgres "12e3.4"
->         ,LexFails postgres "12.4.5"
->         ,LexFails postgres "12.4e5.6"
->         ,LexFails postgres "12.4e5e7"
->          -- special case allow this to lex to 1 .. 2
->          -- this is for 'for loops' in plpgsql
->         ,LexTest postgres "1..2" [SqlNumber "1", Symbol "..", SqlNumber "2"]]
->     ]
->  where
->    edgeCaseCommentOps =
->      [ (x ++ "/*<test*/", [Symbol x, BlockComment "/*<test*/"])
->      | x <- eccops
->      , not (last x == '*')
->      ] ++
->      [ (x ++ "--<test", [Symbol x, LineComment "--<test"])
->      | x <- eccops
->      , not (last x == '-')
->      ]
->    eccops = someValidPostgresOperators 2
->    edgeCasePlusMinusOps = concat
->      [ [ (x ++ "+", [Symbol x, Symbol "+"])
->        , (x ++ "-", [Symbol x, Symbol "-"]) ]
->      | x <- somePostgresOpsWhichWontAddTrailingPlusMinus 2
->      ]
->    edgeCasePlusMinusComments =
->      [("---", [LineComment "---"])
->      ,("+--", [Symbol "+", LineComment "--"])
->      ,("-/**/", [Symbol "-", BlockComment "/**/"])
->      ,("+/**/", [Symbol "+", BlockComment "/**/"])
->      ]
-
-
-> sqlServerLexerTests :: TestItem
-> sqlServerLexerTests = Group "sqlServerLexTests" $
->     [ LexTest sqlserver s t | (s,t) <-
->     [("@variable", [(PrefixedVariable '@' "variable")])
->     ,("#variable", [(PrefixedVariable '#' "variable")])
->     ,("[quoted identifier]", [(Identifier (Just ("[", "]")) "quoted identifier")])
->     ]]
-
-> oracleLexerTests :: TestItem
-> oracleLexerTests = Group "oracleLexTests" $
->     [] -- nothing oracle specific atm
-
-> mySqlLexerTests :: TestItem
-> mySqlLexerTests = Group "mySqlLexerTests" $
->     [ LexTest mysql s t | (s,t) <-
->     [("`quoted identifier`", [(Identifier (Just ("`", "`")) "quoted identifier")])
->     ]
->     ]
-
-> odbcLexerTests :: TestItem
-> odbcLexerTests = Group "odbcLexTests" $
->     [ LexTest sqlserver {diOdbc = True} s t | (s,t) <-
->     [("{}", [Symbol "{", Symbol "}"])
->     ]]
->     ++ [LexFails sqlserver "{"
->        ,LexFails sqlserver "}"]
-
-> combos :: [a] -> Int -> [[a]]
-> combos _ 0 = [[]]
-> combos l n = [ x:tl | x <- l, tl <- combos l (n - 1) ]
-
-figure out a way to do quickcheck testing:
-1. generate valid tokens and check they parse
-
-2. combine two generated tokens together for the combo testing
-
-this especially will work much better for the postgresql extensible
-operator tests which doing exhaustively takes ages and doesn't bring
-much benefit over testing a few using quickcheck.
diff --git a/tools/Language/SQL/SimpleSQL/MySQL.lhs b/tools/Language/SQL/SimpleSQL/MySQL.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/MySQL.lhs
+++ /dev/null
@@ -1,40 +0,0 @@
-
-Tests for mysql dialect parsing
-
-> module Language.SQL.SimpleSQL.MySQL (mySQLTests) where
-
-> import Language.SQL.SimpleSQL.TestTypes
-> import Language.SQL.SimpleSQL.Syntax
-
-> mySQLTests :: TestItem
-> mySQLTests = Group "mysql dialect"
->     [backtickQuotes
->     ,limit]
-
-backtick quotes
-
-limit syntax
-
-[LIMIT {[offset,] row_count | row_count OFFSET offset}]
-
-> backtickQuotes :: TestItem
-> backtickQuotes = Group "backtickQuotes" (map (uncurry (TestScalarExpr mysql))
->     [("`test`", Iden [Name (Just ("`","`")) "test"])
->     ]
->     ++ [ParseScalarExprFails ansi2011 "`test`"]
->     )
-
-> limit :: TestItem
-> limit = Group "queries" ( map (uncurry (TestQueryExpr mysql))
->     [("select * from t limit 5"
->      ,sel {qeFetchFirst = Just (NumLit "5")}
->      )
->     ]
->     ++ [ParseQueryExprFails mysql "select a from t fetch next 10 rows only;"
->        ,ParseQueryExprFails ansi2011 "select * from t limit 5"]
->     )
->   where
->     sel = makeSelect
->           {qeSelectList = [(Star, Nothing)]
->           ,qeFrom = [TRSimple [Name Nothing "t"]]
->           }
diff --git a/tools/Language/SQL/SimpleSQL/Odbc.lhs b/tools/Language/SQL/SimpleSQL/Odbc.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/Odbc.lhs
+++ /dev/null
@@ -1,52 +0,0 @@
-
-> module Language.SQL.SimpleSQL.Odbc (odbcTests) where
-
-> import Language.SQL.SimpleSQL.TestTypes
-> import Language.SQL.SimpleSQL.Syntax
-
-> odbcTests :: TestItem
-> odbcTests = Group "odbc" [
->        Group "datetime" [
->            e "{d '2000-01-01'}" (OdbcLiteral OLDate "2000-01-01")
->           ,e "{t '12:00:01.1'}" (OdbcLiteral OLTime "12:00:01.1")
->           ,e "{ts '2000-01-01 12:00:01.1'}"
->                (OdbcLiteral OLTimestamp "2000-01-01 12:00:01.1")
->        ]
->        ,Group "functions" [
->              e "{fn CHARACTER_LENGTH(string_exp)}"
->              $ OdbcFunc (ap "CHARACTER_LENGTH" [iden "string_exp"])
->             ,e "{fn EXTRACT(day from t)}"
->             $ OdbcFunc (SpecialOpK [Name Nothing "extract"] (Just $ Iden [Name Nothing "day"]) [("from", Iden [Name Nothing "t"])])
->             ,e "{fn now()}"
->              $ OdbcFunc (ap "now" [])
->             ,e "{fn CONVERT('2000-01-01', SQL_DATE)}"
->              $ OdbcFunc (ap "CONVERT"
->               [StringLit "'" "'" "2000-01-01"
->               ,iden "SQL_DATE"])
->             ,e "{fn CONVERT({fn CURDATE()}, SQL_DATE)}"
->              $ OdbcFunc (ap "CONVERT"
->               [OdbcFunc (ap "CURDATE" [])
->               ,iden "SQL_DATE"])
->             ]
->        ,Group "outer join" [
->              TestQueryExpr ansi2011 {diOdbc=True}
->              "select * from {oj t1 left outer join t2 on expr}"
->              $ makeSelect
->                    {qeSelectList = [(Star,Nothing)]
->                    ,qeFrom = [TROdbc $ TRJoin (TRSimple [Name Nothing "t1"]) False JLeft (TRSimple [Name Nothing "t2"])
->                                          (Just $ JoinOn $ Iden [Name Nothing "expr"])]}]
->        ,Group "check parsing bugs" [
->              TestQueryExpr ansi2011 {diOdbc=True}
->              "select {fn CONVERT(cint,SQL_BIGINT)} from t;"
->              $ makeSelect
->                    {qeSelectList = [(OdbcFunc (ap "CONVERT"
->                                                       [iden "cint"
->                                                       ,iden "SQL_BIGINT"]), Nothing)]
->                    ,qeFrom = [TRSimple [Name Nothing "t"]]}]
->        ]
->   where
->     e = TestScalarExpr ansi2011 {diOdbc = True}
->     --tsql = ParseProcSql defaultParseFlags {pfDialect=sqlServerDialect}
->     ap n = App [Name Nothing n]
->     iden n = Iden [Name Nothing n]
-
diff --git a/tools/Language/SQL/SimpleSQL/Oracle.lhs b/tools/Language/SQL/SimpleSQL/Oracle.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/Oracle.lhs
+++ /dev/null
@@ -1,29 +0,0 @@
-
-Tests for oracle dialect parsing
-
-> module Language.SQL.SimpleSQL.Oracle (oracleTests) where
-
-> import Language.SQL.SimpleSQL.TestTypes
-> import Language.SQL.SimpleSQL.Syntax
-
-> oracleTests :: TestItem
-> oracleTests = Group "oracle dialect"
->     [oracleLobUnits]
-
-
-> oracleLobUnits :: TestItem
-> oracleLobUnits = Group "oracleLobUnits" (map (uncurry (TestScalarExpr oracle))
->     [("cast (a as varchar2(3 char))"
->      ,Cast (Iden [Name Nothing "a"]) (
->          PrecLengthTypeName [Name Nothing "varchar2"] 3 Nothing (Just PrecCharacters)))
->      ,("cast (a as varchar2(3 byte))"
->      ,Cast (Iden [Name Nothing "a"]) (
->          PrecLengthTypeName [Name Nothing "varchar2"] 3 Nothing (Just PrecOctets)))
->      ]
->     ++ [TestStatement oracle
->       "create table t (a varchar2(55 BYTE));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a")
->         (PrecLengthTypeName [Name Nothing "varchar2"] 55 Nothing (Just PrecOctets))
->         Nothing []]]
->     )
diff --git a/tools/Language/SQL/SimpleSQL/Postgres.lhs b/tools/Language/SQL/SimpleSQL/Postgres.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/Postgres.lhs
+++ /dev/null
@@ -1,274 +0,0 @@
-
-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
-
-> postgresTests :: TestItem
-> postgresTests = Group "postgresTests" $ map (ParseQueryExpr ansi2011)
-
-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');"
-
->     -- table is a reservered keyword?
->     --,"SELECT ROW(table.*) IS NULL FROM table;"
->     ,"SELECT ROW(tablex.*) IS NULL FROM tablex;"
-
->     ,"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;"
->     -- s.date changed to s.datex because of reserved keyword
->     -- handling, not sure if this is correct or not for ansi sql
->     ,"SELECT product_id, p.name, (sum(s.units) * p.price) AS sales\n\
->      \    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.datex > CURRENT_DATE - INTERVAL '4 weeks'\n\
->      \    GROUP BY product_id, p.name, p.price, p.cost\n\
->      \    HAVING sum(p.price * s.units) > 5000;"
-
->     ,"SELECT a, b, c FROM t"
-
->     ,"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;" -- limit is not standard
-
-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;"
-
->     -- simple-sql-parser doesn't support where without from
->     -- this can be added for the postgres dialect when it is written
->     --,"SELECT distributors.* WHERE distributors.name = 'Westward';"
-
->     ]
diff --git a/tools/Language/SQL/SimpleSQL/QueryExprComponents.lhs b/tools/Language/SQL/SimpleSQL/QueryExprComponents.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/QueryExprComponents.lhs
+++ /dev/null
@@ -1,209 +0,0 @@
-
-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.
-
-> 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 ansi2011))
->     [("select a from t" ,ms SQDefault)
->     ,("select all a from t" ,ms All)
->     ,("select distinct a from t", ms Distinct)
->     ]
->  where
->    ms d = makeSelect
->           {qeSetQuantifier = d
->           ,qeSelectList = [(Iden [Name Nothing "a"],Nothing)]
->           ,qeFrom = [TRSimple [Name Nothing "t"]]}
-
-> selectLists :: TestItem
-> selectLists = Group "selectLists" $ map (uncurry (TestQueryExpr ansi2011))
->     [("select 1",
->       makeSelect {qeSelectList = [(NumLit "1",Nothing)]})
-
->     ,("select a"
->      ,makeSelect {qeSelectList = [(Iden [Name Nothing "a"],Nothing)]})
-
->     ,("select a,b"
->      ,makeSelect {qeSelectList = [(Iden [Name Nothing "a"],Nothing)
->                                  ,(Iden [Name Nothing "b"],Nothing)]})
-
->     ,("select 1+2,3+4"
->      ,makeSelect {qeSelectList =
->                      [(BinOp (NumLit "1") [Name Nothing "+"] (NumLit "2"),Nothing)
->                      ,(BinOp (NumLit "3") [Name Nothing "+"] (NumLit "4"),Nothing)]})
-
->     ,("select a as a, /*comment*/ b as b"
->      ,makeSelect {qeSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "a")
->                                  ,(Iden [Name Nothing "b"], Just $ Name Nothing "b")]})
-
->     ,("select a a, b b"
->      ,makeSelect {qeSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "a")
->                                  ,(Iden [Name Nothing "b"], Just $ Name Nothing "b")]})
-
->     ,("select a + b * c"
->      ,makeSelect {qeSelectList =
->       [(BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"]
->         (BinOp (Iden [Name Nothing "b"]) [Name Nothing "*"] (Iden [Name Nothing "c"]))
->        ,Nothing)]})
-
->     ]
-
-> whereClause :: TestItem
-> whereClause = Group "whereClause" $ map (uncurry (TestQueryExpr ansi2011))
->     [("select a from t where a = 5"
->      ,makeSelect {qeSelectList = [(Iden [Name Nothing "a"],Nothing)]
->                  ,qeFrom = [TRSimple [Name Nothing "t"]]
->                  ,qeWhere = Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "5")})
->     ]
-
-> having :: TestItem
-> having = Group "having" $ map (uncurry (TestQueryExpr ansi2011))
->     [("select a,sum(b) from t group by a having sum(b) > 5"
->      ,makeSelect {qeSelectList = [(Iden [Name Nothing "a"],Nothing)
->                                  ,(App [Name Nothing "sum"] [Iden [Name Nothing "b"]],Nothing)]
->                  ,qeFrom = [TRSimple [Name Nothing "t"]]
->                  ,qeGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]]
->                  ,qeHaving = Just $ BinOp (App [Name Nothing "sum"] [Iden [Name Nothing "b"]])
->                                           [Name Nothing ">"] (NumLit "5")
->                  })
->     ]
-
-> orderBy :: TestItem
-> orderBy = Group "orderBy" $ map (uncurry (TestQueryExpr ansi2011))
->     [("select a from t order by a"
->      ,ms [SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault])
-
->     ,("select a from t order by a, b"
->      ,ms [SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault
->          ,SortSpec (Iden [Name Nothing "b"]) DirDefault NullsOrderDefault])
-
->     ,("select a from t order by a asc"
->      ,ms [SortSpec (Iden [Name Nothing "a"]) Asc NullsOrderDefault])
-
->     ,("select a from t order by a desc, b desc"
->      ,ms [SortSpec (Iden [Name Nothing "a"]) Desc NullsOrderDefault
->          ,SortSpec (Iden [Name Nothing "b"]) Desc NullsOrderDefault])
-
->     ,("select a from t order by a desc nulls first, b desc nulls last"
->      ,ms [SortSpec (Iden [Name Nothing "a"]) Desc NullsFirst
->          ,SortSpec (Iden [Name Nothing "b"]) Desc NullsLast])
-
->     ]
->   where
->     ms o = makeSelect {qeSelectList = [(Iden [Name Nothing "a"],Nothing)]
->                       ,qeFrom = [TRSimple [Name Nothing "t"]]
->                       ,qeOrderBy = o}
-
-> offsetFetch :: TestItem
-> offsetFetch = Group "offsetFetch" $ map (uncurry (TestQueryExpr ansi2011))
->     [-- 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: disabled, will add back when postgres
->      -- dialect is added
->     --,("select a from t limit 10 offset 5"
->     -- ,ms (Just $ NumLit "5") (Just $ NumLit "10"))
->     ]
->   where
->     ms o l = makeSelect
->              {qeSelectList = [(Iden [Name Nothing "a"],Nothing)]
->              ,qeFrom = [TRSimple [Name Nothing "t"]]
->              ,qeOffset = o
->              ,qeFetchFirst = l}
-
-> combos :: TestItem
-> combos = Group "combos" $ map (uncurry (TestQueryExpr ansi2011))
->     [("select a from t union select b from u"
->      ,QueryExprSetOp ms1 Union SQDefault Respectively ms2)
-
->     ,("select a from t intersect select b from u"
->      ,QueryExprSetOp ms1 Intersect SQDefault Respectively ms2)
-
->     ,("select a from t except all select b from u"
->      ,QueryExprSetOp ms1 Except All Respectively ms2)
-
->     ,("select a from t union distinct corresponding \
->       \select b from u"
->      ,QueryExprSetOp 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)
->      ,QueryExprSetOp ms1 Union SQDefault Respectively
->        (QueryExprSetOp ms1 Union SQDefault Respectively ms1))
->     ]
->   where
->     ms1 = makeSelect
->           {qeSelectList = [(Iden [Name Nothing "a"],Nothing)]
->           ,qeFrom = [TRSimple [Name Nothing "t"]]}
->     ms2 = makeSelect
->           {qeSelectList = [(Iden [Name Nothing "b"],Nothing)]
->           ,qeFrom = [TRSimple [Name Nothing "u"]]}
-
-
-> withQueries :: TestItem
-> withQueries = Group "with queries" $ map (uncurry (TestQueryExpr ansi2011))
->     [("with u as (select a from t) select a from u"
->      ,With False [(Alias (Name Nothing "u") Nothing, ms1)] ms2)
-
->     ,("with u(b) as (select a from t) select a from u"
->      ,With False [(Alias (Name Nothing "u") (Just [Name Nothing "b"]), ms1)] ms2)
-
->     ,("with x as (select a from t),\n\
->       \     u as (select a from x)\n\
->       \select a from u"
->      ,With False [(Alias (Name Nothing "x") Nothing, ms1), (Alias (Name Nothing "u") Nothing,ms3)] ms2)
-
->     ,("with recursive u as (select a from t) select a from u"
->      ,With True [(Alias (Name Nothing "u") Nothing, ms1)] ms2)
->     ]
->  where
->    ms c t = makeSelect
->             {qeSelectList = [(Iden [Name Nothing c],Nothing)]
->             ,qeFrom = [TRSimple [Name Nothing t]]}
->    ms1 = ms "a" "t"
->    ms2 = ms "a" "u"
->    ms3 = ms "a" "x"
-
-> values :: TestItem
-> values = Group "values" $ map (uncurry (TestQueryExpr ansi2011))
->     [("values (1,2),(3,4)"
->       ,Values [[NumLit "1", NumLit "2"]
->               ,[NumLit "3", NumLit "4"]])
->     ]
-
-> tables :: TestItem
-> tables = Group "tables" $ map (uncurry (TestQueryExpr ansi2011))
->     [("table tbl", Table [Name Nothing "tbl"])
->     ]
diff --git a/tools/Language/SQL/SimpleSQL/QueryExprs.lhs b/tools/Language/SQL/SimpleSQL/QueryExprs.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/QueryExprs.lhs
+++ /dev/null
@@ -1,24 +0,0 @@
-
-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 (TestStatements ansi2011))
->     [("select 1",[ms])
->     ,("select 1;",[ms])
->     ,("select 1;select 1",[ms,ms])
->     ,(" select 1;select 1; ",[ms,ms])
->     ,("SELECT CURRENT_TIMESTAMP;"
->      ,[SelectStatement $ makeSelect
->       {qeSelectList = [(Iden [Name Nothing "CURRENT_TIMESTAMP"],Nothing)]}])
->     ,("SELECT \"CURRENT_TIMESTAMP\";"
->      ,[SelectStatement $ makeSelect
->       {qeSelectList = [(Iden [Name (Just ("\"","\"")) "CURRENT_TIMESTAMP"],Nothing)]}])
->     ]
->   where
->     ms = SelectStatement $ makeSelect {qeSelectList = [(NumLit "1",Nothing)]}
diff --git a/tools/Language/SQL/SimpleSQL/SQL2011AccessControl.lhs b/tools/Language/SQL/SimpleSQL/SQL2011AccessControl.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/SQL2011AccessControl.lhs
+++ /dev/null
@@ -1,315 +0,0 @@
-
-Section 12 in Foundation
-
-grant, etc
-
-
-> module Language.SQL.SimpleSQL.SQL2011AccessControl (sql2011AccessControlTests) where
-
-> import Language.SQL.SimpleSQL.TestTypes
-> import Language.SQL.SimpleSQL.Syntax
-
-> sql2011AccessControlTests :: TestItem
-> sql2011AccessControlTests = Group "sql 2011 access control tests" [
-
-12 Access control
-
-12.1 <grant statement>
-
-<grant statement> ::=
-    <grant privilege statement>
-  | <grant role statement>
-
-12.2 <grant privilege statement>
-
-<grant privilege statement> ::=
-  GRANT <privileges> TO <grantee> [ { <comma> <grantee> }... ]
-      [ WITH HIERARCHY OPTION ]
-      [ WITH GRANT OPTION ]
-      [ GRANTED BY <grantor> ]
-
-12.3 <privileges>
-<privileges> ::=
-  <object privileges> ON <object name>
-
-<object name> ::=
-      [ TABLE ] <table name>
-  |   DOMAIN <domain name>
-  |   COLLATION <collation name>
-  |   CHARACTER SET <character set name>
-  |   TRANSLATION <transliteration name>
-  |   TYPE <schema-resolved user-defined type name>
-  |   SEQUENCE <sequence generator name>
-  |   <specific routine designator>
-
-<object privileges> ::=
-    ALL PRIVILEGES
-  | <action> [ { <comma> <action> }... ]
-
-<action> ::=
-    SELECT
-  | SELECT <left paren> <privilege column list> <right paren>
-  | SELECT <left paren> <privilege method list> <right paren>
-  | DELETE
-  | INSERT [ <left paren> <privilege column list> <right paren> ]
-  | UPDATE [ <left paren> <privilege column list> <right paren> ]
-  | REFERENCES [ <left paren> <privilege column list> <right paren> ]
-  | USAGE
-  | TRIGGER
-  | UNDER
-  | EXECUTE
-
-<privilege method list> ::=
-  <specific routine designator> [ { <comma> <specific routine designator> }... ]
-
-<privilege column list> ::=
-  <column name list>
-
-<grantee> ::=
-    PUBLIC
-  | <authorization identifier>
-
-<grantor> ::=
-    CURRENT_USER
-  | CURRENT_ROLE
-
->      (TestStatement ansi2011
->       "grant all privileges on tbl1 to role1"
->      $ GrantPrivilege [PrivAll]
->        (PrivTable [Name Nothing "tbl1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
-
->     ,(TestStatement ansi2011
->       "grant all privileges on tbl1 to role1,role2"
->      $ GrantPrivilege [PrivAll]
->        (PrivTable [Name Nothing "tbl1"])
->        [Name Nothing "role1",Name Nothing "role2"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant all privileges on tbl1 to role1 with grant option"
->      $ GrantPrivilege [PrivAll]
->        (PrivTable [Name Nothing "tbl1"])
->        [Name Nothing "role1"] WithGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant all privileges on table tbl1 to role1"
->      $ GrantPrivilege [PrivAll]
->        (PrivTable [Name Nothing "tbl1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant all privileges on domain mydom to role1"
->      $ GrantPrivilege [PrivAll]
->        (PrivDomain [Name Nothing "mydom"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant all privileges on type t1 to role1"
->      $ GrantPrivilege [PrivAll]
->        (PrivType [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant all privileges on sequence s1 to role1"
->      $ GrantPrivilege [PrivAll]
->        (PrivSequence [Name Nothing "s1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
-
->     ,(TestStatement ansi2011
->       "grant select on table t1 to role1"
->      $ GrantPrivilege [PrivSelect []]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant select(a,b) on table t1 to role1"
->      $ GrantPrivilege [PrivSelect [Name Nothing "a", Name Nothing "b"]]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant delete on table t1 to role1"
->      $ GrantPrivilege [PrivDelete]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant insert on table t1 to role1"
->      $ GrantPrivilege [PrivInsert []]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant insert(a,b) on table t1 to role1"
->      $ GrantPrivilege [PrivInsert [Name Nothing "a", Name Nothing "b"]]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant update on table t1 to role1"
->      $ GrantPrivilege [PrivUpdate []]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant update(a,b) on table t1 to role1"
->      $ GrantPrivilege [PrivUpdate [Name Nothing "a", Name Nothing "b"]]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant references on table t1 to role1"
->      $ GrantPrivilege [PrivReferences []]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant references(a,b) on table t1 to role1"
->      $ GrantPrivilege [PrivReferences [Name Nothing "a", Name Nothing "b"]]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant usage on table t1 to role1"
->      $ GrantPrivilege [PrivUsage]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant trigger on table t1 to role1"
->      $ GrantPrivilege [PrivTrigger]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
-
->     ,(TestStatement ansi2011
->       "grant execute on specific function f to role1"
->      $ GrantPrivilege [PrivExecute]
->        (PrivFunction [Name Nothing "f"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
->     ,(TestStatement ansi2011
->       "grant select,delete on table t1 to role1"
->      $ GrantPrivilege [PrivSelect [], PrivDelete]
->        (PrivTable [Name Nothing "t1"])
->        [Name Nothing "role1"] WithoutGrantOption)
-
-skipping for now:
-
-what is 'under' action?
-
-collation, character set, translation, member thing, methods
-
-for review
-
-some pretty big things missing in the standard:
-
-schema, database
-
-functions, etc., by argument types since they can be overloaded
-
-
-
-12.4 <role definition>
-
-<role definition> ::=
-  CREATE ROLE <role name> [ WITH ADMIN <grantor> ]
-
->     ,(TestStatement ansi2011
->       "create role rolee"
->      $ CreateRole (Name Nothing "rolee"))
-
-
-12.5 <grant role statement>
-
-<grant role statement> ::=
-  GRANT <role granted> [ { <comma> <role granted> }... ]
-      TO <grantee> [ { <comma> <grantee> }... ]
-      [ WITH ADMIN OPTION ]
-      [ GRANTED BY <grantor> ]
-
-<role granted> ::=
-  <role name>
-
->     ,(TestStatement ansi2011
->       "grant role1 to public"
->      $ GrantRole [Name Nothing "role1"] [Name Nothing "public"] WithoutAdminOption)
-
->     ,(TestStatement ansi2011
->       "grant role1,role2 to role3,role4"
->      $ GrantRole [Name Nothing "role1",Name Nothing "role2"]
->                  [Name Nothing "role3", Name Nothing "role4"] WithoutAdminOption)
-
->     ,(TestStatement ansi2011
->       "grant role1 to role3 with admin option"
->      $ GrantRole [Name Nothing "role1"] [Name Nothing "role3"] WithAdminOption)
-
-
-12.6 <drop role statement>
-
-<drop role statement> ::=
-  DROP ROLE <role name>
-
->     ,(TestStatement ansi2011
->       "drop role rolee"
->      $ DropRole (Name Nothing "rolee"))
-
-
-12.7 <revoke statement>
-
-<revoke statement> ::=
-    <revoke privilege statement>
-  | <revoke role statement>
-
-<revoke privilege statement> ::=
-  REVOKE [ <revoke option extension> ] <privileges>
-      FROM <grantee> [ { <comma> <grantee> }... ]
-      [ GRANTED BY <grantor> ]
-      <drop behavior>
-
-<revoke option extension> ::=
-    GRANT OPTION FOR
-  | HIERARCHY OPTION FOR
-
-
->     ,(TestStatement ansi2011
->       "revoke select on t1 from role1"
->      $ RevokePrivilege NoGrantOptionFor [PrivSelect []]
->               (PrivTable [Name Nothing "t1"])
->               [Name Nothing "role1"] DefaultDropBehaviour)
-
->     ,(TestStatement ansi2011
->       "revoke grant option for select on t1 from role1,role2 cascade"
->      $ RevokePrivilege GrantOptionFor [PrivSelect []]
->                        (PrivTable [Name Nothing "t1"])
->               [Name Nothing "role1",Name Nothing "role2"] Cascade)
-
-
-<revoke role statement> ::=
-  REVOKE [ ADMIN OPTION FOR ] <role revoked> [ { <comma> <role revoked> }... ]
-      FROM <grantee> [ { <comma> <grantee> }... ]
-      [ GRANTED BY <grantor> ]
-      <drop behavior>
-
-<role revoked> ::=
-  <role name>
-
->     ,(TestStatement ansi2011
->       "revoke role1 from role2"
->      $ RevokeRole NoAdminOptionFor [Name Nothing "role1"]
->                   [Name Nothing "role2"] DefaultDropBehaviour)
-
->     ,(TestStatement ansi2011
->       "revoke role1,role2 from role3,role4"
->      $ RevokeRole NoAdminOptionFor [Name Nothing "role1",Name Nothing "role2"]
->                   [Name Nothing "role3",Name Nothing "role4"] DefaultDropBehaviour)
-
-
->     ,(TestStatement ansi2011
->       "revoke admin option for role1 from role2 cascade"
->      $ RevokeRole AdminOptionFor [Name Nothing "role1"] [Name Nothing "role2"] Cascade)
-
-
->    ]
diff --git a/tools/Language/SQL/SimpleSQL/SQL2011Bits.lhs b/tools/Language/SQL/SimpleSQL/SQL2011Bits.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/SQL2011Bits.lhs
+++ /dev/null
@@ -1,219 +0,0 @@
-
-Sections 17 and 19 in Foundation
-
-This module covers the tests for transaction management (begin,
-commit, savepoint, etc.), and session management (set).
-
-
-> module Language.SQL.SimpleSQL.SQL2011Bits (sql2011BitsTests) where
-
-> import Language.SQL.SimpleSQL.TestTypes
-> import Language.SQL.SimpleSQL.Syntax
-
-> sql2011BitsTests :: TestItem
-> sql2011BitsTests = Group "sql 2011 bits tests" [
-
-17 Transaction management
-
-17.1 <start transaction statement>
-
-<start transaction statement> ::=
-  START TRANSACTION [ <transaction characteristics> ]
-
-BEGIN is not in the standard!
-
->      (TestStatement ansi2011
->       "start transaction"
->      $ StartTransaction)
-
-17.2 <set transaction statement>
-
-<set transaction statement> ::=
-  SET [ LOCAL ] TRANSACTION <transaction characteristics>
-
-17.3 <transaction characteristics>
-
-<transaction characteristics> ::=
-      [ <transaction mode> [ { <comma> <transaction mode> }... ] ]
-
-<transaction mode> ::=
-    <isolation level>
-  | <transaction access mode>
-  | <diagnostics size>
-
-<transaction access mode> ::=
-    READ ONLY
-  | READ WRITE
-
-<isolation level> ::=
-  ISOLATION LEVEL <level of isolation>
-
-<level of isolation> ::=
-    READ UNCOMMITTED
-  | READ COMMITTED
-  | REPEATABLE READ
-  | SERIALIZABLE
-
-<diagnostics size> ::=
-  DIAGNOSTICS SIZE <number of conditions>
-
-<number of conditions> ::=
-  <simple value specification>
-
-17.4 <set constraints mode statement>
-
-<set constraints mode statement> ::=
-  SET CONSTRAINTS <constraint name list> { DEFERRED | IMMEDIATE }
-
-<constraint name list> ::=
-    ALL
-  | <constraint name> [ { <comma> <constraint name> }... ]
-
-17.5 <savepoint statement>
-
-<savepoint statement> ::=
-  SAVEPOINT <savepoint specifier>
-
-<savepoint specifier> ::=
-  <savepoint name>
-
->     ,(TestStatement ansi2011
->       "savepoint difficult_bit"
->      $ Savepoint $ Name Nothing "difficult_bit")
-
-
-17.6 <release savepoint statement>
-
-<release savepoint statement> ::=
-  RELEASE SAVEPOINT <savepoint specifier>
-
->     ,(TestStatement ansi2011
->       "release savepoint difficult_bit"
->      $ ReleaseSavepoint $ Name Nothing "difficult_bit")
-
-
-17.7 <commit statement>
-
-<commit statement> ::=
-  COMMIT [ WORK ] [ AND [ NO ] CHAIN ]
-
->     ,(TestStatement ansi2011
->       "commit"
->      $ Commit)
-
->     ,(TestStatement ansi2011
->       "commit work"
->      $ Commit)
-
-
-17.8 <rollback statement>
-
-<rollback statement> ::=
-  ROLLBACK [ WORK ] [ AND [ NO ] CHAIN ] [ <savepoint clause> ]
-
-<savepoint clause> ::=
-  TO SAVEPOINT <savepoint specifier>
-
->     ,(TestStatement ansi2011
->       "rollback"
->      $ Rollback Nothing)
-
->     ,(TestStatement ansi2011
->       "rollback work"
->      $ Rollback Nothing)
-
->     ,(TestStatement ansi2011
->       "rollback to savepoint difficult_bit"
->      $ Rollback $ Just $ Name Nothing "difficult_bit")
-
-
-19 Session management
-
-19.1 <set session characteristics statement>
-
-<set session characteristics statement> ::=
-  SET SESSION CHARACTERISTICS AS <session characteristic list>
-
-<session characteristic list> ::=
-  <session characteristic> [ { <comma> <session characteristic> }... ]
-
-<session characteristic> ::=
-  <session transaction characteristics>
-
-<session transaction characteristics> ::=
-  TRANSACTION <transaction mode> [ { <comma> <transaction mode> }... ]
-
-19.2 <set session user identifier statement>
-
-<set session user identifier statement> ::=
-  SET SESSION AUTHORIZATION <value specification>
-
-19.3 <set role statement>
-
-<set role statement> ::=
-  SET ROLE <role specification>
-
-<role specification> ::=
-    <value specification>
-  | NONE
-
-19.4 <set local time zone statement>
-
-<set local time zone statement> ::=
-  SET TIME ZONE <set time zone value>
-
-<set time zone value> ::=
-    <interval value expression>
-  | LOCAL
-
-19.5 <set catalog statement>
-
-<set catalog statement> ::=
-  SET <catalog name characteristic>
-
-<catalog name characteristic> ::=
-  CATALOG <value specification>
-
-19.6 <set schema statement>
-
-<set schema statement> ::=
-  SET <schema name characteristic>
-
-<schema name characteristic> ::=
-  SCHEMA <value specification>
-
-19.7 <set names statement>
-
-<set names statement> ::=
-  SET <character set name characteristic>
-
-<character set name characteristic> ::=
-  NAMES <value specification>
-
-19.8 <set path statement>
-
-<set path statement> ::=
-  SET <SQL-path characteristic>
-
-<SQL-path characteristic> ::=
-  PATH <value specification>
-
-19.9 <set transform group statement>
-
-<set transform group statement> ::=
-  SET <transform group characteristic>
-
-<transform group characteristic> ::=
-    DEFAULT TRANSFORM GROUP <value specification>
-  | TRANSFORM GROUP FOR TYPE <path-resolved user-defined type name> <value specification>
-
-19.10 <set session collation statement>
-
-<set session collation statement> ::=
-    SET COLLATION <collation specification> [ FOR <character set specification list> ]
-  | SET NO COLLATION [ FOR <character set specification list> ]
-
-<collation specification> ::=
-  <value specification>
-
->    ]
diff --git a/tools/Language/SQL/SimpleSQL/SQL2011DataManipulation.lhs b/tools/Language/SQL/SimpleSQL/SQL2011DataManipulation.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/SQL2011DataManipulation.lhs
+++ /dev/null
@@ -1,544 +0,0 @@
-
-Section 14 in Foundation
-
-
-> module Language.SQL.SimpleSQL.SQL2011DataManipulation (sql2011DataManipulationTests) where
-
-> import Language.SQL.SimpleSQL.TestTypes
-> import Language.SQL.SimpleSQL.Syntax
-
-> sql2011DataManipulationTests :: TestItem
-> sql2011DataManipulationTests = Group "sql 2011 data manipulation tests"
->     [
-
-
-14 Data manipulation
-
-
-14.1 <declare cursor>
-
-<declare cursor> ::=
-  DECLARE <cursor name> <cursor properties>
-      FOR <cursor specification>
-
-14.2 <cursor properties>
-
-<cursor properties> ::=
-  [ <cursor sensitivity> ] [ <cursor scrollability> ] CURSOR
-      [ <cursor holdability> ]
-      [ <cursor returnability> ]
-
-<cursor sensitivity> ::=
-    SENSITIVE
-  | INSENSITIVE
-  | ASENSITIVE
-
-<cursor scrollability> ::=
-    SCROLL
-  | NO SCROLL
-
-<cursor holdability> ::=
-    WITH HOLD
-  | WITHOUT HOLD
-
-<cursor returnability> ::=
-    WITH RETURN
-  | WITHOUT RETURN
-
-14.3 <cursor specification>
-
-<cursor specification> ::=
-  <query expression> [ <updatability clause> ]
-
-<updatability clause> ::=
-  FOR { READ ONLY | UPDATE [ OF <column name list> ] }
-
-14.4 <open statement>
-
-<open statement> ::=
-  OPEN <cursor name>
-
-14.5 <fetch statement>
-
-<fetch statement> ::=
-     FETCH [ [ <fetch orientation> ] FROM ] <cursor name> INTO <fetch target list>
-
-<fetch orientation> ::=
-    NEXT
-  | PRIOR
-  | FIRST
-  | LAST
-  | { ABSOLUTE | RELATIVE } <simple value specification>
-
-<fetch target list> ::=
-     <target specification> [ { <comma> <target specification> }... ]
-
-
-14.6 <close statement>
-
-<close statement> ::=
-  CLOSE <cursor name>
-
-14.7 <select statement: single row>
-
-<select statement: single row> ::=
-  SELECT [ <set quantifier> ] <select list>
-      INTO <select target list>
-      <table expression>
-
-<select target list> ::=
-  <target specification> [ { <comma> <target specification> }... ]
-
-14.8 <delete statement: positioned>
-
-<delete statement: positioned> ::=
-     DELETE FROM <target table> [ [ AS ] <correlation name> ]
-         WHERE CURRENT OF <cursor name>
-
-<target table> ::=
-    <table name>
-  | ONLY <left paren> <table name> <right paren>
-
-14.9 <delete statement: searched>
-
-<delete statement: searched> ::=
-  DELETE FROM <target table>
-      [ FOR PORTION OF <application time period name>
-        FROM <point in time 1> TO <point in time 2> ]
-      [ [ AS ] <correlation name> ]
-      [ WHERE <search condition> ]
-
->      (TestStatement ansi2011 "delete from t"
->      $ Delete [Name Nothing "t"] Nothing Nothing)
-
->     ,(TestStatement ansi2011 "delete from t as u"
->      $ Delete [Name Nothing "t"] (Just (Name Nothing "u")) Nothing)
-
->     ,(TestStatement ansi2011 "delete from t where x = 5"
->      $ Delete [Name Nothing "t"] Nothing
->        (Just $ BinOp (Iden [Name Nothing "x"]) [Name Nothing "="] (NumLit "5")))
-
-
->     ,(TestStatement ansi2011 "delete from t as u where u.x = 5"
->      $ Delete [Name Nothing "t"] (Just (Name Nothing "u"))
->        (Just $ BinOp (Iden [Name Nothing "u", Name Nothing "x"]) [Name Nothing "="] (NumLit "5")))
-
-14.10 <truncate table statement>
-
-<truncate table statement> ::=
-  TRUNCATE TABLE <target table> [ <identity column restart option> ]
-
-<identity column restart option> ::=
-    CONTINUE IDENTITY
-  | RESTART IDENTITY
-
->     ,(TestStatement ansi2011 "truncate table t"
->      $ Truncate [Name Nothing "t"] DefaultIdentityRestart)
-
->     ,(TestStatement ansi2011 "truncate table t continue identity"
->      $ Truncate [Name Nothing "t"] ContinueIdentity)
-
->     ,(TestStatement ansi2011 "truncate table t restart identity"
->      $ Truncate [Name Nothing "t"] RestartIdentity)
-
-
-14.11 <insert statement>
-
-<insert statement> ::=
-  INSERT INTO <insertion target> <insert columns and source>
-
-<insertion target> ::=
-  <table name>
-
-<insert columns and source> ::=
-    <from subquery>
-  | <from constructor>
-  | <from default>
-
-<from subquery> ::=
-  [ <left paren> <insert column list> <right paren> ]
-      [ <override clause> ]
-      <query expression>
-
-<from constructor> ::=
-  [ <left paren> <insert column list> <right paren> ]
-      [ <override clause> ]
-      <contextually typed table value constructor>
-
-<override clause> ::=
-    OVERRIDING USER VALUE
-  | OVERRIDING SYSTEM VALUE
-
-<from default> ::=
-  DEFAULT VALUES
-
-<insert column list> ::=
-  <column name list>
-
->     ,(TestStatement ansi2011 "insert into t select * from u"
->      $ Insert [Name Nothing "t"] Nothing
->        $ InsertQuery makeSelect
->          {qeSelectList = [(Star, Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "u"]]})
-
->     ,(TestStatement ansi2011 "insert into t(a,b,c) select * from u"
->      $ Insert [Name Nothing "t"] (Just [Name Nothing "a", Name Nothing "b", Name Nothing "c"])
->        $ InsertQuery makeSelect
->          {qeSelectList = [(Star, Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "u"]]})
-
->     ,(TestStatement ansi2011 "insert into t default values"
->      $ Insert [Name Nothing "t"] Nothing DefaultInsertValues)
-
->     ,(TestStatement ansi2011 "insert into t values(1,2)"
->      $ Insert [Name Nothing "t"] Nothing
->        $ InsertQuery $ Values [[NumLit "1", NumLit "2"]])
-
->     ,(TestStatement ansi2011 "insert into t values (1,2),(3,4)"
->      $ Insert [Name Nothing "t"] Nothing
->        $ InsertQuery $ Values [[NumLit "1", NumLit "2"]
->                               ,[NumLit "3", NumLit "4"]])
-
->     ,(TestStatement ansi2011
->       "insert into t values (default,null,array[],multiset[])"
->      $ Insert [Name Nothing "t"] Nothing
->        $ InsertQuery $ Values [[Iden [Name Nothing "default"]
->                                ,Iden [Name Nothing "null"]
->                                ,Array (Iden [Name Nothing "array"]) []
->                                ,MultisetCtor []]])
-
-
-14.12 <merge statement>
-
-<merge statement> ::=
-  MERGE INTO <target table> [ [ AS ] <merge correlation name> ]
-      USING <table reference>
-      ON <search condition> <merge operation specification>
-
-merge into t
-  using t
-  on a = b
-  merge operation specification
-
-merge into t as u
-using (table factor | joined expression)
-
- MERGE INTO tablename USING table_reference ON (condition)
-   WHEN MATCHED THEN
-   UPDATE SET column1 = value1 [, column2 = value2 ...]
-   WHEN NOT MATCHED THEN
-   INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...
-
-merge into t23
-using t42
-on t42.id = t23.id
-when matched then
-    update
-    set     t23.col1 = t42.col1
-when not matched then
-    insert  (id, col1)
-    values  (t42.id, t42.col1)
-
-
-
-MERGE INTO TableA u
-
-USING (SELECT b.Key1, b.ColB1, c.ColC1
-
-FROM TableB b
-
-INNER JOIN TableC c ON c.KeyC1 = b.KeyB1
-
-) s
-
-ON (u.KeyA1 = s.KeyA1)
-
-WHEN MATCHED THEN
-
-UPDATE SET u.ColA1 = s.ColB1, u.ColA2 = s.ColC1
-
-
-MERGE INTO Department 
-USING NewDept AS ND 
-ON nd.Department_Number = Department.
-Department_Number 
-WHEN MATCHED THEN UPDATE 
-SET budget_amount = nd.Budget_Amount 
-WHEN NOT MATCHED THEN INSERT 
-VALUES 
-(nd.Department_Number, nd.Department_
-Name, nd.Budget_Amount, 
- nd.Manager_Employee_Number);
-
-
-MERGE INTO Orders2 
-USING Orders3 
-ON ORDERS3.Order_Number = Orders2.
-Order_Number 
-WHEN NOT MATCHED THEN INSERT 
-Orders3.order_number, Orders3.
-invoice_number, 
- Orders3.customer_number, Orders3.
-initial_order_date, 
- Orders3.invoice_date, Orders3.
-invoice_amount);
-
-MERGE INTO Orders2 
-USING Orders3 
-ON ORDERS3.Order_Number = Orders2.
-Order_Number AND 1=0 
-WHEN NOT MATCHED THEN INSERT 
-(Orders3.order_number, Orders3.invoice_number, 
- Orders3.customer_number, Orders3.
-initial_order_date, 
- Orders3.invoice_date, Orders3.
-invoice_amount);
-
-MERGE INTO Department 
-USING NewDept AS ND 
-ON nd.Department_Number = Department.
-Department_Number 
-WHEN MATCHED THEN UPDATE 
-SET budget_amount = nd.Budget_Amount 
-LOGGING ALL ERRORS WITH NO LIMIT;
-
-
-MERGE INTO Department 
-USING 
- (SELECT Department_Number,
-department_name, 
-        Budget_Amount, 
-Manager_Employee_Number 
-    FROM NewDept 
-    WHERE Department_Number IN 
-(SELECT Department_Number 
-        FROM Employee)) AS m
-ON m.Department_Number = Department.
-Department_Number 
-WHEN MATCHED THEN UPDATE 
-SET budget_amount = m.Budget_Amount 
-WHEN NOT MATCHED THEN INSERT 
-(m.Department_Number, m.Department_
-Name, m.Budget_Amount, 
-m.Manager_Employee_Number) 
-LOGGING ALL ERRORS WITH NO LIMIT;
-
- 
-MERGE INTO Customers AS c
-USING      Moved     AS m
-      ON   m.SSN      = c.SSN
-WHEN MATCHED
-THEN UPDATE
-SET        Street     = m.Street,
-           HouseNo    = m.HouseNo,
-           City       = m.City;
-
-MERGE INTO CentralOfficeAccounts AS C    -- Target
-USING BranchOfficeAccounts AS B          -- Source
-   ON C.account_nbr = B.account_nbr
-WHEN MATCHED THEN                        -- On match update
-     UPDATE SET C.company_name = B.company_name,
-                C.primary_contact = B.primary_contact,
-                C.contact_phone = B.contact_phone
-WHEN NOT MATCHED THEN                    -- Add missing
-     INSERT (account_nbr, company_name, primary_contact, contact_phone)
-     VALUES (B.account_nbr, B.company_name, B.primary_contact, B.contact_phone);
- 
-SELECT account_nbr, company_name, primary_contact, contact_phone 
-FROM CentralOfficeAccounts;
-
-
-
-MERGE INTO CentralOfficeAccounts AS C   -- Target
-USING BranchOfficeAccounts AS B         -- Source
-   ON C.account_nbr = B.account_nbr
-WHEN MATCHED                            -- On match update
- AND (C.company_name <> B.company_name  -- Additional search conditions
-   OR C.primary_contact <> B.primary_contact
-   OR C.contact_phone <> B.contact_phone) THEN                        
-     UPDATE SET C.company_name = B.company_name,
-                C.primary_contact = B.primary_contact,
-                C.contact_phone = B.contact_phone
-WHEN NOT MATCHED THEN                   -- Add missing
-     INSERT (account_nbr, company_name, primary_contact, contact_phone)
-     VALUES (B.account_nbr, B.company_name, B.primary_contact, B.contact_phone);
-
-
-
-MERGE INTO CentralOfficeAccounts AS C   -- Target
-USING BranchOfficeAccounts AS B         -- Source
-   ON C.account_nbr = B.account_nbr
-WHEN MATCHED                            -- On match update
- AND (C.company_name <> B.company_name  -- Additional search conditions
-   OR C.primary_contact <> B.primary_contact
-   OR C.contact_phone <> B.contact_phone) THEN                        
-     UPDATE SET C.company_name = B.company_name,
-                C.primary_contact = B.primary_contact,
-                C.contact_phone = B.contact_phone
-WHEN NOT MATCHED THEN                   -- Add missing
-     INSERT (account_nbr, company_name, primary_contact, contact_phone)
-     VALUES (B.account_nbr, B.company_name, B.primary_contact, B.contact_phone)
-WHEN SOURCE NOT MATCHED THEN            -- Delete missing from source
-     DELETE;
- 
-SELECT account_nbr, company_name, primary_contact, contact_phone 
-FROM CentralOfficeAccounts; 
-
-
-
-
-<merge correlation name> ::=
-  <correlation name>
-
-<merge operation specification> ::=
-  <merge when clause>...
-
-<merge when clause> ::=
-    <merge when matched clause>
-  | <merge when not matched clause>
-
-<merge when matched clause> ::=
-  WHEN MATCHED [ AND <search condition> ]
-      THEN <merge update or delete specification>
-
-<merge update or delete specification> ::=
-    <merge update specification>
-  | <merge delete specification>
-
-<merge when not matched clause> ::=
-  WHEN NOT MATCHED [ AND <search condition> ]
-      THEN <merge insert specification>
-
-<merge update specification> ::=
-  UPDATE SET <set clause list>
-
-<merge delete specification> ::=
-  DELETE
-
-<merge insert specification> ::=
-  INSERT [ <left paren> <insert column list> <right paren> ]
-      [ <override clause> ]
-      VALUES <merge insert value list>
-
-<merge insert value list> ::=
-  <left paren>
-      <merge insert value element> [ { <comma> <merge insert value element> }... ]
-      <right paren>
-
-<merge insert value element> ::=
-    <value expression>
-  | <contextually typed value specification>
-
-14.13 <update statement: positioned>
-
-<updatestatement: positioned> ::=
-     UPDATE <target table> [ [ AS ] <correlation name> ]
-         SET <set clause list>
-         WHERE CURRENT OF <cursor name>
-
-14.14 <update statement: searched>
-
-<update statement: searched> ::=
-  UPDATE <target table>
-      [ FOR PORTION OF <application time period name>
-        FROM <point in time 1> TO <point in time 2> ]
-      [ [ AS ] <correlation name> ]
-      SET <set clause list>
-      [ WHERE <search condition> ]
-
-
->     ,(TestStatement ansi2011 "update t set a=b"
->      $ Update [Name Nothing "t"] Nothing
->        [Set [Name Nothing "a"] (Iden [Name Nothing "b"])] Nothing)
-
->     ,(TestStatement ansi2011 "update t set a=b, c=5"
->      $ Update [Name Nothing "t"] Nothing
->        [Set [Name Nothing "a"] (Iden [Name Nothing "b"])
->        ,Set [Name Nothing "c"] (NumLit "5")] Nothing)
-
-
->     ,(TestStatement ansi2011 "update t set a=b where a>5"
->      $ Update [Name Nothing "t"] Nothing
->        [Set [Name Nothing "a"] (Iden [Name Nothing "b"])]
->        $ Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5"))
-
-
->     ,(TestStatement ansi2011 "update t as u set a=b where u.a>5"
->      $ Update [Name Nothing "t"] (Just $ Name Nothing "u")
->        [Set [Name Nothing "a"] (Iden [Name Nothing "b"])]
->        $ Just $ BinOp (Iden [Name Nothing "u",Name Nothing "a"])
->                       [Name Nothing ">"] (NumLit "5"))
-
->     ,(TestStatement ansi2011 "update t set (a,b)=(3,5)"
->      $ Update [Name Nothing "t"] Nothing
->        [SetMultiple [[Name Nothing "a"],[Name Nothing "b"]]
->                     [NumLit "3", NumLit "5"]] Nothing)
-
-
-
-14.15 <set clause list>
-
-<set clause list> ::=
-  <set clause> [ { <comma> <set clause> }... ]
-
-<set clause> ::=
-    <multiple column assignment>
-  | <set target> <equals operator> <update source>
-
-<set target> ::=
-    <update target>
-  | <mutated set clause>
-
-<multiple column assignment> ::=
-  <set target list> <equals operator> <assigned row>
-
-<set target list> ::=
-  <left paren> <set target> [ { <comma> <set target> }... ] <right paren>
-
-<assigned row> ::=
-  <contextually typed row value expression>
-
-<update target> ::=
-    <object column>
-  | <object column>
-      <left bracket or trigraph> <simple value specification> <right bracket or trigraph>
-
-<object column> ::=
-  <column name>
-
-<mutated set clause> ::=
-  <mutated target> <period> <method name>
-
-<mutated target> ::=
-    <object column>
-  | <mutated set clause>
-
-<update source> ::=
-    <value expression>
-  | <contextually typed value specification>
-
-14.16 <temporary table declaration>
-
-<temporary table declaration> ::=
-  DECLARE LOCAL TEMPORARY TABLE <table name> <table element list>
-      [ ON COMMIT <table commit action> ROWS ]
-
-declare local temporary table t (a int) [on commit {preserve | delete} rows]
-
-14.17 <free locator statement>
-
-<free locator statement> ::=
-  FREE LOCATOR <locator reference> [ { <comma> <locator reference> }... ]
-
-<locator reference> ::=
-    <host parameter name>
-  | <embedded variable name>
-  | <dynamic parameter specification>
-
-14.18 <hold locator statement>
-
-<hold locator statement> ::=
-  HOLD LOCATOR <locator reference> [ { <comma> <locator reference> }... ]
-
-
->    ]
diff --git a/tools/Language/SQL/SimpleSQL/SQL2011Queries.lhs b/tools/Language/SQL/SimpleSQL/SQL2011Queries.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/SQL2011Queries.lhs
+++ /dev/null
@@ -1,4341 +0,0 @@
-
-This file goes through the grammar for SQL 2011 queries (using the
-draft standard).
-
-There are other files which cover some of the other sections from SQL
-2011 (ddl, non-query dml, etc).
-
-Possible sections not in the todo which could
-be covered:
-
-13 modules
-16 control statements
-18 connection management
-20 dynamic
-22 direct
-23 diagnostics
-
-procedural sql
-
-some of the main areas being left for now:
-temporal and versioning stuff
-modules
-ref stuff
-todo: finish this list
-
-
-
-The goal is to create some example tests for each bit of grammar, with
-some areas getting more comprehensive coverage tests, and also to note
-which parts aren't currently supported.
-
-> module Language.SQL.SimpleSQL.SQL2011Queries (sql2011QueryTests) where
-> import Language.SQL.SimpleSQL.TestTypes
-> import Language.SQL.SimpleSQL.Syntax
-
-> sql2011QueryTests :: TestItem
-> sql2011QueryTests = Group "sql 2011 query tests"
->     [literals
->     ,identifiers
->     ,typeNameTests
->     ,fieldDefinition
->     ,valueExpressions
->     ,queryExpressions
->     ,scalarSubquery
->     ,predicates
->     ,intervalQualifier
->     ,collateClause
->     ,aggregateFunction
->     ,sortSpecificationList
->     ]
-
-= 5 Lexical elements
-
-The tests don't make direct use of these definitions.
-
-== 5.1 <SQL terminal character>
-
-Function
-
-Define the terminal symbols of the SQL language and the elements of
-strings.
-
-<SQL terminal character> ::= <SQL language character>
-
-<SQL language character> ::=
-    <simple Latin letter>
-  | <digit>
-  | <SQL special character>
-
-<simple Latin letter> ::=
-    <simple Latin upper case letter>
-  | <simple Latin lower case letter>
-
-<simple Latin upper case letter> ::=
-    A | B | C | D | E | F | G | H | I | J | K | L | M | N | O
-  | P | Q | R | S | T | U | V | W | X | Y | Z
-
-<simple Latin lower case letter> ::=
-    a | b | c | d | e | f | g | h | i | j | k | l | m | n | o
-  | p | q | r | s | t | u | v | w | x | y | z
-
-<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
-
-<SQL special character> ::=
-    <space>
-  | <double quote>
-  | <percent>
-  | <ampersand>
-  | <quote>
-  | <left paren>
-  | <right paren>
-  | <asterisk>
-  | <plus sign>
-  | <comma>
-  | <minus sign>
-  | <period>
-  | <solidus>
-  | <colon>
-  | <semicolon>
-  | <less than operator>
-  | <equals operator>
-  | <greater than operator>
-  | <question mark>
-  | <left bracket>
-  | <right bracket>
-  | <circumflex>
-  | <underscore>
-  | <vertical bar>
-  | <left brace>
-  | <right brace>
-
-<space> ::= !! See the Syntax Rules.
-
-<double quote> ::= "
-
-<percent> ::= %
-
-<ampersand> ::= &
-
-<quote> ::= '
-
-<left paren> ::= (
-
-<right paren> ::= )
-
-<asterisk> ::= *
-
-<plus sign> ::= +
-
-<comma> ::= ,
-
-<minus sign> ::= -
-
-<period> ::= .
-
-<solidus> ::= /
-
-<reverse solidus> ::= \
-
-<colon> ::= :
-
-<semicolon> ::= ;
-
-<less than operator> ::= <
-
-<equals operator> ::= =
-
-<greater than operator> ::= >
-
-<question mark> ::= ?
-
-<left bracket or trigraph> ::= <left bracket> | <left bracket trigraph>
-
-<right bracket or trigraph> ::= <right bracket> | <right bracket trigraph>
-
-<left bracket> ::= [
-
-<left bracket trigraph> ::= ??(
-
-<right bracket> ::= ]
-
-<right bracket trigraph> ::= ??)
-
-<circumflex> ::= ^
-
-<underscore> ::= _
-
-<vertical bar> ::= |
-
-<left brace> ::= {
-
-<right brace> ::= }
-
-== 5.2 <token> and <separator>
-
-Function
-
-Specify lexical units (tokens and separators) that participate in SQL
-language.
-
-<token> ::= <nondelimiter token> | <delimiter token>
-
-<nondelimiter token> ::=
-    <regular identifier>
-  | <key word>
-  | <unsigned numeric literal>
-  | <national character string literal>
-  | <binary string literal>
-  | <large object length token>
-  | <Unicode delimited identifier>
-  | <Unicode character string literal>
-  | <SQL language identifier>
-
-<regular identifier> ::= <identifier body>
-
-<identifier body> ::= <identifier start> [ <identifier part>... ]
-
-<identifier part> ::= <identifier start> | <identifier extend>
-
-<identifier start> ::= !! See the Syntax Rules.
-
-<identifier extend> ::= !! See the Syntax Rules.
-
-<large object length token> ::= <digit>... <multiplier>
-
-<multiplier> ::= K | M | G | T | P
-
-<delimited identifier> ::=
-  <double quote> <delimited identifier body> <double quote>
-
-<delimited identifier body> ::= <delimited identifier part>...
-
-<delimited identifier part> ::=
-    <nondoublequote character>
-  | <doublequote symbol>
-
-<Unicode delimited identifier> ::=
-  U <ampersand> <double quote> <Unicode delimiter body> <double quote>
-      <Unicode escape specifier>
-
-<Unicode escape specifier> ::=
-  [ UESCAPE <quote> <Unicode escape character> <quote> ]
-
-<Unicode delimiter body> ::= <Unicode identifier part>...
-
-<Unicode identifier part> ::=
-    <delimited identifier part>
-  | <Unicode escape value>
-
-<Unicode escape value> ::=
-    <Unicode 4 digit escape value>
-  | <Unicode 6 digit escape value>
-  | <Unicode character escape value>
-
-<Unicode 4 digit escape value> ::=
-  <Unicode escape character> <hexit> <hexit> <hexit> <hexit>
-
-<Unicode 6 digit escape value> ::=
-  <Unicode escape character> <plus sign>
-      <hexit> <hexit> <hexit> <hexit> <hexit> <hexit>
-
-<Unicode character escape value> ::=
-  <Unicode escape character> <Unicode escape character>
-
-<Unicode escape character> ::= !! See the Syntax Rules.
-
-<nondoublequote character> ::= !! See the Syntax Rules.
-
-<doublequote symbol> ::= ""!! two consecutive double quote characters
-
-<delimiter token> ::=
-    <character string literal>
-  | <date string>
-  | <time string>
-  | <timestamp string>
-  | <interval string>
-  | <delimited identifier>
-  | <SQL special character>
-  | <not equals operator>
-  | <greater than or equals operator>
-  | <less than or equals operator>
-  | <concatenation operator>
-  | <right arrow>
-  | <left bracket trigraph>
-  | <right bracket trigraph>
-  | <double colon>
-  | <double period>
-  | <named argument assignment token>
-
-<not equals operator> ::= <>
-
-<greater than or equals operator> ::= >=
-
-<less than or equals operator> ::= <=
-
-<concatenation operator> ::= ||
-
-<right arrow> ::= ->
-
-<double colon> ::= ::
-
-<double period> ::= ..
-
-<named argument assignment token> ::= =>
-
-<separator> ::= { <comment> | <white space> }...
-
-<white space> ::= !! See the Syntax Rules.
-
-<comment> ::= <simple comment> | <bracketed comment>
-
-<simple comment> ::=
-  <simple comment introducer> [ <comment character>... ] <newline>
-
-<simple comment introducer> ::= <minus sign> <minus sign>
-
-<bracketed comment> ::=
-  <bracketed comment introducer>
-      <bracketed comment contents>
-      <bracketed comment terminator>
-
-<bracketed comment introducer> ::= /*
-
-<bracketed comment terminator> ::= */
-
-<bracketed comment contents> ::=
-  [ { <comment character> | <separator> }... ]!! See the Syntax Rules.
-
-<comment character> ::= <nonquote character> | <quote>
-
-<newline> ::= !! See the Syntax Rules.
-
-<key word> ::= <reserved word> | <non-reserved word>
-
-<non-reserved word> ::=
-    A | ABSOLUTE | ACTION | ADA | ADD | ADMIN | AFTER | ALWAYS | ASC
-  | ASSERTION | ASSIGNMENT | ATTRIBUTE | ATTRIBUTES
-
-  | BEFORE | BERNOULLI | BREADTH
-
-  | C | CASCADE | CATALOG | CATALOG_NAME | CHAIN | CHARACTER_SET_CATALOG
-  | CHARACTER_SET_NAME | CHARACTER_SET_SCHEMA | CHARACTERISTICS | CHARACTERS
-  | CLASS_ORIGIN | COBOL | COLLATION | COLLATION_CATALOG | COLLATION_NAME | COLLATION_SCHEMA
-  | COLUMN_NAME | COMMAND_FUNCTION | COMMAND_FUNCTION_CODE | COMMITTED
-  | CONDITION_NUMBER | CONNECTION | CONNECTION_NAME | CONSTRAINT_CATALOG | CONSTRAINT_NAME
-  | CONSTRAINT_SCHEMA | CONSTRAINTS | CONSTRUCTOR | CONTINUE | CURSOR_NAME
-
-  | DATA | DATETIME_INTERVAL_CODE | DATETIME_INTERVAL_PRECISION | DEFAULTS | DEFERRABLE
-  | DEFERRED | DEFINED | DEFINER | DEGREE | DEPTH | DERIVED | DESC | DESCRIPTOR
-  | DIAGNOSTICS | DISPATCH | DOMAIN | DYNAMIC_FUNCTION | DYNAMIC_FUNCTION_CODE
-
-  | ENFORCED | EXCLUDE | EXCLUDING | EXPRESSION
-
-  | FINAL | FIRST | FLAG | FOLLOWING | FORTRAN | FOUND
-
-  | G | GENERAL | GENERATED | GO | GOTO | GRANTED
-
-  | HIERARCHY
-
-  | IGNORE | IMMEDIATE | IMMEDIATELY | IMPLEMENTATION | INCLUDING | INCREMENT | INITIALLY
-  | INPUT | INSTANCE | INSTANTIABLE | INSTEAD | INVOKER | ISOLATION
-
-  | K | KEY | KEY_MEMBER | KEY_TYPE
-
-  | LAST | LENGTH | LEVEL | LOCATOR
-
-  | M | MAP | MATCHED | MAXVALUE | MESSAGE_LENGTH | MESSAGE_OCTET_LENGTH
-  | MESSAGE_TEXT | MINVALUE | MORE | MUMPS
-
-  | NAME | NAMES | NESTING | NEXT | NFC | NFD | NFKC | NFKD
-  | NORMALIZED | NULLABLE | NULLS | NUMBER
-
-  | OBJECT | OCTETS | OPTION | OPTIONS | ORDERING | ORDINALITY | OTHERS
-  | OUTPUT | OVERRIDING
-
-  | P | PAD | PARAMETER_MODE | PARAMETER_NAME | PARAMETER_ORDINAL_POSITION
-  | PARAMETER_SPECIFIC_CATALOG | PARAMETER_SPECIFIC_NAME | PARAMETER_SPECIFIC_SCHEMA
-  | PARTIAL | PASCAL | PATH | PLACING | PLI | PRECEDING | PRESERVE | PRIOR
-  | PRIVILEGES | PUBLIC
-
-  | READ | RELATIVE | REPEATABLE | RESPECT | RESTART | RESTRICT | RETURNED_CARDINALITY
-  | RETURNED_LENGTH | RETURNED_OCTET_LENGTH | RETURNED_SQLSTATE | ROLE
-  | ROUTINE | ROUTINE_CATALOG | ROUTINE_NAME | ROUTINE_SCHEMA | ROW_COUNT
-
-  | SCALE | SCHEMA | SCHEMA_NAME | SCOPE_CATALOG | SCOPE_NAME | SCOPE_SCHEMA
-  | SECTION | SECURITY | SELF | SEQUENCE | SERIALIZABLE | SERVER_NAME | SESSION
-  | SETS | SIMPLE | SIZE | SOURCE | SPACE | SPECIFIC_NAME | STATE | STATEMENT
-  | STRUCTURE | STYLE | SUBCLASS_ORIGIN
-
-  | T | TABLE_NAME | TEMPORARY | TIES | TOP_LEVEL_COUNT | TRANSACTION
-  | TRANSACTION_ACTIVE | TRANSACTIONS_COMMITTED | TRANSACTIONS_ROLLED_BACK
-  | TRANSFORM | TRANSFORMS | TRIGGER_CATALOG | TRIGGER_NAME | TRIGGER_SCHEMA | TYPE
-
-  | UNBOUNDED | UNCOMMITTED | UNDER | UNNAMED | USAGE | USER_DEFINED_TYPE_CATALOG
-  | USER_DEFINED_TYPE_CODE | USER_DEFINED_TYPE_NAME | USER_DEFINED_TYPE_SCHEMA
-
-  | VIEW
-
-  | WORK | WRITE
-
-  | ZONE
-
-<reserved word> ::=
-    ABS | ALL | ALLOCATE | ALTER | AND | ANY | ARE | ARRAY | ARRAY_AGG
-  | ARRAY_MAX_CARDINALITY | AS | ASENSITIVE | ASYMMETRIC | AT | ATOMIC | AUTHORIZATION
-  | AVG
-
-  | BEGIN | BEGIN_FRAME | BEGIN_PARTITION | BETWEEN | BIGINT | BINARY
-  | BLOB | BOOLEAN | BOTH | BY
-
-  | CALL | CALLED | CARDINALITY | CASCADED | CASE | CAST | CEIL | CEILING
-  | CHAR | CHAR_LENGTH | CHARACTER | CHARACTER_LENGTH | CHECK | CLOB | CLOSE
-  | COALESCE | COLLATE | COLLECT | COLUMN | COMMIT | CONDITION | CONNECT
-  | CONSTRAINT | CONTAINS | CONVERT | CORR | CORRESPONDING | COUNT | COVAR_POP
-  | COVAR_SAMP | CREATE | CROSS | CUBE | CUME_DIST | CURRENT | CURRENT_CATALOG
-  | CURRENT_DATE | CURRENT_DEFAULT_TRANSFORM_GROUP | CURRENT_PATH | CURRENT_ROLE
-  | CURRENT_ROW | CURRENT_SCHEMA | CURRENT_TIME | CURRENT_TIMESTAMP
-  | CURRENT_TRANSFORM_GROUP_FOR_TYPE | CURRENT_USER | CURSOR | CYCLE
-
-  | DATE | DAY | DEALLOCATE | DEC | DECIMAL | DECLARE | DEFAULT | DELETE
-  | DENSE_RANK | DEREF | DESCRIBE | DETERMINISTIC | DISCONNECT | DISTINCT
-  | DOUBLE | DROP | DYNAMIC
-
-  | EACH | ELEMENT | ELSE | END | END_FRAME | END_PARTITION | END-EXEC
-  | EQUALS | ESCAPE | EVERY | EXCEPT | EXEC | EXECUTE | EXISTS | EXP
-  | EXTERNAL | EXTRACT
-
-  | FALSE | FETCH | FILTER | FIRST_VALUE | FLOAT | FLOOR | FOR | FOREIGN
-  | FRAME_ROW | FREE | FROM | FULL | FUNCTION | FUSION
-
-  | GET | GLOBAL | GRANT | GROUP | GROUPING | GROUPS
-
-  | HAVING | HOLD | HOUR
-
-  | IDENTITY | IN | INDICATOR | INNER | INOUT | INSENSITIVE | INSERT
-  | INT | INTEGER | INTERSECT | INTERSECTION | INTERVAL | INTO | IS
-
-  | JOIN
-
-  | LAG | LANGUAGE | LARGE | LAST_VALUE | LATERAL | LEAD | LEADING | LEFT
-  | LIKE | LIKE_REGEX | LN | LOCAL | LOCALTIME | LOCALTIMESTAMP | LOWER
-
-  | MATCH | MAX | MEMBER | MERGE | METHOD | MIN | MINUTE
-  | MOD | MODIFIES | MODULE | MONTH | MULTISET
-
-  | NATIONAL | NATURAL | NCHAR | NCLOB | NEW | NO | NONE | NORMALIZE | NOT
-  | NTH_VALUE | NTILE | NULL | NULLIF | NUMERIC
-
-  | OCTET_LENGTH | OCCURRENCES_REGEX | OF | OFFSET | OLD | ON | ONLY | OPEN
-  | OR | ORDER | OUT | OUTER | OVER | OVERLAPS | OVERLAY
-
-  | PARAMETER | PARTITION | PERCENT | PERCENT_RANK | PERCENTILE_CONT
-  | PERCENTILE_DISC | PERIOD | PORTION | POSITION | POSITION_REGEX | POWER | PRECEDES
-  | PRECISION | PREPARE | PRIMARY | PROCEDURE
-
-  | RANGE | RANK | READS | REAL | RECURSIVE | REF | REFERENCES | REFERENCING
-  | REGR_AVGX | REGR_AVGY | REGR_COUNT | REGR_INTERCEPT | REGR_R2 | REGR_SLOPE
-  | REGR_SXX | REGR_SXY | REGR_SYY | RELEASE | RESULT | RETURN | RETURNS
-  | REVOKE | RIGHT | ROLLBACK | ROLLUP | ROW | ROW_NUMBER | ROWS
-
-  | SAVEPOINT | SCOPE | SCROLL | SEARCH | SECOND | SELECT
-  | SENSITIVE | SESSION_USER | SET | SIMILAR | SMALLINT | SOME | SPECIFIC
-  | SPECIFICTYPE | SQL | SQLEXCEPTION | SQLSTATE | SQLWARNING | SQRT | START
-  | STATIC | STDDEV_POP | STDDEV_SAMP | SUBMULTISET | SUBSTRING | SUBSTRING_REGEX
-  | SUCCEEDS | SUM | SYMMETRIC | SYSTEM | SYSTEM_TIME | SYSTEM_USER
-
-  | TABLE | TABLESAMPLE | THEN | TIME | TIMESTAMP | TIMEZONE_HOUR | TIMEZONE_MINUTE
-  | TO | TRAILING | TRANSLATE | TRANSLATE_REGEX | TRANSLATION | TREAT
-  | TRIGGER | TRUNCATE | TRIM | TRIM_ARRAY | TRUE
-
-  | UESCAPE | UNION | UNIQUE | UNKNOWN | UNNEST | UPDATE | UPPER | USER | USING
-
-  | VALUE | VALUES | VALUE_OF | VAR_POP | VAR_SAMP | VARBINARY
-  | VARCHAR | VARYING | VERSIONING
-
-  | WHEN | WHENEVER | WHERE | WIDTH_BUCKET | WINDOW | WITH | WITHIN | WITHOUT
-
-  | YEAR
-
-== 5.3 <literal>
-
-Function
-Specify a non-null value.
-
-> literals :: TestItem
-> literals = Group "literals"
->     [numericLiterals,generalLiterals]
-
-<literal> ::= <signed numeric literal> | <general literal>
-
-<unsigned literal> ::= <unsigned numeric literal> | <general literal>
-
-<general literal> ::=
-    <character string literal>
-  | <national character string literal>
-  | <Unicode character string literal>
-  | <binary string literal>
-  | <datetime literal>
-  | <interval literal>
-  | <boolean literal>
-
-> generalLiterals :: TestItem
-> generalLiterals = Group "general literals"
->     [characterStringLiterals
->     ,nationalCharacterStringLiterals
->     ,unicodeCharacterStringLiterals
->     ,binaryStringLiterals
->     ,dateTimeLiterals
->     ,intervalLiterals
->     ,booleanLiterals]
-
-<character string literal> ::=
-  [ <introducer> <character set specification> ]
-      <quote> [ <character representation>... ] <quote>
-      [ { <separator> <quote> [ <character representation>... ] <quote> }... ]
-
-<introducer> ::= <underscore>
-
-<character representation> ::= <nonquote character> | <quote symbol>
-
-<nonquote character> ::= !! See the Syntax Rules.
-
-<quote symbol> ::= <quote> <quote>
-
-> characterStringLiterals :: TestItem
-> characterStringLiterals = Group "character string literals"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("'a regular string literal'"
->      ,StringLit "'" "'" "a regular string literal")
->     ,("'something' ' some more' 'and more'"
->      ,StringLit "'" "'" "something some moreand more")
->     ,("'something' \n ' some more' \t 'and more'"
->      ,StringLit "'" "'" "something some moreand more")
->     ,("'something' -- a comment\n ' some more' /*another comment*/ 'and more'"
->      ,StringLit "'" "'" "something some moreand more")
->     ,("'a quote: '', stuff'"
->      ,StringLit "'" "'" "a quote: '', stuff")
->     ,("''"
->      ,StringLit "'" "'" "")
-
-I'm not sure how this should work. Maybe the parser should reject non
-ascii characters in strings and identifiers unless the current SQL
-character set allows them.
-
->     ,("_francais 'français'"
->      ,TypedLit (TypeName [Name Nothing "_francais"]) "français")
->     ]
-
-<national character string literal> ::=
-  N <quote> [ <character representation>... ]
-      <quote> [ { <separator> <quote> [ <character representation>... ] <quote> }... ]
-
-> nationalCharacterStringLiterals :: TestItem
-> nationalCharacterStringLiterals = Group "national character string literals"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("N'something'", StringLit "N'" "'" "something")
->     ,("n'something'", StringLit "n'" "'" "something")
->     ]
-
-<Unicode character string literal> ::=
-  [ <introducer> <character set specification> ]
-      U <ampersand> <quote> [ <Unicode representation>... ] <quote>
-      [ { <separator> <quote> [ <Unicode representation>... ] <quote> }... ]
-      <Unicode escape specifier>
-
-<Unicode representation> ::=
-    <character representation>
-  | <Unicode escape value>
-
-> unicodeCharacterStringLiterals :: TestItem
-> unicodeCharacterStringLiterals = Group "unicode character string literals"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("U&'something'", StringLit "U&'" "'" "something")
->     {-,("u&'something' escape ="
->      ,Escape (StringLit "u&'" "'" "something") '=')
->     ,("u&'something' uescape ="
->      ,UEscape (StringLit "u&'" "'" "something") '=')-}
->     ]
-
-TODO: unicode escape
-
-<binary string literal> ::=
-  X <quote> [ <space>... ] [ { <hexit> [ <space>... ] <hexit> [ <space>... ] }... ] <quote>
-      [ { <separator> <quote> [ <space>... ] [ { <hexit> [ <space>... ]
-      <hexit> [ <space>... ] }... ] <quote> }... ]
-
-<hexit> ::= <digit> | A | B | C | D | E | F | a | b | c | d | e | f
-
-> binaryStringLiterals :: TestItem
-> binaryStringLiterals = Group "binary string literals"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [--("B'101010'", CSStringLit "B" "101010")
->      ("X'7f7f7f'", StringLit "X'" "'" "7f7f7f")
->     --,("X'7f7f7f' escape z", Escape (StringLit "X'" "'" "7f7f7f") 'z')
->     ]
-
-<signed numeric literal> ::= [ <sign> ] <unsigned numeric literal>
-
-<unsigned numeric literal> ::=
-    <exact numeric literal>
-  | <approximate numeric literal>
-
-<exact numeric literal> ::=
-    <unsigned integer> [ <period> [ <unsigned integer> ] ]
-  | <period> <unsigned integer>
-
-<sign> ::= <plus sign> | <minus sign>
-
-<approximate numeric literal> ::= <mantissa> E <exponent>
-
-<mantissa> ::= <exact numeric literal>
-
-<exponent> ::= <signed integer>
-
-<signed integer> ::= [ <sign> ] <unsigned integer>
-
-<unsigned integer> ::= <digit>...
-
-> numericLiterals :: TestItem
-> numericLiterals = Group "numeric literals"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("11", NumLit "11")
->     ,("11.11", NumLit "11.11")
-
->     ,("11E23", NumLit "11E23")
->     ,("11E+23", NumLit "11E+23")
->     ,("11E-23", NumLit "11E-23")
-
->     ,("11.11E23", NumLit "11.11E23")
->     ,("11.11E+23", NumLit "11.11E+23")
->     ,("11.11E-23", NumLit "11.11E-23")
-
->     ,("+11E23", PrefixOp [Name Nothing "+"] $ NumLit "11E23")
->     ,("+11E+23", PrefixOp [Name Nothing "+"] $ NumLit "11E+23")
->     ,("+11E-23", PrefixOp [Name Nothing "+"] $ NumLit "11E-23")
->     ,("+11.11E23", PrefixOp [Name Nothing "+"] $ NumLit "11.11E23")
->     ,("+11.11E+23", PrefixOp [Name Nothing "+"] $ NumLit "11.11E+23")
->     ,("+11.11E-23", PrefixOp [Name Nothing "+"] $ NumLit "11.11E-23")
-
->     ,("-11E23", PrefixOp [Name Nothing "-"] $ NumLit "11E23")
->     ,("-11E+23", PrefixOp [Name Nothing "-"] $ NumLit "11E+23")
->     ,("-11E-23", PrefixOp [Name Nothing "-"] $ NumLit "11E-23")
->     ,("-11.11E23", PrefixOp [Name Nothing "-"] $ NumLit "11.11E23")
->     ,("-11.11E+23", PrefixOp [Name Nothing "-"] $ NumLit "11.11E+23")
->     ,("-11.11E-23", PrefixOp [Name Nothing "-"] $ NumLit "11.11E-23")
-
->     ,("11.11e23", NumLit "11.11e23")
-
->     ]
-
-<datetime literal> ::= <date literal> | <time literal> | <timestamp literal>
-
-<date literal> ::= DATE <date string>
-
-<time literal> ::= TIME <time string>
-
-<timestamp literal> ::= TIMESTAMP <timestamp string>
-
-<date string> ::= <quote> <unquoted date string> <quote>
-
-<time string> ::= <quote> <unquoted time string> <quote>
-
-<timestamp string> ::= <quote> <unquoted timestamp string> <quote>
-
-<time zone interval> ::= <sign> <hours value> <colon> <minutes value>
-
-<date value> ::=
-  <years value> <minus sign> <months value> <minus sign> <days value>
-
-<time value> ::= <hours value> <colon> <minutes value> <colon> <seconds value>
-
-> dateTimeLiterals :: TestItem
-> dateTimeLiterals = Group "datetime literals"
->     [-- TODO: datetime literals
->     ]
-
-<interval literal> ::=
-  INTERVAL [ <sign> ] <interval string> <interval qualifier>
-
-<interval string> ::= <quote> <unquoted interval string> <quote>
-
-<unquoted date string> ::= <date value>
-
-<unquoted time string> ::= <time value> [ <time zone interval> ]
-
-<unquoted timestamp string> ::=
-  <unquoted date string> <space> <unquoted time string>
-
-<unquoted interval string> ::=
-  [ <sign> ] { <year-month literal> | <day-time literal> }
-
-<year-month literal> ::=
-    <years value> [ <minus sign> <months value> ]
-  | <months value>
-
-<day-time literal> ::= <day-time interval> | <time interval>
-
-<day-time interval> ::=
-  <days value> [ <space> <hours value> [ <colon> <minutes value>
-      [ <colon> <seconds value> ] ] ]
-
-<time interval> ::=
-    <hours value> [ <colon> <minutes value> [ <colon> <seconds value> ] ]
-  | <minutes value> [ <colon> <seconds value> ]
-  | <seconds value>
-
-<years value> ::= <datetime value>
-
-<months value> ::= <datetime value>
-
-<days value> ::= <datetime value>
-
-<hours value> ::= <datetime value>
-
-<minutes value> ::= <datetime value>
-
-<seconds value> ::= <seconds integer value> [ <period> [ <seconds fraction> ] ]
-
-<seconds integer value> ::= <unsigned integer>
-
-<seconds fraction> ::= <unsigned integer>
-
-<datetime value> ::= <unsigned integer>
-
-> intervalLiterals :: TestItem
-> intervalLiterals = Group "intervalLiterals literals"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("interval '1'", TypedLit (TypeName [Name Nothing "interval"]) "1")
->     ,("interval '1' day"
->      ,IntervalLit Nothing "1" (Itf "day" Nothing) Nothing)
->     ,("interval '1' day(3)"
->      ,IntervalLit Nothing "1" (Itf "day" $ Just (3,Nothing)) Nothing)
->     ,("interval + '1' day(3)"
->      ,IntervalLit (Just Plus) "1" (Itf "day" $ Just (3,Nothing)) Nothing)
->     ,("interval - '1' second(2,2)"
->      ,IntervalLit (Just Minus) "1" (Itf "second" $ Just (2,Just 2)) Nothing)
->     ,("interval '1' year to month"
->      ,IntervalLit Nothing "1" (Itf "year" Nothing)
->                                   (Just $ Itf "month" Nothing))
-
->     ,("interval '1' year(4) to second(2,3) "
->      ,IntervalLit Nothing "1" (Itf "year" $ Just (4,Nothing))
->                             (Just $ Itf "second" $ Just (2, Just 3)))
->     ]
-
-<boolean literal> ::= TRUE | FALSE | UNKNOWN
-
-> booleanLiterals :: TestItem
-> booleanLiterals = Group "boolean literals"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("true", Iden [Name Nothing "true"])
->     ,("false", Iden [Name Nothing "false"])
->     ,("unknown", Iden [Name Nothing "unknown"])
->     ]
-
-== 5.4 Names and identifiers
-
-Function
-Specify names.
-
-<identifier> ::= <actual identifier>
-
-<actual identifier> ::=
-    <regular identifier>
-  | <delimited identifier>
-  | <Unicode delimited identifier>
-
-> identifiers :: TestItem
-> identifiers = Group "identifiers"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("test",Iden [Name Nothing "test"])
->     ,("_test",Iden [Name Nothing "_test"])
->     ,("t1",Iden [Name Nothing "t1"])
->     ,("a.b",Iden [Name Nothing "a", Name Nothing "b"])
->     ,("a.b.c",Iden [Name Nothing "a", Name Nothing "b", Name Nothing "c"])
->     ,("\"quoted iden\"", Iden [Name (Just ("\"","\"")) "quoted iden"])
->     ,("\"quoted \"\" iden\"", Iden [Name (Just ("\"","\"")) "quoted \"\" iden"])
->     ,("U&\"quoted iden\"", Iden [Name (Just ("U&\"","\"")) "quoted iden"])
->     ,("U&\"quoted \"\" iden\"", Iden [Name (Just ("U&\"","\"")) "quoted \"\" iden"])
->     ]
-
-TODO: more identifiers, e.g. unicode escapes?, mixed quoted/unquoted
-chains
-
-TODO: review below stuff for exact rules
-
-<SQL language identifier> ::=
-  <SQL language identifier start> [ <SQL language identifier part>... ]
-
-<SQL language identifier start> ::= <simple Latin letter>
-
-<SQL language identifier part> ::=
-    <simple Latin letter>
-  | <digit>
-  | <underscore>
-
-<authorization identifier> ::= <role name> | <user identifier>
-
-<table name> ::= <local or schema qualified name>
-
-<domain name> ::= <schema qualified name>
-
-<schema name> ::= [ <catalog name> <period> ] <unqualified schema name>
-
-<unqualified schema name> ::= <identifier>
-
-<catalog name> ::= <identifier>
-
-<schema qualified name> ::= [ <schema name> <period> ] <qualified identifier>
-
-<local or schema qualified name> ::=
-  [ <local or schema qualifier> <period> ] <qualified identifier>
-
-<local or schema qualifier> ::= <schema name> | <local qualifier>
-
-<qualified identifier> ::= <identifier>
-
-<column name> ::= <identifier>
-
-<correlation name> ::= <identifier>
-
-<query name> ::= <identifier>
-
-<SQL-client module name> ::= <identifier>
-
-<procedure name> ::= <identifier>
-
-<schema qualified routine name> ::= <schema qualified name>
-
-<method name> ::= <identifier>
-
-<specific name> ::= <schema qualified name>
-
-<cursor name> ::= <local qualified name>
-
-<local qualified name> ::=
-  [ <local qualifier> <period> ] <qualified identifier>
-
-<local qualifier> ::= MODULE
-
-<host parameter name> ::= <colon> <identifier>
-
-<SQL parameter name> ::= <identifier>
-
-<constraint name> ::= <schema qualified name>
-
-<external routine name> ::= <identifier> | <character string literal>
-
-<trigger name> ::= <schema qualified name>
-
-<collation name> ::= <schema qualified name>
-
-<character set name> ::= [ <schema name> <period> ] <SQL language identifier>
-
-<transliteration name> ::= <schema qualified name>
-
-<transcoding name> ::= <schema qualified name>
-
-<schema-resolved user-defined type name> ::= <user-defined type name>
-
-<user-defined type name> ::= [ <schema name> <period> ] <qualified identifier>
-
-<attribute name> ::= <identifier>
-
-<field name> ::= <identifier>
-
-<savepoint name> ::= <identifier>
-
-<sequence generator name> ::= <schema qualified name>
-
-<role name> ::= <identifier>
-
-<user identifier> ::= <identifier>
-
-<connection name> ::= <simple value specification>
-
-<SQL-server name> ::= <simple value specification>
-
-<connection user name> ::= <simple value specification>
-
-<SQL statement name> ::= <statement name> | <extended statement name>
-
-<statement name> ::= <identifier>
-
-<extended statement name> ::= [ <scope option> ] <simple value specification>
-
-<dynamic cursor name> ::= <cursor name> | <extended cursor name>
-
-<extended cursor name> ::= [ <scope option> ] <simple value specification>
-
-<descriptor name> ::=
-    <non-extended descriptor name>
-  | <extended descriptor name>
-
-<non-extended descriptor name> ::= <identifier>
-
-<extended descriptor name> ::= [ <scope option> ] <simple value specification>
-
-<scope option> ::= GLOBAL | LOCAL
-
-<window name> ::= <identifier>
-
-= 6 Scalar expressions
-
-== 6.1 <data type>
-
-Function
-Specify a data type.
-
-<data type> ::=
-    <predefined type>
-  | <row type>
-  | <path-resolved user-defined type name>
-  | <reference type>
-  | <collection type>
-
-<predefined type> ::=
-    <character string type> [ CHARACTER SET <character set specification> ]
-          [ <collate clause> ]
-  | <national character string type> [ <collate clause> ]
-  | <binary string type>
-  | <numeric type>
-  | <boolean type>
-  | <datetime type>
-  | <interval type>
-
-<character string type> ::=
-    CHARACTER [ <left paren> <character length> <right paren> ]
-  | CHAR [ <left paren> <character length> <right paren> ]
-  | CHARACTER VARYING <left paren> <character length> <right paren>
-  | CHAR VARYING <left paren> <character length> <right paren>
-  | VARCHAR <left paren> <character length> <right paren>
-  | <character large object type>
-
-<character large object type> ::=
-    CHARACTER LARGE OBJECT [ <left paren> <character large object length> <right paren> ]
-  | CHAR LARGE OBJECT [ <left paren> <character large object length> <right paren> ]
-  | CLOB [ <left paren> <character large object length> <right paren> ]
-
-<national character string type> ::=
-    NATIONAL CHARACTER [ <left paren> <character length> <right paren> ]
-  | NATIONAL CHAR [ <left paren> <character length> <right paren> ]
-  | NCHAR [ <left paren> <character length> <right paren> ]
-  | NATIONAL CHARACTER VARYING <left paren> <character length> <right paren>
-  | NATIONAL CHAR VARYING <left paren> <character length> <right paren>
-  | NCHAR VARYING <left paren> <character length> <right paren>
-  | <national character large object type>
-
-<national character large object type> ::=
-    NATIONAL CHARACTER LARGE OBJECT [ <left paren> <character large object length> <right
-    paren> ]
-  | NCHAR LARGE OBJECT [ <left paren> <character large object length> <right paren> ]
-  | NCLOB [ <left paren> <character large object length> <right paren> ]
-
-<binary string type> ::=
-    BINARY [ <left paren> <length> <right paren> ]
-  | BINARY VARYING <left paren> <length> <right paren>
-  | VARBINARY <left paren> <length> <right paren>
-  | <binary large object string type>
-
-<binary large object string type> ::=
-    BINARY LARGE OBJECT [ <left paren> <large object length> <right paren> ]
-  | BLOB [ <left paren> <large object length> <right paren> ]
-
-<numeric type> ::= <exact numeric type> | <approximate numeric type>
-
-<exact numeric type> ::=
-    NUMERIC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
-  | DECIMAL [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
-  | DEC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
-  | SMALLINT
-  | INTEGER
-  | INT
-  | BIGINT
-
-<approximate numeric type> ::=
-    FLOAT [ <left paren> <precision> <right paren> ]
-  | REAL
-  | DOUBLE PRECISION
-
-<length> ::= <unsigned integer>
-
-<character length> ::= <length> [ <char length units> ]
-
-<large object length> ::=
-    <length> [ <multiplier> ]
-  | <large object length token>
-
-<character large object length> ::=
-  <large object length> [ <char length units> ]
-
-<char length units> ::= CHARACTERS | OCTETS
-
-<precision> ::= <unsigned integer>
-
-<scale> ::= <unsigned integer>
-
-<boolean type> ::= BOOLEAN
-
-<datetime type> ::=
-    DATE
-  | TIME [ <left paren> <time precision> <right paren> ] [ <with or without time zone> ]
-  | TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
-      [ <with or without time zone> ]
-
-<with or without time zone> ::= WITH TIME ZONE | WITHOUT TIME ZONE
-
-<time precision> ::= <time fractional seconds precision>
-
-<timestamp precision> ::= <time fractional seconds precision>
-
-<time fractional seconds precision> ::= <unsigned integer>
-
-<interval type> ::= INTERVAL <interval qualifier>
-
-<row type> ::= ROW <row type body>
-
-<row type body> ::=
-  <left paren> <field definition> [ { <comma> <field definition> }... ] <right paren>
-
-<reference type> ::=
-  REF <left paren> <referenced type> <right paren> [ <scope clause> ]
-
-<scope clause> ::= SCOPE <table name>
-
-<referenced type> ::= <path-resolved user-defined type name>
-
-<path-resolved user-defined type name> ::= <user-defined type name>
-
-<collection type> ::= <array type> | <multiset type>
-
-<array type> ::=
-  <data type> ARRAY
-      [ <left bracket or trigraph> <maximum cardinality> <right bracket or trigraph> ]
-
-<maximum cardinality> ::= <unsigned integer>
-
-<multiset type> ::= <data type> MULTISET
-
-TODO: below, add new stuff:
-review the length syntaxes
-binary, binary varying/varbinary
-new multipliers
-
-create a list of type name variations:
-
-> typeNames :: ([(String,TypeName)],[(String,TypeName)])
-> typeNames =
->     (basicTypes, concatMap makeArray basicTypes
->                  ++ map makeMultiset basicTypes)
->   where
->     makeArray (s,t) = [(s ++ " array", ArrayTypeName t Nothing)
->                       ,(s ++ " array[5]", ArrayTypeName t (Just 5))]
->     makeMultiset (s,t) = (s ++ " multiset", MultisetTypeName t)
->     basicTypes =
->         -- example of every standard type name
->         map (\t -> (t,TypeName [Name Nothing t]))
->         ["binary"
->         ,"binary varying"
->         ,"character"
->         ,"char"
->         ,"character varying"
->         ,"char varying"
->         ,"varbinary"
->         ,"varchar"
->         ,"character large object"
->         ,"char large object"
->         ,"clob"
->         ,"national character"
->         ,"national char"
->         ,"nchar"
->         ,"national character varying"
->         ,"national char varying"
->         ,"nchar varying"
->         ,"national character large object"
->         ,"nchar large object"
->         ,"nclob"
->         ,"binary large object"
->         ,"blob"
->         ,"numeric"
->         ,"decimal"
->         ,"dec"
->         ,"smallint"
->         ,"integer"
->         ,"int"
->         ,"bigint"
->         ,"float"
->         ,"real"
->         ,"double precision"
->         ,"boolean"
->         ,"date"
->         ,"time"
->         ,"timestamp"]
->         --interval -- not allowed without interval qualifier
->         --row -- not allowed without row type body
->         -- array -- not allowed on own
->         -- multiset -- not allowed on own
-
->          ++
->          [-- 1 single prec + 1 with multiname
->           ("char(5)", PrecTypeName [Name Nothing "char"] 5)
->          ,("char varying(5)", PrecTypeName [Name Nothing "char varying"] 5)
->          -- 1 scale
->          ,("decimal(15,2)", PrecScaleTypeName [Name Nothing "decimal"] 15 2)
->          ,("char(3 octets)"
->           ,PrecLengthTypeName [Name Nothing "char"] 3 Nothing (Just PrecOctets))
->          ,("varchar(50 characters)"
->           ,PrecLengthTypeName [Name Nothing "varchar"] 50 Nothing (Just PrecCharacters))
->          -- lob prec + with multiname
->          ,("blob(3M)", PrecLengthTypeName [Name Nothing "blob"] 3 (Just PrecM) Nothing)
->          ,("blob(3T)", PrecLengthTypeName [Name Nothing "blob"] 3 (Just PrecT) Nothing)
->          ,("blob(3P)", PrecLengthTypeName [Name Nothing "blob"] 3 (Just PrecP) Nothing)
->          ,("blob(4M characters) "
->           ,PrecLengthTypeName [Name Nothing "blob"] 4 (Just PrecM) (Just PrecCharacters))
->          ,("blob(6G octets) "
->           ,PrecLengthTypeName [Name Nothing "blob"] 6 (Just PrecG) (Just PrecOctets))
->          ,("national character large object(7K) "
->           ,PrecLengthTypeName [Name Nothing "national character large object"]
->                7 (Just PrecK) Nothing)
->          -- 1 with and without tz
->          ,("time with time zone"
->           ,TimeTypeName [Name Nothing "time"] Nothing True)
->          ,("datetime(3) without time zone"
->           ,TimeTypeName [Name Nothing "datetime"] (Just 3) False)
->          -- chars: (single/multiname) x prec x charset x collate
->          -- 1111
->          ,("char varying(5) character set something collate something_insensitive"
->           ,CharTypeName [Name Nothing "char varying"] (Just 5)
->            [Name Nothing "something"] [Name Nothing "something_insensitive"])
->           -- 0111
->          ,("char(5) character set something collate something_insensitive"
->           ,CharTypeName [Name Nothing "char"] (Just 5)
->            [Name Nothing "something"] [Name Nothing "something_insensitive"])
-
->           -- 1011
->          ,("char varying character set something collate something_insensitive"
->           ,CharTypeName [Name Nothing "char varying"] Nothing
->            [Name Nothing "something"] [Name Nothing "something_insensitive"])
->           -- 0011
->          ,("char character set something collate something_insensitive"
->           ,CharTypeName [Name Nothing "char"] Nothing
->            [Name Nothing "something"] [Name Nothing "something_insensitive"])
-
->           -- 1101
->          ,("char varying(5) collate something_insensitive"
->           ,CharTypeName [Name Nothing "char varying"] (Just 5)
->            [] [Name Nothing "something_insensitive"])
->           -- 0101
->          ,("char(5) collate something_insensitive"
->           ,CharTypeName [Name Nothing "char"] (Just 5)
->            [] [Name Nothing "something_insensitive"])
->           -- 1001
->          ,("char varying collate something_insensitive"
->           ,CharTypeName [Name Nothing "char varying"] Nothing
->            [] [Name Nothing "something_insensitive"])
->           -- 0001
->          ,("char collate something_insensitive"
->           ,CharTypeName [Name Nothing "char"] Nothing
->            [] [Name Nothing "something_insensitive"])
-
->          -- 1110
->          ,("char varying(5) character set something"
->           ,CharTypeName [Name Nothing "char varying"] (Just 5)
->            [Name Nothing "something"] [])
->           -- 0110
->          ,("char(5) character set something"
->           ,CharTypeName [Name Nothing "char"] (Just 5)
->            [Name Nothing "something"] [])
->           -- 1010
->          ,("char varying character set something"
->           ,CharTypeName [Name Nothing "char varying"] Nothing
->            [Name Nothing "something"] [])
->           -- 0010
->          ,("char character set something"
->           ,CharTypeName [Name Nothing "char"] Nothing
->            [Name Nothing "something"] [])
->           -- 1100
->          ,("char varying character set something"
->           ,CharTypeName [Name Nothing "char varying"] Nothing
->            [Name Nothing "something"] [])
-
->          -- single row field, two row field
->          ,("row(a int)", RowTypeName [(Name Nothing "a", TypeName [Name Nothing "int"])])
->          ,("row(a int,b char)"
->           ,RowTypeName [(Name Nothing "a", TypeName [Name Nothing "int"])
->                        ,(Name Nothing "b", TypeName [Name Nothing "char"])])
->          -- interval each type raw
->          ,("interval year"
->           ,IntervalTypeName (Itf "year" Nothing) Nothing)
->          -- one type with single suffix
->          -- one type with double suffix
->          ,("interval year(2)"
->           ,IntervalTypeName (Itf "year" $ Just (2,Nothing)) Nothing)
->          ,("interval second(2,5)"
->           ,IntervalTypeName (Itf "second" $ Just (2,Just 5)) Nothing)
->          -- a to b with raw
->          -- a to b with single suffix
->          ,("interval year to month"
->           ,IntervalTypeName (Itf "year" Nothing)
->                             (Just $ Itf "month" Nothing))
->          ,("interval year(4) to second(2,3)"
->           ,IntervalTypeName (Itf "year" $ Just (4,Nothing))
->                             (Just $ Itf "second" $ Just (2, Just 3)))
->          ]
-
-Now test each variation in both cast expression and typed literal
-expression
-
-> typeNameTests :: TestItem
-> typeNameTests = Group "type names"
->     [Group "type names" $ map (uncurry (TestScalarExpr ansi2011))
->                           $ concatMap makeSimpleTests $ fst typeNames
->     ,Group "generated casts" $ map (uncurry (TestScalarExpr ansi2011))
->                           $ concatMap makeCastTests $ fst typeNames
->     ,Group "generated typename" $ map (uncurry (TestScalarExpr ansi2011))
->                           $ concatMap makeTests $ snd typeNames]
->   where
->     makeSimpleTests (ctn, stn) =
->         [(ctn ++ " 'test'", TypedLit stn "test")
->         ]
->     makeCastTests (ctn, stn) =
->         [("cast('test' as " ++ ctn ++ ")", Cast (StringLit "'" "'" "test") stn)
->         ]
->     makeTests a = makeSimpleTests a ++ makeCastTests a
-
-
-== 6.2 <field definition>
-
-Function
-Define a field of a row type.
-
-<field definition> ::= <field name> <data type>
-
-> fieldDefinition :: TestItem
-> fieldDefinition = Group "field definition"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("cast('(1,2)' as row(a int,b char))"
->      ,Cast (StringLit "'" "'" "(1,2)")
->      $ RowTypeName [(Name Nothing "a", TypeName [Name Nothing "int"])
->                    ,(Name Nothing "b", TypeName [Name Nothing "char"])])]
-
-== 6.3 <value expression primary>
-
-Function
-Specify a value that is syntactically self-delimited.
-
-<value expression primary> ::=
-    <parenthesized value expression>
-  | <nonparenthesized value expression primary>
-
-<parenthesized value expression> ::=
-  <left paren> <value expression> <right paren>
-
-<nonparenthesized value expression primary> ::=
-       <unsigned value specification>
-     | <column reference>
-     | <set function specification>
-     | <window function>
-     | <nested window function>
-     | <scalar subquery>
-     | <case expression>
-     | <cast specification>
-     | <field reference>
-     | <subtype treatment>
-     | <method invocation>
-     | <static method invocation>
-     | <new specification>
-     | <attribute or method reference>
-     | <reference resolution>
-     | <collection value constructor>
-     | <array element reference>
-     | <multiset element reference>
-     | <next value expression>
-     | <routine invocation>
-
-<collection value constructor> ::=
-    <array value constructor>
-  | <multiset value constructor>
-
-> valueExpressions :: TestItem
-> valueExpressions = Group "value expressions"
->     [generalValueSpecification
->     ,parameterSpecification
->     ,contextuallyTypedValueSpecification
->     ,identifierChain
->     ,columnReference
->     ,setFunctionSpecification
->     ,windowFunction
->     ,nestedWindowFunction
->     ,caseExpression
->     ,castSpecification
->     ,nextScalarExpression
->     ,fieldReference
->     ,arrayElementReference
->     ,multisetElementReference
->     ,numericScalarExpression
->     ,numericValueFunction
->     ,stringScalarExpression
->     ,stringValueFunction
->     ,datetimeScalarExpression
->     ,datetimeValueFunction
->     ,intervalScalarExpression
->     ,intervalValueFunction
->     ,booleanScalarExpression
->     ,arrayScalarExpression
->     ,arrayValueFunction
->     ,arrayValueConstructor
->     ,multisetScalarExpression
->     ,multisetValueFunction
->     ,multisetValueConstructor
->     ,parenthesizedScalarExpression
->     ]
-
-> parenthesizedScalarExpression :: TestItem
-> parenthesizedScalarExpression = Group "parenthesized value expression"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("(3)", Parens (NumLit "3"))
->     ,("((3))", Parens $ Parens (NumLit "3"))
->     ]
-
-== 6.4 <value specification> and <target specification>
-
-Function
-Specify one or more values, host parameters, SQL parameters, dynamic parameters, or host variables.
-
-<value specification> ::= <literal> | <general value specification>
-
-<unsigned value specification> ::=
-    <unsigned literal>
-  | <general value specification>
-
- <general value specification> ::=
-    <host parameter specification>
-  | <SQL parameter reference>
-  | <dynamic parameter specification>
-  | <embedded variable specification>
-  | <current collation specification>
-  | CURRENT_CATALOG
-  | CURRENT_DEFAULT_TRANSFORM_GROUP
-  | CURRENT_PATH
-  | CURRENT_ROLE
-  | CURRENT_SCHEMA
-  | CURRENT_TRANSFORM_GROUP_FOR_TYPE <path-resolved user-defined type name>
-  | CURRENT_USER
-  | SESSION_USER
-  | SYSTEM_USER
-  | USER
-  | VALUE
-
-> generalValueSpecification :: TestItem
-> generalValueSpecification = Group "general value specification"
->     $ map (uncurry (TestScalarExpr ansi2011)) $
->     map mkIden ["CURRENT_DEFAULT_TRANSFORM_GROUP"
->                ,"CURRENT_PATH"
->                ,"CURRENT_ROLE"
->                ,"CURRENT_USER"
->                ,"SESSION_USER"
->                ,"SYSTEM_USER"
->                ,"USER"
->                ,"VALUE"]
->   where
->     mkIden nm = (nm,Iden [Name Nothing nm])
-
-TODO: add the missing bits
-
-<simple value specification> ::=
-    <literal>
-  | <host parameter name>
-  | <SQL parameter reference>
-  | <embedded variable name>
-
-<target specification> ::=
-    <host parameter specification>
-  | <SQL parameter reference>
-  | <column reference>
-  | <target array element specification>
-  | <dynamic parameter specification>
-  | <embedded variable specification>
-
-<simple target specification> ::=
-    <host parameter name>
-  | <SQL parameter reference>
-  | <column reference>
-  | <embedded variable name>
-
-<host parameter specification> ::=
-  <host parameter name> [ <indicator parameter> ]
-
-<dynamic parameter specification> ::= <question mark>
-
-<embedded variable specification> ::=
-  <embedded variable name> [ <indicator variable> ]
-
-<indicator variable> ::= [ INDICATOR ] <embedded variable name>
-
-<indicator parameter> ::= [ INDICATOR ] <host parameter name>
-
-<target array element specification> ::=
-  <target array reference>
-      <left bracket or trigraph> <simple value specification> <right bracket or trigraph>
-
-<target array reference> ::= <SQL parameter reference> | <column reference>
-
-> parameterSpecification :: TestItem
-> parameterSpecification = Group "parameter specification"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [(":hostparam", HostParameter ":hostparam" Nothing)
->     ,(":hostparam indicator :another_host_param"
->      ,HostParameter ":hostparam" $ Just ":another_host_param")
->     ,("?", Parameter)
->     ,(":h[3]", Array (HostParameter ":h" Nothing) [NumLit "3"])
->     ]
-
-<current collation specification> ::=
-  COLLATION FOR <left paren> <string value expression> <right paren>
-
-TODO: review the modules stuff
-
-== 6.5 <contextually typed value specification>
-
-Function
-Specify a value whose data type is to be inferred from its context.
-
-<contextually typed value specification> ::=
-    <implicitly typed value specification>
-  | <default specification>
-
-<implicitly typed value specification> ::=
-    <null specification>
-  | <empty specification>
-
-<null specification> ::= NULL
-
-<empty specification> ::=
-    ARRAY <left bracket or trigraph> <right bracket or trigraph>
-  | MULTISET <left bracket or trigraph> <right bracket or trigraph>
-
-<default specification> ::= DEFAULT
-
-> contextuallyTypedValueSpecification :: TestItem
-> contextuallyTypedValueSpecification =
->     Group "contextually typed value specification"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("null", Iden [Name Nothing "null"])
->     ,("array[]", Array (Iden [Name Nothing "array"]) [])
->     ,("multiset[]", MultisetCtor [])
->     ,("default", Iden [Name Nothing "default"])
->     ]
-
-== 6.6 <identifier chain>
-
-Function
-Disambiguate a <period>-separated chain of identifiers.
-
-<identifier chain> ::= <identifier> [ { <period> <identifier> }... ]
-
-<basic identifier chain> ::= <identifier chain>
-
-> identifierChain :: TestItem
-> identifierChain = Group "identifier chain"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("a.b", Iden [Name Nothing "a",Name Nothing "b"])]
-
-== 6.7 <column reference>
-
-Function
-Reference a column.
-
-<column reference> ::=
-    <basic identifier chain>
-  | MODULE <period> <qualified identifier> <period> <column name>
-
-> columnReference :: TestItem
-> columnReference = Group "column reference"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("module.a.b", Iden [Name Nothing "module",Name Nothing "a",Name Nothing "b"])]
-
-== 6.8 <SQL parameter reference>
-
-Function
-Reference an SQL parameter.
-
-<SQL parameter reference> ::= <basic identifier chain>
-
-== 6.9 <set function specification>
-
-Function
-Specify a value derived by the application of a function to an argument.
-
-<set function specification> ::= <aggregate function> | <grouping operation>
-
-<grouping operation> ::=
-  GROUPING <left paren> <column reference>
-      [ { <comma> <column reference> }... ] <right paren>
-
-> setFunctionSpecification :: TestItem
-> setFunctionSpecification = Group "set function specification"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("SELECT SalesQuota, SUM(SalesYTD) TotalSalesYTD,\n\
->       \   GROUPING(SalesQuota) AS Grouping\n\
->       \FROM Sales.SalesPerson\n\
->       \GROUP BY ROLLUP(SalesQuota);"
->      ,makeSelect
->       {qeSelectList = [(Iden [Name Nothing "SalesQuota"],Nothing)
->                       ,(App [Name Nothing "SUM"] [Iden [Name Nothing "SalesYTD"]]
->                        ,Just (Name Nothing "TotalSalesYTD"))
->                       ,(App [Name Nothing "GROUPING"] [Iden [Name Nothing "SalesQuota"]]
->                        ,Just (Name Nothing "Grouping"))]
->       ,qeFrom = [TRSimple [Name Nothing "Sales",Name Nothing "SalesPerson"]]
->       ,qeGroupBy = [Rollup [SimpleGroup (Iden [Name Nothing "SalesQuota"])]]})
->     ]
-
-== 6.10 <window function>
-
-Function
-Specify a window function.
-
-<window function> ::=
-  <window function type> OVER <window name or specification>
-
-<window function type> ::=
-    <rank function type> <left paren> <right paren>
-  | ROW_NUMBER <left paren> <right paren>
-  | <aggregate function>
-  | <ntile function>
-  | <lead or lag function>
-  | <first or last value function>
-  | <nth value function>
-
-<rank function type> ::= RANK | DENSE_RANK | PERCENT_RANK | CUME_DIST
-
-<ntile function> ::= NTILE <left paren> <number of tiles> <right paren>
-
-<number of tiles> ::=
-    <simple value specification>
-  | <dynamic parameter specification>
-
-<lead or lag function> ::=
-  <lead or lag> <left paren> <lead or lag extent>
-      [ <comma> <offset> [ <comma> <default expression> ] ] <right paren>
-      [ <null treatment> ]
-
-<lead or lag> ::= LEAD | LAG
-
-<lead or lag extent> ::= <value expression>
-
-<offset> ::= <exact numeric literal>
-
-<default expression> ::= <value expression>
-
-<null treatment> ::= RESPECT NULLS | IGNORE NULLS
-
-<first or last value function> ::=
-  <first or last value> <left paren> <value expression> <right paren> [ <null treatment>
-  ]
-
-<first or last value> ::= FIRST_VALUE | LAST_VALUE
-
-<nth value function> ::=
-  NTH_VALUE <left paren> <value expression> <comma> <nth row> <right paren>
-      [ <from first or last> ] [ <null treatment> ]
-
-<nth row> ::= <simple value specification> | <dynamic parameter specification>
-
-<from first or last> ::= FROM FIRST | FROM LAST
-
-<window name or specification> ::=
-    <window name>
-  | <in-line window specification>
-
-<in-line window specification> ::= <window specification>
-
-> windowFunction :: TestItem
-> windowFunction = Group "window function"
->     [-- todo: window function
->     ]
-
-== 6.11 <nested window function>
-
-Function
-
-Specify a function nested in an aggregated argument of an
-<aggregate function> simply contained in a <window function>.
-
-<nested window function> ::=
-    <nested row number function>
-  | <value_of expression at row>
-
-<nested row number function> ::=
-  ROW_NUMBER <left paren> <row marker> <right paren>
-
-<value_of expression at row> ::=
-  VALUE_OF <left paren> <value expression> AT <row marker expression>
-      [ <comma> <value_of default value> ] <right paren>
-
-<row marker> ::=
-    BEGIN_PARTITION
-  | BEGIN_FRAME
-  | CURRENT_ROW
-  | FRAME_ROW
-  | END_FRAME
-  | END_PARTITION
-
-<row marker expression> ::= <row marker> [ <row marker delta> ]
-
-<row marker delta> ::=
-    <plus sign> <row marker offset>
-  | <minus sign> <row marker offset>
-
-<row marker offset> ::=
-    <simple value specification>
-  | <dynamic parameter specification>
-
-<value_of default value> ::= <value expression>
-
-> nestedWindowFunction :: TestItem
-> nestedWindowFunction = Group "nested window function"
->     [-- todo: nested window function
->     ]
-
-
-== 6.12 <case expression>
-
-Function
-Specify a conditional value.
-
-<case expression> ::= <case abbreviation> | <case specification>
-
-<case abbreviation> ::=
-    NULLIF <left paren> <value expression> <comma> <value expression> <right paren>
-  | COALESCE <left paren> <value expression>
-      { <comma> <value expression> }... <right paren>
-
-<case specification> ::= <simple case> | <searched case>
-
-<simple case> ::=
-  CASE <case operand> <simple when clause>... [ <else clause> ] END
-
-<searched case> ::= CASE <searched when clause>... [ <else clause> ] END
-
-<simple when clause> ::= WHEN <when operand list> THEN <result>
-
-<searched when clause> ::= WHEN <search condition> THEN <result>
-
-<else clause> ::= ELSE <result>
-
-<case operand> ::= <row value predicand> | <overlaps predicate part 1>
-
-<when operand list> ::= <when operand> [ { <comma> <when operand> }... ]
-
-<when operand> ::=
-    <row value predicand>
-  | <comparison predicate part 2>
-  | <between predicate part 2>
-  | <in predicate part 2>
-  | <character like predicate part 2>
-  | <octet like predicate part 2>
-  | <similar predicate part 2>
-  | <regex like predicate part 2>
-  | <null predicate part 2>
-  | <quantified comparison predicate part 2>
-  | <normalized predicate part 2>
-  | <match predicate part 2>
-  | <overlaps predicate part 2>
-  | <distinct predicate part 2>
-  | <member predicate part 2>
-  | <submultiset predicate part 2>
-  | <set predicate part 2>
-  | <type predicate part 2>
-
-I haven't seen these part 2 style when operands in the wild. It
-doesn't even allow all the binary operators here. We will allow them
-all, and parser and represent these expressions by considering all the
-binary ops as unary prefix ops.
-
-<result> ::= <result expression> | NULL
-
-<result expression> ::= <value expression>
-
-> caseExpression :: TestItem
-> caseExpression = Group "case expression"
->     [-- todo: case expression
->     ]
-
-== 6.13 <cast specification>
-
-Function
-Specify a data conversion.
-
-<cast specification> ::=
-     CAST <left paren> <cast operand> AS <cast target> <right paren>
-
-<cast operand> ::= <value expression> | <implicitly typed value specification>
-
-<cast target> ::= <domain name> | <data type>
-
-> castSpecification :: TestItem
-> castSpecification = Group "cast specification"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("cast(a as int)"
->      ,Cast (Iden [Name Nothing "a"]) (TypeName [Name Nothing "int"]))
->     ]
-
-== 6.14 <next value expression>
-
-Function
-Return the next value of a sequence generator.
-
-<next value expression> ::= NEXT VALUE FOR <sequence generator name>
-
-> nextScalarExpression :: TestItem
-> nextScalarExpression = Group "next value expression"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("next value for a.b", NextValueFor [Name Nothing "a", Name Nothing "b"])
->     ]
-
-== 6.15 <field reference>
-
-Function
-Reference a field of a row value.
-
-<field reference> ::= <value expression primary> <period> <field name>
-
-> fieldReference :: TestItem
-> fieldReference = Group "field reference"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("f(something).a"
->       ,BinOp (App [Name Nothing "f"] [Iden [Name Nothing "something"]])
->        [Name Nothing "."]
->        (Iden [Name Nothing "a"]))
->     ]
-
-TODO: try all possible value expression syntax variations followed by
-field reference
-
-== 6.16 <subtype treatment>
-
-Function
-Modify the declared type of an expression.
-
-<subtype treatment> ::=
-  TREAT <left paren> <subtype operand> AS <target subtype> <right paren>
-
-<subtype operand> ::= <value expression>
-
-<target subtype> ::= <path-resolved user-defined type name> | <reference type>
-
-todo: subtype treatment
-
-== 6.17 <method invocation>
-
-Function
-Reference an SQL-invoked method of a user-defined type value.
-
-<method invocation> ::= <direct invocation> | <generalized invocation>
-
-<direct invocation> ::=
-  <value expression primary> <period> <method name> [ <SQL argument list> ]
-
-<generalized invocation> ::=
-  <left paren> <value expression primary> AS <data type> <right paren>
-      <period> <method name> [ <SQL argument list> ]
-
-<method selection> ::= <routine invocation>
-
-<constructor method selection> ::= <routine invocation>
-
-todo: method invocation
-
-== 6.18 <static method invocation>
-
-Function
-Invoke a static method.
-
-<static method invocation> ::=
-  <path-resolved user-defined type name> <double colon> <method name>
-      [ <SQL argument list> ]
-
-<static method selection> ::= <routine invocation>
-
-todo: static method invocation
-
-== 6.19 <new specification>
-
-Function
-Invoke a method on a newly-constructed value of a structured type.
-
-<new specification> ::=
-  NEW <path-resolved user-defined type name> <SQL argument list>
-
-<new invocation> ::= <method invocation> | <routine invocation>
-
-todo: new specification
-
-== 6.20 <attribute or method reference>
-
-Function
-Return a value acquired by accessing a column of the row identified by
-a value of a reference type or by invoking an SQL-invoked method.
-
-<attribute or method reference> ::=
-  <value expression primary> <dereference operator> <qualified identifier>
-      [ <SQL argument list> ]
-
-<dereference operator> ::= <right arrow>
-
-todo: attribute of method reference
-
-== 6.21 <dereference operation>
-
-Function
-Access a column of the row identified by a value of a reference type.
-
-<dereference operation> ::=
-  <reference value expression> <dereference operator> <attribute name>
-
-todo: deference operation
-
-== 6.22 <method reference>
-
-Function
-Return a value acquired from invoking an SQL-invoked routine that is a method.
-
-<method reference> ::=
-  <value expression primary> <dereference operator> <method name> <SQL argument list>
-
-todo: method reference
-
-== 6.23 <reference resolution>
-
-Function
-Obtain the value referenced by a reference value.
-
-<reference resolution> ::=
-  DEREF <left paren> <reference value expression> <right paren>
-
-todo: reference resolution
-
-== 6.24 <array element reference>
-
-Function
-Return an element of an array.
-
-<array element reference> ::=
-  <array value expression>
-      <left bracket or trigraph> <numeric value expression> <right bracket or trigraph>
-
-> arrayElementReference :: TestItem
-> arrayElementReference = Group "array element reference"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("something[3]"
->      ,Array (Iden [Name Nothing "something"]) [NumLit "3"])
->     ,("(something(a))[x]"
->       ,Array (Parens (App [Name Nothing "something"] [Iden [Name Nothing "a"]]))
->         [Iden [Name Nothing "x"]])
->     ,("(something(a))[x][y] "
->       ,Array (
->         Array (Parens (App [Name Nothing "something"] [Iden [Name Nothing "a"]]))
->         [Iden [Name Nothing "x"]])
->         [Iden [Name Nothing "y"]])
->     ]
-
-== 6.25 <multiset element reference>
-
-Function
-Return the sole element of a multiset of one element.
-
-<multiset element reference> ::=
-  ELEMENT <left paren> <multiset value expression> <right paren>
-
-> multisetElementReference :: TestItem
-> multisetElementReference = Group "multisetElementReference"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("element(something)"
->      ,App [Name Nothing "element"] [Iden [Name Nothing "something"]])
->     ]
-
-== 6.26 <value expression>
-
-Function
-Specify a value.
-
-<value expression> ::=
-    <common value expression>
-  | <boolean value expression>
-  | <row value expression>
-
-<common value expression> ::=
-       <numeric value expression>
-     | <string value expression>
-     | <datetime value expression>
-     | <interval value expression>
-     | <user-defined type value expression>
-     | <reference value expression>
-     | <collection value expression>
-
-<user-defined type value expression> ::= <value expression primary>
-
-<reference value expression> ::= <value expression primary>
-
-<collection value expression> ::=
-    <array value expression>
-  | <multiset value expression>
-
-== 6.27 <numeric value expression>
-
-Function
-Specify a numeric value.
-
-<numeric value expression> ::=
-    <term>
-  | <numeric value expression> <plus sign> <term>
-  | <numeric value expression> <minus sign> <term>
-
-<term> ::= <factor> | <term> <asterisk> <factor> | <term> <solidus> <factor>
-
-<factor> ::= [ <sign> ] <numeric primary>
-
-<numeric primary> ::= <value expression primary> | <numeric value function>
-
-> numericScalarExpression :: TestItem
-> numericScalarExpression = Group "numeric value expression"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("a + b", binOp "+")
->     ,("a - b", binOp "-")
->     ,("a * b", binOp "*")
->     ,("a / b", binOp "/")
->     ,("+a", prefOp "+")
->     ,("-a", prefOp "-")
->     ]
->   where
->     binOp o = BinOp (Iden [Name Nothing "a"]) [Name Nothing o] (Iden [Name Nothing "b"])
->     prefOp o = PrefixOp [Name Nothing o] (Iden [Name Nothing "a"])
-
-TODO: precedence and associativity tests (need to review all operators
-for what precendence and associativity tests to write)
-
-== 6.28 <numeric value function>
-
-Function
-Specify a function yielding a value of type numeric.
-
-<numeric value function> ::=
-    <position expression>
-  | <regex occurrences function>
-  | <regex position expression>
-  | <extract expression>
-  | <length expression>
-  | <cardinality expression>
-  | <max cardinality expression>
-  | <absolute value expression>
-  | <modulus expression>
-  | <natural logarithm>
-  | <exponential function>
-  | <power function>
-  | <square root>
-  | <floor function>
-  | <ceiling function>
-  | <width bucket function>
-
-
-> numericValueFunction :: TestItem
-> numericValueFunction = Group "numeric value function"
->     [-- todo: numeric value function
->     ]
-
-<position expression> ::=
-    <character position expression>
-  | <binary position expression>
-
-<regex occurrences function> ::=
-  OCCURRENCES_REGEX <left paren>
-      <XQuery pattern> [ FLAG <XQuery option flag> ]
-      IN <regex subject string>
-      [ FROM <start position> ]
-      [ USING <char length units> ]
-      <right paren>
-
-<XQuery pattern> ::= <character value expression>
-
-<XQuery option flag> ::= <character value expression>
-
-<regex subject string> ::= <character value expression>
-
-<regex position expression> ::=
-  POSITION_REGEX <left paren>
-      [ <regex position start or after> ]
-      <XQuery pattern> [ FLAG <XQuery option flag> ]
-      IN <regex subject string>
-      [ FROM <start position> ]
-      [ USING <char length units> ]
-      [ OCCURRENCE <regex occurrence> ]
-      [ GROUP <regex capture group> ]
-      <right paren>
-
-<regex position start or after> ::= START | AFTER
-
-<regex occurrence> ::= <numeric value expression>
-
-<regex capture group> ::= <numeric value expression>
-
-<character position expression> ::=
-  POSITION <left paren> <character value expression 1> IN <character value expression 2>
-      [ USING <char length units> ] <right paren>
-
-<character value expression 1> ::= <character value expression>
-
-<character value expression 2> ::= <character value expression>
-
-<binary position expression> ::=
-  POSITION <left paren> <binary value expression> IN <binary value expression> <right paren>
-
-<length expression> ::= <char length expression> | <octet length expression>
-
-<char length expression> ::=
-  { CHAR_LENGTH | CHARACTER_LENGTH } <left paren> <character value expression>
-      [ USING <char length units> ] <right paren>
-
-<octet length expression> ::=
-  OCTET_LENGTH <left paren> <string value expression> <right paren>
-
-<extract expression> ::=
-  EXTRACT <left paren> <extract field> FROM <extract source> <right paren>
-
-<extract field> ::= <primary datetime field> | <time zone field>
-
-<time zone field> ::= TIMEZONE_HOUR | TIMEZONE_MINUTE
-
-<extract source> ::= <datetime value expression> | <interval value expression>
-
-<cardinality expression> ::=
-  CARDINALITY <left paren> <collection value expression> <right paren>
-
-<max cardinality expression> ::=
-  ARRAY_MAX_CARDINALITY <left paren> <array value expression> <right paren>
-
-<absolute value expression> ::=
-  ABS <left paren> <numeric value expression> <right paren>
-
-<modulus expression> ::=
-  MOD <left paren> <numeric value expression dividend> <comma>
-      <numeric value expression divisor> <right paren>
-
-<numeric value expression dividend> ::= <numeric value expression>
-
-<numeric value expression divisor> ::= <numeric value expression>
-
-<natural logarithm> ::=
-  LN <left paren> <numeric value expression> <right paren>
-
-<exponential function> ::=
-  EXP <left paren> <numeric value expression> <right paren>
-
-<power function> ::=
-  POWER <left paren> <numeric value expression base> <comma>
-      <numeric value expression exponent> <right paren>
-
-<numeric value expression base> ::= <numeric value expression>
-
-<numeric value expression exponent> ::= <numeric value expression>
-
-<square root> ::= SQRT <left paren> <numeric value expression> <right paren>
-
-<floor function> ::=
-  FLOOR <left paren> <numeric value expression> <right paren>
-
-<ceiling function> ::=
-  { CEIL | CEILING } <left paren> <numeric value expression> <right paren>
-
-<width bucket function> ::=
-  WIDTH_BUCKET <left paren> <width bucket operand> <comma> <width bucket bound 1> <comma>
-      <width bucket bound 2> <comma> <width bucket count> <right paren>
-
-<width bucket operand> ::= <numeric value expression>
-
-<width bucket bound 1> ::= <numeric value expression>
-
-<width bucket bound 2> ::= <numeric value expression>
-
-<width bucket count> ::= <numeric value expression>
-
-== 6.29 <string value expression>
-
-Function
-Specify a character string value or a binary string value.
-
-<string value expression> ::=
-    <character value expression>
-  | <binary value expression>
-
-<character value expression> ::= <concatenation> | <character factor>
-
-<concatenation> ::=
-  <character value expression> <concatenation operator> <character factor>
-
-<character factor> ::= <character primary> [ <collate clause> ]
-
-<character primary> ::= <value expression primary> | <string value function>
-
-<binary value expression> ::= <binary concatenation> | <binary factor>
-
-<binary factor> ::= <binary primary>
-
-<binary primary> ::= <value expression primary> | <string value function>
-
-<binary concatenation> ::=
-  <binary value expression> <concatenation operator> <binary factor>
-
-> stringScalarExpression :: TestItem
-> stringScalarExpression = Group "string value expression"
->     [-- todo: string value expression
->     ]
-
-== 6.30 <string value function>
-
-Function
-Specify a function yielding a value of type character string or binary string.
-
-<string value function> ::=
-    <character value function>
-  | <binary value function>
-
-<character value function> ::=
-    <character substring function>
-  | <regular expression substring function>
-  | <regex substring function>
-  | <fold>
-  | <transcoding>
-  | <character transliteration>
-  | <regex transliteration>
-  | <trim function>
-  | <character overlay function>
-  | <normalize function>
-  | <specific type method>
-
-> stringValueFunction :: TestItem
-> stringValueFunction = Group "string value function"
->     [-- todo: string value function
->     ]
-
-<character substring function> ::=
-  SUBSTRING <left paren> <character value expression> FROM <start position>
-      [ FOR <string length> ] [ USING <char length units> ] <right paren>
-
-<regular expression substring function> ::=
-  SUBSTRING <left paren> <character value expression> SIMILAR <character value expression>
-      ESCAPE <escape character> <right paren>
-
-<regex substring function> ::=
-  SUBSTRING_REGEX <left paren>
-      <XQuery pattern> [ FLAG <XQuery option flag> ]
-      IN <regex subject string>
-      [ FROM <start position> ]
-      [ USING <char length units> ]
-      [ OCCURRENCE <regex occurrence> ]
-      [ GROUP <regex capture group> ]
-      <right paren>
-
-<fold> ::=
-  { UPPER | LOWER } <left paren> <character value expression> <right paren>
-
-<transcoding> ::=
-  CONVERT <left paren> <character value expression>
-      USING <transcoding name> <right paren>
-
-<character transliteration> ::=
-  TRANSLATE <left paren> <character value expression>
-      USING <transliteration name> <right paren>
-
-<regex transliteration> ::=
-  TRANSLATE_REGEX <left paren>
-      <XQuery pattern> [ FLAG <XQuery option flag> ]
-      IN <regex subject string>
-      [ WITH <XQuery replacement string> ]
-      [ FROM <start position> ]
-      [ USING <char length units> ]
-      [ OCCURRENCE <regex transliteration occurrence> ]
-      <right paren>
-
-<XQuery replacement string> ::= <character value expression>
-
-<regex transliteration occurrence> ::= <regex occurrence> | ALL
-
-<trim function> ::= TRIM <left paren> <trim operands> <right paren>
-
-<trim operands> ::=
-  [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source>
-
-<trim source> ::= <character value expression>
-
-<trim specification> ::= LEADING | TRAILING | BOTH
-
-<trim character> ::= <character value expression>
-
-<character overlay function> ::=
-  OVERLAY <left paren> <character value expression> PLACING <character value expression>
-      FROM <start position> [ FOR <string length> ]
-      [ USING <char length units> ] <right paren>
-
-<normalize function> ::=
-  NORMALIZE <left paren> <character value expression>
-      [ <comma> <normal form> [ <comma> <normalize function result length> ] ] <right paren>
-
-<normal form> ::= NFC | NFD | NFKC | NFKD
-
-<normalize function result length> ::=
-    <character length>
-  | <character large object length>
-
-<specific type method> ::=
-  <user-defined type value expression> <period> SPECIFICTYPE
-      [ <left paren> <right paren> ]
-
-<binary value function> ::=
-       <binary substring function>
-     | <binary trim function>
-     | <binary overlay function>
-
-<binary substring function> ::=
-  SUBSTRING <left paren> <binary value expression> FROM <start position>
-      [ FOR <string length> ] <right paren>
-
-<binary trim function> ::=
-  TRIM <left paren> <binary trim operands> <right paren>
-
-<binary trim operands> ::=
-  [ [ <trim specification> ] [ <trim octet> ] FROM ] <binary trim source>
-
-<binary trim source> ::= <binary value expression>
-
-<trim octet> ::= <binary value expression>
-
-<binary overlay function> ::=
-  OVERLAY <left paren> <binary value expression> PLACING <binary value expression>
-      FROM <start position> [ FOR <string length> ] <right paren>
-
-<start position> ::= <numeric value expression>
-
-<string length> ::= <numeric value expression>
-
-== 6.31 <datetime value expression>
-
-Function
-Specify a datetime value.
-
-<datetime value expression> ::=
-    <datetime term>
-  | <interval value expression> <plus sign> <datetime term>
-  | <datetime value expression> <plus sign> <interval term>
-  | <datetime value expression> <minus sign> <interval term>
-
-> datetimeScalarExpression :: TestItem
-> datetimeScalarExpression = Group "datetime value expression"
->     [-- todo: datetime value expression
->      datetimeValueFunction 
->     ]
-
-<datetime term> ::= <datetime factor>
-
-<datetime factor> ::= <datetime primary> [ <time zone> ]
-
-<datetime primary> ::= <value expression primary> | <datetime value function>
-
-<time zone> ::= AT <time zone specifier>
-
-<time zone specifier> ::= LOCAL | TIME ZONE <interval primary>
-
-== 6.32 <datetime value function>
-
-Function
-Specify a function yielding a value of type datetime.
-
-<datetime value function> ::=
-    <current date value function>
-  | <current time value function>
-  | <current timestamp value function>
-  | <current local time value function>
-  | <current local timestamp value function>
-
-> datetimeValueFunction :: TestItem
-> datetimeValueFunction = Group "datetime value function"
->     [-- todo: datetime value function
->     ]
-
-<current date value function> ::= CURRENT_DATE
-
-<current time value function> ::=
-  CURRENT_TIME [ <left paren> <time precision> <right paren> ]
-
-<current local time value function> ::=
-  LOCALTIME [ <left paren> <time precision> <right paren> ]
-
-<current timestamp value function> ::=
-  CURRENT_TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
-
-<current local timestamp value function> ::=
-  LOCALTIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
-
-== 6.33 <interval value expression>
-
-Function
-Specify an interval value.
-
-<interval value expression> ::=
-    <interval term>
-  | <interval value expression 1> <plus sign> <interval term 1>
-  | <interval value expression 1> <minus sign> <interval term 1>
-  | <left paren> <datetime value expression> <minus sign> <datetime term> <right paren>
-      <interval qualifier>
-
-> intervalScalarExpression :: TestItem
-> intervalScalarExpression = Group "interval value expression"
->     [-- todo: interval value expression
->     ]
-
-
-<interval term> ::=
-    <interval factor>
-  | <interval term 2> <asterisk> <factor>
-  | <interval term 2> <solidus> <factor>
-  | <term> <asterisk> <interval factor>
-
-<interval factor> ::= [ <sign> ] <interval primary>
-
-<interval primary> ::=
-    <value expression primary> [ <interval qualifier> ]
-  | <interval value function>
-
-<interval value expression 1> ::= <interval value expression>
-
-<interval term 1> ::= <interval term>
-
-<interval term 2> ::= <interval term>
-
-== 6.34 <interval value function>
-
-Function
-Specify a function yielding a value of type interval.
-
-<interval value function> ::= <interval absolute value function>
-
-<interval absolute value function> ::=
-  ABS <left paren> <interval value expression> <right paren>
-
-> intervalValueFunction :: TestItem
-> intervalValueFunction = Group "interval value function"
->     [-- todo: interval value function
->     ]
-
-
-== 6.35 <boolean value expression>
-
-Function
-Specify a boolean value.
-
-<boolean value expression> ::=
-    <boolean term>
-  | <boolean value expression> OR <boolean term>
-
-<boolean term> ::= <boolean factor> | <boolean term> AND <boolean factor>
-
-<boolean factor> ::= [ NOT ] <boolean test>
-
-<boolean test> ::= <boolean primary> [ IS [ NOT ] <truth value> ]
-
-<truth value> ::= TRUE | FALSE | UNKNOWN
-
-<boolean primary> ::= <predicate> | <boolean predicand>
-
-<boolean predicand> ::=
-    <parenthesized boolean value expression>
-  | <nonparenthesized value expression primary>
-
-<parenthesized boolean value expression> ::=
-  <left paren> <boolean value expression> <right paren>
-
-
-> booleanScalarExpression :: TestItem
-> booleanScalarExpression = Group "booleab value expression"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("a or b", BinOp a [Name Nothing "or"] b)
->     ,("a and b", BinOp a [Name Nothing "and"] b)
->     ,("not a", PrefixOp [Name Nothing "not"] a)
->     ,("a is true", postfixOp "is true")
->     ,("a is false", postfixOp "is false")
->     ,("a is unknown", postfixOp "is unknown")
->     ,("a is not true", postfixOp "is not true")
->     ,("a is not false", postfixOp "is not false")
->     ,("a is not unknown", postfixOp "is not unknown")
->     ,("(a or b)", Parens $ BinOp a [Name Nothing "or"] b)
->     ]
->   where
->     a = Iden [Name Nothing "a"]
->     b = Iden [Name Nothing "b"]
->     postfixOp nm = PostfixOp [Name Nothing nm] a
-
-TODO: review if more tests are needed. Should at least have
-precendence tests for mixed and, or and not without parens.
-
-== 6.36 <array value expression>
-
-Function
-Specify an array value.
-
-<array value expression> ::= <array concatenation> | <array primary>
-
-<array concatenation> ::=
-  <array value expression 1> <concatenation operator> <array primary>
-
-<array value expression 1> ::= <array value expression>
-
-<array primary> ::= <array value function> | <value expression primary>
-
-> arrayScalarExpression :: TestItem
-> arrayScalarExpression = Group "array value expression"
->     [-- todo: array value expression
->     ]
-
-== 6.37 <array value function>
-
-Function
-Specify a function yielding a value of an array type.
-
-<array value function> ::= <trim array function>
-
-<trim array function> ::=
-  TRIM_ARRAY <left paren> <array value expression> <comma> <numeric value expression>
-    <right paren>
-
-> arrayValueFunction :: TestItem
-> arrayValueFunction = Group "array value function"
->     [-- todo: array value function
->     ]
-
-== 6.38 <array value constructor>
-
-Function
-Specify construction of an array.
-
-<array value constructor> ::=
-    <array value constructor by enumeration>
-  | <array value constructor by query>
-
-<array value constructor by enumeration> ::=
-  ARRAY <left bracket or trigraph> <array element list> <right bracket or trigraph>
-
-<array element list> ::= <array element> [ { <comma> <array element> }... ]
-
-<array element> ::= <value expression>
-
-<array value constructor by query> ::= ARRAY <table subquery>
-
-> arrayValueConstructor :: TestItem
-> arrayValueConstructor = Group "array value constructor"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("array[1,2,3]"
->      ,Array (Iden [Name Nothing "array"])
->       [NumLit "1", NumLit "2", NumLit "3"])
->     ,("array[a,b,c]"
->      ,Array (Iden [Name Nothing "array"])
->       [Iden [Name Nothing "a"], Iden [Name Nothing "b"], Iden [Name Nothing "c"]])
->     ,("array(select * from t)"
->       ,ArrayCtor (makeSelect
->                   {qeSelectList = [(Star,Nothing)]
->                   ,qeFrom = [TRSimple [Name Nothing "t"]]}))
->     ,("array(select * from t order by a)"
->       ,ArrayCtor (makeSelect
->                   {qeSelectList = [(Star,Nothing)]
->                   ,qeFrom = [TRSimple [Name Nothing "t"]]
->                   ,qeOrderBy = [SortSpec (Iden [Name Nothing "a"])
->                                     DirDefault NullsOrderDefault]}))
->     ]
-
-
-== 6.39 <multiset value expression>
-
-Function
-Specify a multiset value.
-
-<multiset value expression> ::=
-    <multiset term>
-  | <multiset value expression> MULTISET UNION [ ALL | DISTINCT ] <multiset term>
-  | <multiset value expression> MULTISET EXCEPT [ ALL | DISTINCT ] <multiset term>
-
-<multiset term> ::=
-    <multiset primary>
-  | <multiset term> MULTISET INTERSECT [ ALL | DISTINCT ] <multiset primary>
-
-<multiset primary> ::= <multiset value function> | <value expression primary>
-
-> multisetScalarExpression :: TestItem
-> multisetScalarExpression = Group "multiset value expression"
->    $ map (uncurry (TestScalarExpr ansi2011))
->    [("a multiset union b"
->     ,MultisetBinOp (Iden [Name Nothing "a"]) Union SQDefault (Iden [Name Nothing "b"]))
->    ,("a multiset union all b"
->     ,MultisetBinOp (Iden [Name Nothing "a"]) Union All (Iden [Name Nothing "b"]))
->    ,("a multiset union distinct b"
->     ,MultisetBinOp (Iden [Name Nothing "a"]) Union Distinct (Iden [Name Nothing "b"]))
->    ,("a multiset except b"
->     ,MultisetBinOp (Iden [Name Nothing "a"]) Except SQDefault (Iden [Name Nothing "b"]))
->    ,("a multiset intersect b"
->     ,MultisetBinOp (Iden [Name Nothing "a"]) Intersect SQDefault (Iden [Name Nothing "b"]))
->    ]
-
-TODO: check precedence and associativity
-
-== 6.40 <multiset value function>
-
-Function
-Specify a function yielding a value of a multiset type.
-
-<multiset value function> ::= <multiset set function>
-
-<multiset set function> ::=
-  SET <left paren> <multiset value expression> <right paren>
-
-TODO: set is now a reserved keyword. Fix the set parsing with a
-special case term.
-
-> multisetValueFunction :: TestItem
-> multisetValueFunction = Group "multiset value function"
->    $ map (uncurry (TestScalarExpr ansi2011))
->    [("set(a)", App [Name Nothing "set"] [Iden [Name Nothing "a"]])
->    ]
-
-== 6.41 <multiset value constructor>
-
-Function
-Specify construction of a multiset.
-
-<multiset value constructor> ::=
-    <multiset value constructor by enumeration>
-  | <multiset value constructor by query>
-  | <table value constructor by query>
-
-<multiset value constructor by enumeration> ::=
-  MULTISET <left bracket or trigraph> <multiset element list> <right bracket or trigraph>
-
-<multiset element list> ::=
-  <multiset element> [ { <comma> <multiset element> }... ]
-
-<multiset element> ::= <value expression>
-
-<multiset value constructor by query> ::= MULTISET <table subquery>
-
-<table value constructor by query> ::= TABLE <table subquery>
-
-> multisetValueConstructor :: TestItem
-> multisetValueConstructor = Group "multiset value constructor"
->    $ map (uncurry (TestScalarExpr ansi2011))
->    [("multiset[a,b,c]", MultisetCtor[Iden [Name Nothing "a"]
->                                     ,Iden [Name Nothing "b"], Iden [Name Nothing "c"]])
->    ,("multiset(select * from t)", MultisetQueryCtor qe)
->    ,("table(select * from t)", MultisetQueryCtor qe)
->    ]
->   where
->     qe = makeSelect {qeSelectList = [(Star,Nothing)]
->                     ,qeFrom = [TRSimple [Name Nothing "t"]]}
-
-
-= 7 Query expressions
-
-> queryExpressions :: TestItem
-> queryExpressions = Group "query expressions"
->     [rowValueConstructor
->     ,tableValueConstructor
->     ,fromClause
->     ,tableReference
->     ,joinedTable
->     ,whereClause
->     ,groupByClause
->     ,havingClause
->     ,windowClause
->     ,querySpecification
->     ,withQueryExpression
->     ,setOpQueryExpression
->     ,explicitTableQueryExpression
->     ,orderOffsetFetchQueryExpression
->     ,searchOrCycleClause
->     ]
-
-
-== 7.1 <row value constructor>
-
-Function
-Specify a value or list of values to be constructed into a row.
-
-<row value constructor> ::=
-    <common value expression>
-  | <boolean value expression>
-  | <explicit row value constructor>
-
-<explicit row value constructor> ::=
-    <left paren> <row value constructor element> <comma>
-        <row value constructor element list> <right paren>
-  | ROW <left paren> <row value constructor element list> <right paren>
-  | <row subquery>
-
-<row value constructor element list> ::=
-  <row value constructor element> [ { <comma> <row value constructor element> }... ]
-
-<row value constructor element> ::= <value expression>
-
-<contextually typed row value constructor> ::=
-    <common value expression>
-  | <boolean value expression>
-  | <contextually typed value specification>
-  | <left paren> <contextually typed value specification> <right paren>
-  | <left paren> <contextually typed row value constructor element> <comma>
-      <contextually typed row value constructor element list> <right paren>
-  | ROW <left paren> <contextually typed row value constructor element list> <right paren>
-
-<contextually typed row value constructor element list> ::=
-  <contextually typed row value constructor element>
-      [ { <comma> <contextually typed row value constructor element> }... ]
-
-<contextually typed row value constructor element> ::=
-    <value expression>
-  | <contextually typed value specification>
-
-<row value constructor predicand> ::=
-    <common value expression>
-  | <boolean predicand>
-  | <explicit row value constructor>
-
-> rowValueConstructor :: TestItem
-> rowValueConstructor = Group "row value constructor"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("(a,b)"
->      ,SpecialOp [Name Nothing "rowctor"] [Iden [Name Nothing "a"], Iden [Name Nothing "b"]])
->     ,("row(1)",App [Name Nothing "row"] [NumLit "1"])
->     ,("row(1,2)",App [Name Nothing "row"] [NumLit "1",NumLit "2"])
->     ]
-
-== 7.2 <row value expression>
-
-Function
-Specify a row value.
-
-<row value expression> ::=
-    <row value special case>
-  | <explicit row value constructor>
-
-<table row value expression> ::=
-    <row value special case>
-  | <row value constructor>
-
-<contextually typed row value expression> ::=
-    <row value special case>
-  | <contextually typed row value constructor>
-
-<row value predicand> ::=
-    <row value special case>
-  | <row value constructor predicand>
-
-<row value special case> ::= <nonparenthesized value expression primary>
-
-There is nothing new here.
-
-== 7.3 <table value constructor>
-
-Function
-Specify a set of <row value expression>s to be constructed into a table.
-
-<table value constructor> ::= VALUES <row value expression list>
-
-<row value expression list> ::=
-  <table row value expression> [ { <comma> <table row value expression> }... ]
-
-<contextually typed table value constructor> ::=
-  VALUES <contextually typed row value expression list>
-
-<contextually typed row value expression list> ::=
-  <contextually typed row value expression>
-      [ { <comma> <contextually typed row value expression> }... ]
-
-> tableValueConstructor :: TestItem
-> tableValueConstructor = Group "table value constructor"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("values (1,2), (a+b,(select count(*) from t));"
->      ,Values [[NumLit "1", NumLit "2"]
->              ,[BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"]
->                      (Iden [Name Nothing "b"])
->               ,SubQueryExpr SqSq
->                (makeSelect
->                 {qeSelectList = [(App [Name Nothing "count"] [Star],Nothing)]
->                 ,qeFrom = [TRSimple [Name Nothing "t"]]})]])
->     ]
-
-== 7.4 <table expression>
-
-Function
-Specify a table or a grouped table.
-
-<table expression> ::=
-  <from clause>
-      [ <where clause> ]
-      [ <group by clause> ]
-      [ <having clause> ]
-      [ <window clause> ]
-
-== 7.5 <from clause>
-
-Function
-Specify a table derived from one or more tables.
-
-<from clause> ::= FROM <table reference list>
-
-<table reference list> ::=
-  <table reference> [ { <comma> <table reference> }... ]
-
-> fromClause :: TestItem
-> fromClause = Group "fromClause"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("select * from tbl1,tbl2"
->      ,makeSelect
->       {qeSelectList = [(Star, Nothing)]
->       ,qeFrom = [TRSimple [Name Nothing "tbl1"], TRSimple [Name Nothing "tbl2"]]
->       })]
-
-
-== 7.6 <table reference>
-
-Function
-Reference a table.
-
-> tableReference :: TestItem
-> tableReference = Group "table reference"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("select * from t", sel)
-
-<table reference> ::= <table factor> | <joined table>
-
-<table factor> ::= <table primary> [ <sample clause> ]
-
-<sample clause> ::=
-  TABLESAMPLE <sample method> <left paren> <sample percentage> <right paren>
-      [ <repeatable clause> ]
-
-<sample method> ::= BERNOULLI | SYSTEM
-
-<repeatable clause> ::= REPEATABLE <left paren> <repeat argument> <right paren>
-
-<sample percentage> ::= <numeric value expression>
-
-<repeat argument> ::= <numeric value expression>
-
-<table primary> ::=
-    <table or query name> [ <query system time period specification> ]
-          [ [ AS ] <correlation name>
-            [ <left paren> <derived column list> <right paren> ] ]
-  | <derived table> [ AS ] <correlation name>
-        [ <left paren> <derived column list> <right paren> ]
-  | <lateral derived table> [ AS ] <correlation name>
-        [ <left paren> <derived column list> <right paren> ]
-  | <collection derived table> [ AS ] <correlation name>
-        [ <left paren> <derived column list> <right paren> ]
-  | <table function derived table> [ AS ] <correlation name>
-        [ <left paren> <derived column list> <right paren> ]
-  | <only spec> [ [ AS ] <correlation name>
-        [ <left paren> <derived column list> <right paren> ] ]
-  | <data change delta table> [ [ AS ] <correlation name>
-        [ <left paren> <derived column list> <right paren> ] ]
-  | <parenthesized joined table>
-
-<query system time period specification> ::=
-    FOR SYSTEM_TIME AS OF <point in time 1>
-  | FOR SYSTEM_TIME BETWEEN [ ASYMMETRIC | SYMMETRIC ]
-      <point in time 1> AND <point in time 2>
-  | FOR SYSTEM_TIME FROM <point in time 1> TO <point in time 2>
-
-TODO: query system time period spec
-
-<point in time 1> ::= <point in time>
-
-<point in time 2> ::= <point in time>
-
-<point in time> ::= <datetime value expression>
-
-<only spec> ::= ONLY <left paren> <table or query name> <right paren>
-
-TODO: only
-
-<lateral derived table> ::= LATERAL <table subquery>
-
-<collection derived table> ::=
-  UNNEST <left paren> <collection value expression>
-      [ { <comma> <collection value expression> }... ] <right paren>
-      [ WITH ORDINALITY ]
-
-<table function derived table> ::=
-  TABLE <left paren> <collection value expression> <right paren>
-
-<derived table> ::= <table subquery>
-
-<table or query name> ::= <table name> | <transition table name> | <query name>
-
-<derived column list> ::= <column name list>
-
-<column name list> ::= <column name> [ { <comma> <column name> }... ]
-
-<data change delta table> ::=
-  <result option> TABLE <left paren> <data change statement> <right paren>
-
-<data change statement> ::=
-    <delete statement: searched>
-  | <insert statement>
-  | <merge statement>
-  | <update statement: searched>
-
-<result option> ::= FINAL | NEW | OLD
-
-<parenthesized joined table> ::=
-    <left paren> <parenthesized joined table> <right paren>
-  | <left paren> <joined table> <right paren>
-
-
->     -- table or query name
->     ,("select * from t u", a sel)
->     ,("select * from t as u", a sel)
->     ,("select * from t u(a,b)", sel1 )
->     ,("select * from t as u(a,b)", sel1)
->     -- derived table TODO: realistic example
->     ,("select * from (select * from t) u"
->      ,a $ sel {qeFrom = [TRQueryExpr sel]})
->     -- lateral TODO: realistic example
->     ,("select * from lateral t"
->      ,af TRLateral sel)
->     -- TODO: bug, lateral should bind more tightly than the alias
->     --,("select * from lateral t u"
->     -- ,a $ af sel TRLateral)
->     -- collection TODO: realistic example
->      -- TODO: make it work
->     --,("select * from unnest(a)", undefined)
->     --,("select * from unnest(a,b)", undefined)
->     --,("select * from unnest(a,b) with ordinality", undefined)
->     --,("select * from unnest(a,b) with ordinality u", undefined)
->     --,("select * from unnest(a,b) with ordinality as u", undefined)
->     -- table fn TODO: realistic example
->      -- TODO: make it work
->     --,("select * from table(a)", undefined)
->     -- parens
->     ,("select * from (a join b)", jsel)
->     ,("select * from (a join b) u", a jsel)
->     ,("select * from ((a join b)) u", a $ af TRParens jsel)
->     ,("select * from ((a join b) u) u", a $ af TRParens $ a jsel)
->     ]
->   where
->     sel = makeSelect
->           {qeSelectList = [(Star, Nothing)]
->           ,qeFrom = [TRSimple [Name Nothing "t"]]}
->     af f s = s {qeFrom = map f (qeFrom s)}
->     a s = af (\x -> TRAlias x $ Alias (Name Nothing "u") Nothing) s
->     sel1 = makeSelect
->           {qeSelectList = [(Star, Nothing)]
->           ,qeFrom = [TRAlias (TRSimple [Name Nothing "t"])
->                      $ Alias (Name Nothing "u") $ Just [Name Nothing "a", Name Nothing "b"]]}
->     jsel = sel {qeFrom =
->                 [TRParens $ TRJoin (TRSimple [Name Nothing "a"])
->                                    False
->                                    JInner
->                                    (TRSimple [Name Nothing "b"])
->                                    Nothing]}
-
-== 7.7 <joined table>
-
-Function
-Specify a table derived from a Cartesian product, inner join, or outer join.
-
-<joined table> ::= <cross join> | <qualified join> | <natural join>
-
-<cross join> ::= <table reference> CROSS JOIN <table factor>
-
-<qualified join> ::=
-  { <table reference> | <partitioned join table> }
-      [ <join type> ] JOIN
-      { <table reference> | <partitioned join table> }
-      <join specification>
-
-<partitioned join table> ::=
-  <table factor> PARTITION BY
-      <partitioned join column reference list>
-
-<partitioned join column reference list> ::=
-  <left paren> <partitioned join column reference>
-      [ { <comma> <partitioned join column reference> }... ]
-      <right paren>
-
-<partitioned join column reference> ::= <column reference>
-
-<natural join> ::=
-  { <table reference> | <partitioned join table> }
-      NATURAL [ <join type> ] JOIN
-      { <table factor> | <partitioned join table> }
-
-<join specification> ::= <join condition> | <named columns join>
-
-<join condition> ::= ON <search condition>
-
-<named columns join> ::= USING <left paren> <join column list> <right paren>
-
-<join type> ::= INNER | <outer join type> [ OUTER ]
-
-<outer join type> ::= LEFT | RIGHT | FULL
-
-<join column list> ::= <column name list>
-
-> joinedTable :: TestItem
-> joinedTable = Group "joined table"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("select * from a cross join b"
->      ,sel $ TRJoin a False JCross b Nothing)
->     ,("select * from a join b on true"
->      ,sel $ TRJoin a False JInner b
->       (Just $ JoinOn $ Iden [Name Nothing "true"]))
->     ,("select * from a join b using (c)"
->      ,sel $ TRJoin a False JInner b
->       (Just $ JoinUsing [Name Nothing "c"]))
->     ,("select * from a inner join b on true"
->      ,sel $ TRJoin a False JInner b
->       (Just $ JoinOn $ Iden [Name Nothing "true"]))
->     ,("select * from a left join b on true"
->      ,sel $ TRJoin a False JLeft b
->       (Just $ JoinOn $ Iden [Name Nothing "true"]))
->     ,("select * from a left outer join b on true"
->      ,sel $ TRJoin a False JLeft b
->       (Just $ JoinOn $ Iden [Name Nothing "true"]))
->     ,("select * from a right join b on true"
->      ,sel $ TRJoin a False JRight b
->       (Just $ JoinOn $ Iden [Name Nothing "true"]))
->     ,("select * from a full join b on true"
->      ,sel $ TRJoin a False JFull b
->       (Just $ JoinOn $ Iden [Name Nothing "true"]))
->     ,("select * from a natural join b"
->      ,sel $ TRJoin a True JInner b Nothing)
->     ,("select * from a natural inner join b"
->      ,sel $ TRJoin a True JInner b Nothing)
->     ,("select * from a natural left join b"
->      ,sel $ TRJoin a True JLeft b Nothing)
->     ,("select * from a natural left outer join b"
->      ,sel $ TRJoin a True JLeft b Nothing)
->     ,("select * from a natural right join b"
->      ,sel $ TRJoin a True JRight b Nothing)
->     ,("select * from a natural full join b"
->      ,sel $ TRJoin a True JFull b Nothing)
->     ]
->   where
->     sel t = makeSelect
->             {qeSelectList = [(Star, Nothing)]
->             ,qeFrom = [t]}
->     a = TRSimple [Name Nothing "a"]
->     b = TRSimple [Name Nothing "b"]
-
-TODO: partitioned joins
-
-== 7.8 <where clause>
-
-Function
-
-Specify a table derived by the application of a <search condition> to
-the result of the preceding <from clause>.
-
-<where clause> ::= WHERE <search condition>
-
-> whereClause :: TestItem
-> whereClause = Group "where clause"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("select * from t where a = 5"
->     ,makeSelect
->      {qeSelectList = [(Star,Nothing)]
->      ,qeFrom = [TRSimple [Name Nothing "t"]]
->      ,qeWhere = Just $ BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "5")})]
-
-== 7.9 <group by clause>
-
-Function
-
-Specify a grouped table derived by the application of the <group by
-clause> to the result of the previously specified clause.
-
-<group by clause> ::= GROUP BY [ <set quantifier> ] <grouping element list>
-
-<grouping element list> ::=
-  <grouping element> [ { <comma> <grouping element> }... ]
-
-<grouping element> ::=
-    <ordinary grouping set>
-  | <rollup list>
-  | <cube list>
-  | <grouping sets specification>
-  | <empty grouping set>
-
-<ordinary grouping set> ::=
-    <grouping column reference>
-  | <left paren> <grouping column reference list> <right paren>
-
-<grouping column reference> ::= <column reference> [ <collate clause> ]
-
-<grouping column reference list> ::=
-  <grouping column reference> [ { <comma> <grouping column reference> }... ]
-
-<rollup list> ::=
-  ROLLUP <left paren> <ordinary grouping set list> <right paren>
-
-<ordinary grouping set list> ::=
-  <ordinary grouping set> [ { <comma> <ordinary grouping set> }... ]
-
-<cube list> ::= CUBE <left paren> <ordinary grouping set list> <right paren>
-
-<grouping sets specification> ::=
-  GROUPING SETS <left paren> <grouping set list> <right paren>
-
-<grouping set list> ::= <grouping set> [ { <comma> <grouping set> }... ]
-
-<grouping set> ::=
-    <ordinary grouping set>
-  | <rollup list>
-  | <cube list>
-  | <grouping sets specification>
-  | <empty grouping set>
-
-<empty grouping set> ::= <left paren> <right paren>
-
-
-> groupByClause :: TestItem
-> groupByClause = Group "group by clause"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("select a,sum(x) from t group by a"
->      ,qe [SimpleGroup $ Iden [Name Nothing "a"]])
->      ,("select a,sum(x) from t group by a collate c"
->      ,qe [SimpleGroup $ Collate (Iden [Name Nothing "a"]) [Name Nothing "c"]])
->     ,("select a,b,sum(x) from t group by a,b"
->      ,qex [SimpleGroup $ Iden [Name Nothing "a"]
->           ,SimpleGroup $ Iden [Name Nothing "b"]])
->     -- todo: group by set quantifier
->     --,("select a,sum(x) from t group by distinct a"
->     --,undefined)
->     --,("select a,sum(x) from t group by all a"
->     -- ,undefined)
->     ,("select a,b,sum(x) from t group by rollup(a,b)"
->      ,qex [Rollup [SimpleGroup $ Iden [Name Nothing "a"]
->                   ,SimpleGroup $ Iden [Name Nothing "b"]]])
->     ,("select a,b,sum(x) from t group by cube(a,b)"
->      ,qex [Cube [SimpleGroup $ Iden [Name Nothing "a"]
->                 ,SimpleGroup $ Iden [Name Nothing "b"]]])
->     ,("select a,b,sum(x) from t group by grouping sets((),(a,b))"
->      ,qex [GroupingSets [GroupingParens []
->                         ,GroupingParens [SimpleGroup $ Iden [Name Nothing "a"]
->                                         ,SimpleGroup $ Iden [Name Nothing "b"]]]])
->     ,("select sum(x) from t group by ()"
->      ,let x = qe [GroupingParens []]
->       in x {qeSelectList = tail $ qeSelectList x})
->     ]
->   where
->     qe g = makeSelect
->            {qeSelectList = [(Iden [Name Nothing "a"], Nothing)
->                            ,(App [Name Nothing "sum"] [Iden [Name Nothing "x"]], Nothing)]
->            ,qeFrom = [TRSimple [Name Nothing "t"]]
->            ,qeGroupBy = g}
->     qex g = let x = qe g
->             in x {qeSelectList = let [a,b] = qeSelectList x
->                                  in [a,(Iden [Name Nothing "b"],Nothing),b]}
-
-== 7.10 <having clause>
-
-Function
-
-Specify a grouped table derived by the elimination of groups that do
-not satisfy a <search condition>.
-
-<having clause> ::= HAVING <search condition>
-
-> havingClause :: TestItem
-> havingClause = Group "having clause"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("select a,sum(x) from t group by a having sum(x) > 1000"
->      ,makeSelect
->       {qeSelectList = [(Iden [Name Nothing "a"], Nothing)
->                       ,(App [Name Nothing "sum"] [Iden [Name Nothing "x"]], Nothing)]
->       ,qeFrom = [TRSimple [Name Nothing "t"]]
->       ,qeGroupBy = [SimpleGroup $ Iden [Name Nothing "a"]]
->       ,qeHaving = Just $ BinOp (App [Name Nothing "sum"] [Iden [Name Nothing "x"]])
->                                [Name Nothing ">"]
->                                (NumLit "1000")})
->     ]
-
-== 7.11 <window clause>
-
-Function
-Specify one or more window definitions.
-
-<window clause> ::= WINDOW <window definition list>
-
-<window definition list> ::=
-  <window definition> [ { <comma> <window definition> }... ]
-
-<window definition> ::= <new window name> AS <window specification>
-
-<new window name> ::= <window name>
-
-<window specification> ::=
-  <left paren> <window specification details> <right paren>
-
-<window specification details> ::=
-  [ <existing window name> ]
-      [ <window partition clause> ]
-      [ <window order clause> ]
-      [ <window frame clause> ]
-
-<existing window name> ::= <window name>
-
-<window partition clause> ::=
-  PARTITION BY <window partition column reference list>
-
-<window partition column reference list> ::=
-  <window partition column reference>
-      [ { <comma> <window partition column reference> }... ]
-
-<window partition column reference> ::= <column reference> [ <collate clause> ]
-
-<window order clause> ::= ORDER BY <sort specification list>
-
-<window frame clause> ::=
-  <window frame units> <window frame extent>
-      [ <window frame exclusion> ]
-
-<window frame units> ::= ROWS | RANGE | GROUPS
-
-<window frame extent> ::= <window frame start> | <window frame between>
-
-<window frame start> ::=
-    UNBOUNDED PRECEDING
-  | <window frame preceding>
-  | CURRENT ROW
-
-<window frame preceding> ::= <unsigned value specification> PRECEDING
-
-<window frame between> ::=
-  BETWEEN <window frame bound 1> AND <window frame bound 2>
-
-<window frame bound 1> ::= <window frame bound>
-
-<window frame bound 2> ::= <window frame bound>
-
-<window frame bound> ::=
-    <window frame start>
-  | UNBOUNDED FOLLOWING
-  | <window frame following>
-
-<window frame following> ::= <unsigned value specification> FOLLOWING
-
-<window frame exclusion> ::=
-    EXCLUDE CURRENT ROW
-  | EXCLUDE GROUP
-  | EXCLUDE TIES
-  | EXCLUDE NO OTHERS
-
-> windowClause :: TestItem
-> windowClause = Group "window clause"
->     [-- todo: window clause
->     ]
-
-== 7.12 <query specification>
-
-Function
-Specify a table derived from the result of a <table expression>.
-
-<query specification> ::=
-  SELECT [ <set quantifier> ] <select list> <table expression>
-
-<select list> ::=
-    <asterisk>
-  | <select sublist> [ { <comma> <select sublist> }... ]
-
-<select sublist> ::= <derived column> | <qualified asterisk>
-
-<qualified asterisk> ::=
-    <asterisked identifier chain> <period> <asterisk>
-  | <all fields reference>
-
-<asterisked identifier chain> ::=
-  <asterisked identifier> [ { <period> <asterisked identifier> }... ]
-
-<asterisked identifier> ::= <identifier>
-
-<derived column> ::= <value expression> [ <as clause> ]
-
-<as clause> ::= [ AS ] <column name>
-
-<all fields reference> ::=
-  <value expression primary> <period> <asterisk>
-      [ AS <left paren> <all fields column name list> <right paren> ]
-
-<all fields column name list> ::= <column name list>
-
-> querySpecification :: TestItem
-> querySpecification = Group "query specification"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("select a from t",qe)
->     ,("select all a from t",qe {qeSetQuantifier = All})
->     ,("select distinct a from t",qe {qeSetQuantifier = Distinct})
->     ,("select * from t", qe {qeSelectList = [(Star,Nothing)]})
->     ,("select a.* from t"
->      ,qe {qeSelectList = [(BinOp (Iden [Name Nothing "a"]) [Name Nothing "."] Star
->                            ,Nothing)]})
->     ,("select a b from t"
->      ,qe {qeSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "b")]})
->     ,("select a as b from t"
->      ,qe {qeSelectList = [(Iden [Name Nothing "a"], Just $ Name Nothing "b")]})
->     ,("select a,b from t"
->      ,qe {qeSelectList = [(Iden [Name Nothing "a"], Nothing)
->                          ,(Iden [Name Nothing "b"], Nothing)]})
->     -- todo: all field reference alias
->     --,("select * as (a,b) from t",undefined)
->     ]
->   where
->     qe = makeSelect
->          {qeSelectList = [(Iden [Name Nothing "a"], Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]
->          }
-
-== 7.13 <query expression>
-
-Function
-Specify a table.
-
-<query expression> ::=
-  [ <with clause> ] <query expression body>
-      [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
-
-<with clause> ::= WITH [ RECURSIVE ] <with list>
-
-<with list> ::= <with list element> [ { <comma> <with list element> }... ]
-
-<with list element> ::=
-  <query name> [ <left paren> <with column list> <right paren> ]
-      AS <table subquery> [ <search or cycle clause> ]
-
-<with column list> ::= <column name list>
-
-> withQueryExpression :: TestItem
-> withQueryExpression= Group "with query expression"
->     [-- todo: with query expression
->     ]
-
-<query expression body> ::=
-    <query term>
-  | <query expression body> UNION [ ALL | DISTINCT ]
-      [ <corresponding spec> ] <query term>
-  | <query expression body> EXCEPT [ ALL | DISTINCT ]
-      [ <corresponding spec> ] <query term>
-
-<query term> ::=
-    <query primary>
-  | <query term> INTERSECT [ ALL | DISTINCT ]
-      [ <corresponding spec> ] <query primary>
-
-<query primary> ::=
-    <simple table>
-  | <left paren> <query expression body>
-      [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
-      <right paren>
-
-> setOpQueryExpression :: TestItem
-> setOpQueryExpression= Group "set operation query expression"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     -- todo: complete setop query expression tests
->     [{-("select * from t union select * from t"
->      ,undefined)
->     ,("select * from t union all select * from t"
->      ,undefined)
->     ,("select * from t union distinct select * from t"
->      ,undefined)
->     ,("select * from t union corresponding select * from t"
->      ,undefined)
->     ,("select * from t union corresponding by (a,b) select * from t"
->      ,undefined)
->     ,("select * from t except select * from t"
->      ,undefined)
->     ,("select * from t in intersect select * from t"
->      ,undefined)-}
->     ]
-
-TODO: tests for the associativity and precendence
-
-TODO: not sure exactly where parens are allowed, we will allow them
-everywhere
-
-<simple table> ::=
-    <query specification>
-  | <table value constructor>
-  | <explicit table>
-
-<explicit table> ::= TABLE <table or query name>
-
-<corresponding spec> ::=
-  CORRESPONDING [ BY <left paren> <corresponding column list> <right paren> ]
-
-<corresponding column list> ::= <column name list>
-
-> explicitTableQueryExpression :: TestItem
-> explicitTableQueryExpression= Group "explicit table query expression"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("table t", Table [Name Nothing "t"])
->     ]
-
-
-<order by clause> ::= ORDER BY <sort specification list>
-
-<result offset clause> ::= OFFSET <offset row count> { ROW | ROWS }
-
-<fetch first clause> ::=
-  FETCH { FIRST | NEXT } [ <fetch first quantity> ] { ROW | ROWS } { ONLY | WITH TIES }
-
-<fetch first quantity> ::= <fetch first row count> | <fetch first percentage>
-
-<offset row count> ::= <simple value specification>
-
-<fetch first row count> ::= <simple value specification>
-
-<fetch first percentage> ::= <simple value specification> PERCENT
-
-> orderOffsetFetchQueryExpression :: TestItem
-> orderOffsetFetchQueryExpression = Group "order, offset, fetch query expression"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [-- todo: finish tests for order offset and fetch
->      ("select a from t order by a"
->      ,qe {qeOrderBy = [SortSpec (Iden [Name Nothing "a"])
->                            DirDefault NullsOrderDefault]})
->     ,("select a from t offset 5 row"
->      ,qe {qeOffset = Just $ NumLit "5"})
->     ,("select a from t offset 5 rows"
->      ,qe {qeOffset = Just $ NumLit "5"})
->     ,("select a from t fetch first 5 row only"
->      ,qe {qeFetchFirst = Just $ NumLit "5"})
->     -- todo: support with ties and percent in fetch
->     --,("select a from t fetch next 5 rows with ties"
->     --,("select a from t fetch first 5 percent rows only"
->     ]
->  where
->     qe = makeSelect
->          {qeSelectList = [(Iden [Name Nothing "a"], Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]
->          }
-
-
-== 7.14 <search or cycle clause>
-
-Function
-
-Specify the generation of ordering and cycle detection information in
-the result of recursive query expressions.
-
-<search or cycle clause> ::=
-    <search clause>
-  | <cycle clause>
-  | <search clause> <cycle clause>
-
-<search clause> ::= SEARCH <recursive search order> SET <sequence column>
-
-<recursive search order> ::=
-    DEPTH FIRST BY <column name list>
-  | BREADTH FIRST BY <column name list>
-
-<sequence column> ::= <column name>
-
-<cycle clause> ::=
-  CYCLE <cycle column list> SET <cycle mark column> TO <cycle mark value>
-      DEFAULT <non-cycle mark value> USING <path column>
-
-<cycle column list> ::= <cycle column> [ { <comma> <cycle column> }... ]
-
-<cycle column> ::= <column name>
-
-<cycle mark column> ::= <column name>
-
-<path column> ::= <column name>
-
-<cycle mark value> ::= <value expression>
-
-<non-cycle mark value> ::= <value expression>
-
-> searchOrCycleClause :: TestItem
-> searchOrCycleClause = Group "search or cycle clause"
->     [-- todo: search or cycle clause
->     ]
-
-== 7.15 <subquery>
-
-Function
-
-Specify a scalar value, a row, or a table derived from a <query
-expression>.
-
-<scalar subquery> ::= <subquery>
-
-<row subquery> ::= <subquery>
-
-<table subquery> ::= <subquery>
-
-<subquery> ::= <left paren> <query expression> <right paren>
-
-> scalarSubquery :: TestItem
-> scalarSubquery = Group "scalar subquery"
->     [-- todo: scalar subquery
->     ]
-
-= 8 Predicates
-
-== 8.1 <predicate>
-
-Function
-Specify a condition that can be evaluated to give a boolean value.
-
-<predicate> ::=
-    <comparison predicate>
-  | <between predicate>
-  | <in predicate>
-  | <like predicate>
-  | <similar predicate>
-  | <regex like predicate>
-  | <null predicate>
-  | <quantified comparison predicate>
-  | <exists predicate>
-  | <unique predicate>
-  | <normalized predicate>
-  | <match predicate>
-  | <overlaps predicate>
-  | <distinct predicate>
-  | <member predicate>
-  | <submultiset predicate>
-  | <set predicate>
-  | <type predicate>
-  | <period predicate>
-
-> predicates :: TestItem
-> predicates = Group "predicates"
->     [comparisonPredicates
->     ,betweenPredicate
->     ,inPredicate
->     ,likePredicate
->     ,similarPredicate
->     ,regexLikePredicate
->     ,nullPredicate
->     ,quantifiedComparisonPredicate
->     ,existsPredicate
->     ,uniquePredicate
->     ,normalizedPredicate
->     ,matchPredicate
->     ,overlapsPredicate
->     ,distinctPredicate
->     ,memberPredicate
->     ,submultisetPredicate
->     ,setPredicate
->     ,periodPredicate
->     ]
-
-
-== 8.1 <predicate>
-
-No grammar
-
-== 8.2 <comparison predicate>
-
-Function
-Specify a comparison of two row values.
-
-<comparison predicate> ::= <row value predicand> <comparison predicate part 2>
-
-<comparison predicate part 2> ::= <comp op> <row value predicand>
-
-<comp op> ::=
-    <equals operator>
-  | <not equals operator>
-  | <less than operator>
-  | <greater than operator>
-  | <less than or equals operator>
-  | <greater than or equals operator>
-
-> comparisonPredicates :: TestItem
-> comparisonPredicates = Group "comparison predicates"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     $ map mkOp ["=", "<>", "<", ">", "<=", ">="]
->     ++ [("ROW(a) = ROW(b)"
->         ,BinOp (App [Name Nothing "ROW"] [a])
->                [Name Nothing "="]
->                (App [Name Nothing "ROW"] [b]))
->        ,("(a,b) = (c,d)"
->         ,BinOp (SpecialOp [Name Nothing "rowctor"] [a,b])
->            [Name Nothing "="]
->            (SpecialOp [Name Nothing "rowctor"] [Iden [Name Nothing "c"], Iden [Name Nothing "d"]]))
->     ]
->   where
->     mkOp nm = ("a " ++ nm ++ " b"
->               ,BinOp a [Name Nothing nm] b)
->     a = Iden [Name Nothing "a"]
->     b = Iden [Name Nothing "b"]
-
-TODO: what other tests, more complex expressions with comparisons?
-
-== 8.3 <between predicate>
-
-Function
-Specify a range comparison.
-
-<between predicate> ::= <row value predicand> <between predicate part 2>
-
-<between predicate part 2> ::=
-  [ NOT ] BETWEEN [ ASYMMETRIC | SYMMETRIC ]
-      <row value predicand> AND <row value predicand>
-
-> betweenPredicate :: TestItem
-> betweenPredicate = Group "between predicate"
->     [-- todo: between predicate
->     ]
-
-== 8.4 <in predicate>
-
-Function
-Specify a quantified comparison.
-
-<in predicate> ::= <row value predicand> <in predicate part 2>
-
-<in predicate part 2> ::= [ NOT ] IN <in predicate value>
-
-<in predicate value> ::=
-    <table subquery>
-  | <left paren> <in value list> <right paren>
-
-<in value list> ::=
-  <row value expression> [ { <comma> <row value expression> }... ]
-
-> inPredicate :: TestItem
-> inPredicate = Group "in predicate"
->     [-- todo: in predicate
->     ]
-
-== 8.5 <like predicate>
-
-Function
-Specify a pattern-match comparison.
-
-<like predicate> ::= <character like predicate> | <octet like predicate>
-
-<character like predicate> ::=
-  <row value predicand> <character like predicate part 2>
-
-<character like predicate part 2> ::=
-  [ NOT ] LIKE <character pattern> [ ESCAPE <escape character> ]
-
-<character pattern> ::= <character value expression>
-
-<escape character> ::= <character value expression>
-
-<octet like predicate> ::= <row value predicand> <octet like predicate part 2>
-
-<octet like predicate part 2> ::=
-  [ NOT ] LIKE <octet pattern> [ ESCAPE <escape octet> ]
-
-<octet pattern> ::= <binary value expression>
-
-<escape octet> ::= <binary value expression>
-
-> likePredicate :: TestItem
-> likePredicate = Group "like predicate"
->     [-- todo: like predicate
->     ]
-
-== 8.6 <similar predicate>
-
-Function
-Specify a character string similarity by means of a regular expression.
-
-<similar predicate> ::= <row value predicand> <similar predicate part 2>
-
-<similar predicate part 2> ::=
-  [ NOT ] SIMILAR TO <similar pattern> [ ESCAPE <escape character> ]
-
-<similar pattern> ::= <character value expression>
-
-<regular expression> ::=
-    <regular term>
-  | <regular expression> <vertical bar> <regular term>
-
-<regular term> ::= <regular factor> | <regular term> <regular factor>
-
-<regular factor> ::=
-    <regular primary>
-  | <regular primary> <asterisk>
-  | <regular primary> <plus sign>
-  | <regular primary> <question mark>
-  | <regular primary> <repeat factor>
-
-<repeat factor> ::= <left brace> <low value> [ <upper limit> ] <right brace>
-
-<upper limit> ::= <comma> [ <high value> ]
-
-<low value> ::= <unsigned integer>
-
-<high value> ::= <unsigned integer>
-
-<regular primary> ::=
-    <character specifier>
-  | <percent>
-  | <regular character set>
-  | <left paren> <regular expression> <right paren>
-
-<character specifier> ::= <non-escaped character> | <escaped character>
-
-<non-escaped character> ::= !! See the Syntax Rules.
-
-<escaped character> ::= !! See the Syntax Rules.
-
-<regular character set> ::=
-    <underscore>
-  | <left bracket> <character enumeration>... <right bracket>
-  | <left bracket> <circumflex> <character enumeration>... <right bracket>
-  | <left bracket> <character enumeration include>...
-      <circumflex> <character enumeration exclude>... <right bracket>
-
-<character enumeration include> ::= <character enumeration>
-
-<character enumeration exclude> ::= <character enumeration>
-
-<character enumeration> ::=
-    <character specifier>
-  | <character specifier> <minus sign> <character specifier>
-  | <left bracket> <colon> <regular character set identifier> <colon> <right bracket>
-
-<regular character set identifier> ::= <identifier>
-
-> similarPredicate :: TestItem
-> similarPredicate = Group "similar predicate"
->     [-- todo: similar predicate
->     ]
-
-
-== 8.7 <regex like predicate>
-
-Function
-Specify a pattern-match comparison using an XQuery regular expression.
-
-<regex like predicate> ::= <row value predicand> <regex like predicate part 2>
-
-<regex like predicate part 2> ::=
-  [ NOT ] LIKE_REGEX <XQuery pattern> [ FLAG <XQuery option flag> ]
-
-> regexLikePredicate :: TestItem
-> regexLikePredicate = Group "regex like predicate"
->     [-- todo: regex like predicate
->     ]
-
-== 8.8 <null predicate>
-
-Function
-Specify a test for a null value.
-
-<null predicate> ::= <row value predicand> <null predicate part 2>
-
-<null predicate part 2> ::= IS [ NOT ] NULL
-
-> nullPredicate :: TestItem
-> nullPredicate = Group "null predicate"
->     [-- todo: null predicate
->     ]
-
-== 8.9 <quantified comparison predicate>
-
-Function
-Specify a quantified comparison.
-
-<quantified comparison predicate> ::=
-  <row value predicand> <quantified comparison predicate part 2>
-
-<quantified comparison predicate part 2> ::=
-  <comp op> <quantifier> <table subquery>
-
-<quantifier> ::= <all> | <some>
-
-<all> ::= ALL
-
-<some> ::= SOME | ANY
-
-> quantifiedComparisonPredicate :: TestItem
-> quantifiedComparisonPredicate = Group "quantified comparison predicate"
->     $ map (uncurry (TestScalarExpr ansi2011))
-
->     [("a = any (select * from t)"
->      ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "="] CPAny qe)
->     ,("a <= some (select * from t)"
->      ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "<="] CPSome qe)
->     ,("a > all (select * from t)"
->      ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing ">"] CPAll qe)
->     ,("(a,b) <> all (select * from t)"
->      ,QuantifiedComparison
->       (SpecialOp [Name Nothing "rowctor"] [Iden [Name Nothing "a"]
->                                   ,Iden [Name Nothing "b"]]) [Name Nothing "<>"] CPAll qe)
->     ]
->   where
->     qe = makeSelect
->          {qeSelectList = [(Star,Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]}
-
-== 8.10 <exists predicate>
-
-Function
-Specify a test for a non-empty set.
-
-<exists predicate> ::= EXISTS <table subquery>
-
-> existsPredicate :: TestItem
-> existsPredicate = Group "exists predicate"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("exists(select * from t where a = 4)"
->      ,SubQueryExpr SqExists
->       $ makeSelect
->         {qeSelectList = [(Star,Nothing)]
->         ,qeFrom = [TRSimple [Name Nothing "t"]]
->         ,qeWhere = Just (BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "4"))
->         }
->      )]
-
-== 8.11 <unique predicate>
-
-Function
-Specify a test for the absence of duplicate rows.
-
-<unique predicate> ::= UNIQUE <table subquery>
-
-> uniquePredicate :: TestItem
-> uniquePredicate = Group "unique predicate"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("unique(select * from t where a = 4)"
->      ,SubQueryExpr SqUnique
->       $ makeSelect
->         {qeSelectList = [(Star,Nothing)]
->         ,qeFrom = [TRSimple [Name Nothing "t"]]
->         ,qeWhere = Just (BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "4"))
->         }
->      )]
-
-== 8.12 <normalized predicate>
-
-Function
-Determine whether a character string value is normalized.
-
-<normalized predicate> ::= <row value predicand> <normalized predicate part 2>
-
-<normalized predicate part 2> ::= IS [ NOT ] [ <normal form> ] NORMALIZED
-
-> normalizedPredicate :: TestItem
-> normalizedPredicate = Group "normalized predicate"
->     [-- todo: normalized predicate
->     ]
-
-== 8.13 <match predicate>
-
-Function
-Specify a test for matching rows.
-
-<match predicate> ::= <row value predicand> <match predicate part 2>
-
-<match predicate part 2> ::=
-  MATCH [ UNIQUE ] [ SIMPLE | PARTIAL | FULL ] <table subquery>
-
-> matchPredicate :: TestItem
-> matchPredicate = Group "match predicate"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("a match (select a from t)"
->      ,Match (Iden [Name Nothing "a"]) False qe)
->     ,("(a,b) match (select a,b from t)"
->      ,Match (SpecialOp [Name Nothing "rowctor"]
->              [Iden [Name Nothing "a"], Iden [Name Nothing "b"]]) False qea)
->     ,("(a,b) match unique (select a,b from t)"
->      ,Match (SpecialOp [Name Nothing "rowctor"]
->              [Iden [Name Nothing "a"], Iden [Name Nothing "b"]]) True qea)
->     ]
->   where
->     qe = makeSelect
->          {qeSelectList = [(Iden [Name Nothing "a"],Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]}
->     qea = qe {qeSelectList = qeSelectList qe
->                              ++ [(Iden [Name Nothing "b"],Nothing)]}
-
-TODO: simple, partial and full
-
-== 8.14 <overlaps predicate>
-
-Function
-Specify a test for an overlap between two datetime periods.
-
-<overlaps predicate> ::=
-  <overlaps predicate part 1> <overlaps predicate part 2>
-
-<overlaps predicate part 1> ::= <row value predicand 1>
-
-<overlaps predicate part 2> ::= OVERLAPS <row value predicand 2>
-
-<row value predicand 1> ::= <row value predicand>
-
-<row value predicand 2> ::= <row value predicand>
-
-> overlapsPredicate :: TestItem
-> overlapsPredicate = Group "overlaps predicate"
->     [-- todo: overlaps predicate
->     ]
-
-== 8.15 <distinct predicate>
-
-Function
-Specify a test of whether two row values are distinct
-
-<distinct predicate> ::= <row value predicand 3> <distinct predicate part 2>
-
-<distinct predicate part 2> ::=
-  IS [ NOT ] DISTINCT FROM <row value predicand 4>
-
-<row value predicand 3> ::= <row value predicand>
-
-<row value predicand 4> ::= <row value predicand>
-
-> distinctPredicate :: TestItem
-> distinctPredicate = Group "distinct predicate"
->     [-- todo: distinct predicate
->     ]
-
-== 8.16 <member predicate>
-
-Function
-Specify a test of whether a value is a member of a multiset.
-
-<member predicate> ::= <row value predicand> <member predicate part 2>
-
-<member predicate part 2> ::= [ NOT ] MEMBER [ OF ] <multiset value expression>
-
-> memberPredicate :: TestItem
-> memberPredicate = Group "member predicate"
->     [-- todo: member predicate
->     ]
-
-== 8.17 <submultiset predicate>
-
-Function
-Specify a test of whether a multiset is a submultiset of another multiset.
-
-<submultiset predicate> ::=
-  <row value predicand> <submultiset predicate part 2>
-
-<submultiset predicate part 2> ::=
-  [ NOT ] SUBMULTISET [ OF ] <multiset value expression>
-
-> submultisetPredicate :: TestItem
-> submultisetPredicate = Group "submultiset predicate"
->     [-- todo: submultiset predicate
->     ]
-
-== 8.18 <set predicate>
-
-Function
-
-Specify a test of whether a multiset is a set (that is, does not
-contain any duplicates).
-
-<set predicate> ::= <row value predicand> <set predicate part 2>
-
-<set predicate part 2> ::= IS [ NOT ] A SET
-
-> setPredicate :: TestItem
-> setPredicate = Group "set predicate"
->     [-- todo: set predicate
->     ]
-
-== 8.19 <type predicate>
-
-Function
-Specify a type test.
-
-<type predicate> ::= <row value predicand> <type predicate part 2>
-
-<type predicate part 2> ::=
-  IS [ NOT ] OF <left paren> <type list> <right paren>
-
-<type list> ::=
-  <user-defined type specification>
-      [ { <comma> <user-defined type specification> }... ]
-
-<user-defined type specification> ::=
-    <inclusive user-defined type specification>
-  | <exclusive user-defined type specification>
-
-<inclusive user-defined type specification> ::=
-  <path-resolved user-defined type name>
-
-<exclusive user-defined type specification> ::=
-  ONLY <path-resolved user-defined type name>
-
-TODO: type predicate
-
-== 8.20 <period predicate>
-
-Function
-Specify a test to determine the relationship between periods.
-
-<period predicate> ::=
-    <period overlaps predicate>
-  | <period equals predicate>
-  | <period contains predicate>
-  | <period precedes predicate>
-  | <period succeeds predicate>
-  | <period immediately precedes predicate>
-  | <period immediately succeeds predicate>
-
-<period overlaps predicate> ::=
-  <period predicand 1> <period overlaps predicate part 2>
-
-<period overlaps predicate part 2> ::= OVERLAPS <period predicand 2>
-
-<period predicand 1> ::= <period predicand>
-
-<period predicand 2> ::= <period predicand>
-
-<period predicand> ::=
-    <period reference>
-  | PERIOD <left paren> <period start value> <comma> <period end value> <right paren>
-
-<period reference> ::= <basic identifier chain>
-
-<period start value> ::= <datetime value expression>
-
-<period end value> ::= <datetime value expression>
-
-<period equals predicate> ::=
-  <period predicand 1> <period equals predicate part 2>
-
-<period equals predicate part 2> ::= EQUALS <period predicand 2>
-
-<period contains predicate> ::=
-  <period predicand 1> <period contains predicate part 2>
-
-<period contains predicate part 2> ::=
-  CONTAINS <period or point-in-time predicand>
-
-<period or point-in-time predicand> ::=
-    <period predicand>
-  | <datetime value expression>
-
-<period precedes predicate> ::=
-  <period predicand 1> <period precedes predicate part 2>
-
-<period precedes predicate part 2> ::= PRECEDES <period predicand 2>
-
-<period succeeds predicate> ::=
-  <period predicand 1> <period succeeds predicate part 2>
-
-<period succeeds predicate part 2> ::= SUCCEEDS <period predicand 2>
-
-<period immediately precedes predicate> ::=
-  <period predicand 1> <period immediately precedes predicate part 2>
-
-<period immediately precedes predicate part 2> ::=
-  IMMEDIATELY PRECEDES <period predicand 2>
-
-<period immediately succeeds predicate> ::=
-  <period predicand 1> <period immediately succeeds predicate part 2>
-
-<period immediately succeeds predicate part 2> ::=
-  IMMEDIATELY SUCCEEDS <period predicand 2>
-
-> periodPredicate :: TestItem
-> periodPredicate = Group "period predicate"
->     [-- todo: period predicate
->     ]
-
-== 8.21 <search condition>
-
-Function
-
-Specify a condition that is True, False, or Unknown, depending on the
-value of a <boolean value expression>.
-
-<search condition> ::= <boolean value expression>
-
-= 10 Additional common elements
-
-== 10.1 <interval qualifier>
-
-Function
-Specify the precision of an interval data type.
-
-<interval qualifier> ::= <start field> TO <end field> | <single datetime field>
-
-<start field> ::=
-  <non-second primary datetime field>
-      [ <left paren> <interval leading field precision> <right paren> ]
-
-<end field> ::=
-    <non-second primary datetime field>
-  | SECOND [ <left paren> <interval fractional seconds precision> <right paren> ]
-
-<single datetime field> ::=
-    <non-second primary datetime field>
-        [ <left paren> <interval leading field precision> <right paren> ]
-  | SECOND [ <left paren> <interval leading field precision>
-      [ <comma> <interval fractional seconds precision> ] <right paren> ]
-
-<primary datetime field> ::= <non-second primary datetime field> | SECOND
-
-<non-second primary datetime field> ::= YEAR | MONTH | DAY | HOUR | MINUTE
-
-<interval fractional seconds precision> ::= <unsigned integer>
-
-<interval leading field precision> ::= <unsigned integer>
-
-> intervalQualifier :: TestItem
-> intervalQualifier = Group "interval qualifier"
->     [-- todo: interval qualifier
->     ]
-
-todo: also test all of these in the typenames and in the interval
-literal tests
-
-== 10.2 <language clause>
-
-Function
-Specify a programming language.
-
-<language clause> ::= LANGUAGE <language name>
-
-<language name> ::= ADA | C | COBOL | FORTRAN | M | MUMPS | PASCAL | PLI | SQL
-
-== 10.3 <path specification>
-
-Function
-Specify an order for searching for an SQL-invoked routine.
-
-<path specification> ::= PATH <schema name list>
-
-<schema name list> ::= <schema name> [ { <comma> <schema name> }... ]
-
-== 10.4 <routine invocation>
-
-Function
-Invoke an SQL-invoked routine.
-
-<routine invocation> ::= <routine name> <SQL argument list>
-
-<routine name> ::= [ <schema name> <period> ] <qualified identifier>
-
-<SQL argument list> ::=
-  <left paren> [ <SQL argument> [ { <comma> <SQL argument> }... ] ] <right paren>
-
-<SQL argument> ::=
-    <value expression>
-  | <generalized expression>
-  | <target specification>
-  | <contextually typed value specification>
-  | <named argument specification>
-
-<generalized expression> ::=
-  <value expression> AS <path-resolved user-defined type name>
-
-<named argument specification> ::=
-  <SQL parameter name> <named argument assignment token>
-      <named argument SQL argument>
-
-<named argument SQL argument> ::=
-    <value expression>
-  | <target specification>
-     | <contextually typed value specification>
-
-== 10.5 <character set specification>
-
-Function
-Identify a character set.
-
-<character set specification> ::=
-    <standard character set name>
-  | <implementation-defined character set name>
-  | <user-defined character set name>
-
-<standard character set name> ::= <character set name>
-
-<implementation-defined character set name> ::= <character set name>
-
-<user-defined character set name> ::= <character set name>
-
-tested in the type names
-
-== 10.6 <specific routine designator>
-
-Function
-Specify an SQL-invoked routine.
-
-<specific routine designator> ::=
-    SPECIFIC <routine type> <specific name>
-  | <routine type> <member name> [ FOR <schema-resolved user-defined type name> ]
-
-<routine type> ::=
-    ROUTINE
-  | FUNCTION
-  | PROCEDURE
-  | [ INSTANCE | STATIC | CONSTRUCTOR ] METHOD
-
-<member name> ::= <member name alternatives> [ <data type list> ]
-
-<member name alternatives> ::= <schema qualified routine name> | <method name>
-
-<data type list> ::=
-  <left paren> [ <data type> [ { <comma> <data type> }... ] ] <right paren>
-
-== 10.7 <collate clause>
-
-Function
-Specify a default collation.
-
-<collate clause> ::= COLLATE <collation name>
-
-> collateClause :: TestItem
-> collateClause = Group "collate clause"
->     $ map (uncurry (TestScalarExpr ansi2011))
->     [("a collate my_collation"
->      ,Collate (Iden [Name Nothing "a"]) [Name Nothing "my_collation"])]
-
-== 10.8 <constraint name definition> and <constraint characteristics>
-
-Function
-Specify the name of a constraint and its characteristics.
-
-<constraint name definition> ::= CONSTRAINT <constraint name>
-
-<constraint characteristics> ::=
-    <constraint check time> [ [ NOT ] DEFERRABLE ] [ <constraint enforcement> ]
-  | [ NOT ] DEFERRABLE [ <constraint check time> ] [ <constraint enforcement> ]
-  | <constraint enforcement>
-
-<constraint check time> ::= INITIALLY DEFERRED | INITIALLY IMMEDIATE
-
-<constraint enforcement> ::= [ NOT ] ENFORCED
-
-== 10.9 <aggregate function>
-
-Function
-Specify a value computed from a collection of rows.
-
-<aggregate function> ::=
-    COUNT <left paren> <asterisk> <right paren> [ <filter clause> ]
-  | <general set function> [ <filter clause> ]
-  | <binary set function> [ <filter clause> ]
-  | <ordered set function> [ <filter clause> ]
-  | <array aggregate function> [ <filter clause> ]
-
-<general set function> ::=
-  <set function type> <left paren> [ <set quantifier> ]
-      <value expression> <right paren>
-
-<set function type> ::= <computational operation>
-
-<computational operation> ::=
-    AVG
-  | MAX
-  | MIN
-  | SUM
-  | EVERY
-  | ANY
-  | SOME
-  | COUNT
-  | STDDEV_POP
-  | STDDEV_SAMP
-  | VAR_SAMP
-  | VAR_POP
-  | COLLECT
-  | FUSION
-  | INTERSECTION
-
-<set quantifier> ::= DISTINCT | ALL
-
-<filter clause> ::= FILTER <left paren> WHERE <search condition> <right paren>
-
-<binary set function> ::=
-  <binary set function type> <left paren> <dependent variable expression> <comma>
-      <independent variable expression> <right paren>
-
-<binary set function type> ::=
-    COVAR_POP
-  | COVAR_SAMP
-  | CORR
-  | REGR_SLOPE
-  | REGR_INTERCEPT
-  | REGR_COUNT
-  | REGR_R2
-  | REGR_AVGX
-  | REGR_AVGY
-  | REGR_SXX
-  | REGR_SYY
-  | REGR_SXY
-
-<dependent variable expression> ::= <numeric value expression>
-
-<independent variable expression> ::= <numeric value expression>
-
-<ordered set function> ::=
-    <hypothetical set function>
-  | <inverse distribution function>
-
-<hypothetical set function> ::=
-  <rank function type> <left paren>
-      <hypothetical set function value expression list> <right paren>
-      <within group specification>
-
-<within group specification> ::=
-  WITHIN GROUP <left paren> ORDER BY <sort specification list> <right paren>
-
-<hypothetical set function value expression list> ::=
-  <value expression> [ { <comma> <value expression> }... ]
-
-<inverse distribution function> ::=
-  <inverse distribution function type> <left paren>
-      <inverse distribution function argument> <right paren>
-      <within group specification>
-
-<inverse distribution function argument> ::= <numeric value expression>
-
-<inverse distribution function type> ::= PERCENTILE_CONT | PERCENTILE_DISC
-
-<array aggregate function> ::=
-  ARRAY_AGG
-      <left paren> <value expression> [ ORDER BY <sort specification list> ] <right paren>
-
-> aggregateFunction :: TestItem
-> aggregateFunction = Group "aggregate function"
->     $ map (uncurry (TestScalarExpr ansi2011)) $
->     [("count(*)",App [Name Nothing "count"] [Star])
->     ,("count(*) filter (where something > 5)"
->      ,AggregateApp [Name Nothing "count"] SQDefault [Star] [] fil)
-
-gsf
-
->     ,("count(a)",App [Name Nothing "count"] [Iden [Name Nothing "a"]])
->     ,("count(distinct a)"
->      ,AggregateApp [Name Nothing "count"]
->                    Distinct
->                    [Iden [Name Nothing "a"]] [] Nothing)
->     ,("count(all a)"
->      ,AggregateApp [Name Nothing "count"]
->                    All
->                    [Iden [Name Nothing "a"]] [] Nothing)
->     ,("count(all a) filter (where something > 5)"
->      ,AggregateApp [Name Nothing "count"]
->                    All
->                    [Iden [Name Nothing "a"]] [] fil)
->     ] ++ concatMap mkSimpleAgg
->          ["avg","max","min","sum"
->          ,"every", "any", "some"
->          ,"stddev_pop","stddev_samp","var_samp","var_pop"
->          ,"collect","fusion","intersection"]
-
-bsf
-
->     ++ concatMap mkBsf
->          ["COVAR_POP","COVAR_SAMP","CORR","REGR_SLOPE"
->           ,"REGR_INTERCEPT","REGR_COUNT","REGR_R2"
->           ,"REGR_AVGX","REGR_AVGY"
->           ,"REGR_SXX","REGR_SYY","REGR_SXY"]
-
-osf
-
->     ++
->     [("rank(a,c) within group (order by b)"
->      ,AggregateAppGroup [Name Nothing "rank"]
->           [Iden [Name Nothing "a"], Iden [Name Nothing "c"]]
->           ob)]
->     ++ map mkGp ["dense_rank","percent_rank"
->                 ,"cume_dist", "percentile_cont"
->                 ,"percentile_disc"]
->     ++ [("array_agg(a)", App [Name Nothing "array_agg"] [Iden [Name Nothing "a"]])
->        ,("array_agg(a order by z)"
->         ,AggregateApp [Name Nothing "array_agg"]
->                        SQDefault
->                        [Iden [Name Nothing "a"]]
->                        [SortSpec (Iden [Name Nothing "z"])
->                             DirDefault NullsOrderDefault]
->                        Nothing)]
-
->   where
->     fil = Just $ BinOp (Iden [Name Nothing "something"]) [Name Nothing ">"] (NumLit "5")
->     ob = [SortSpec (Iden [Name Nothing "b"]) DirDefault NullsOrderDefault]
->     mkGp nm = (nm ++ "(a) within group (order by b)"
->               ,AggregateAppGroup [Name Nothing nm]
->                [Iden [Name Nothing "a"]]
->                ob)
-
->     mkSimpleAgg nm =
->         [(nm ++ "(a)",App [Name Nothing nm] [Iden [Name Nothing "a"]])
->         ,(nm ++ "(distinct a)"
->          ,AggregateApp [Name Nothing nm]
->                        Distinct
->                        [Iden [Name Nothing "a"]] [] Nothing)]
->     mkBsf nm =
->         [(nm ++ "(a,b)",App [Name Nothing nm] [Iden [Name Nothing "a"],Iden [Name Nothing "b"]])
->         ,(nm ++"(a,b) filter (where something > 5)"
->           ,AggregateApp [Name Nothing nm]
->                         SQDefault
->                         [Iden [Name Nothing "a"],Iden [Name Nothing "b"]] [] fil)]
-
-== 10.10 <sort specification list>
-
-Function
-Specify a sort order.
-
-<sort specification list> ::=
-  <sort specification> [ { <comma> <sort specification> }... ]
-
-<sort specification> ::=
-  <sort key> [ <ordering specification> ] [ <null ordering> ]
-
-<sort key> ::= <value expression>
-
-<ordering specification> ::= ASC | DESC
-
-<null ordering> ::=
-  | NULLS LAST
-    NULLS FIRST
-
-> sortSpecificationList :: TestItem
-> sortSpecificationList = Group "sort specification list"
->     $ map (uncurry (TestQueryExpr ansi2011))
->     [("select * from t order by a"
->      ,qe {qeOrderBy = [SortSpec (Iden [Name Nothing "a"])
->                            DirDefault NullsOrderDefault]})
->     ,("select * from t order by a,b"
->      ,qe {qeOrderBy = [SortSpec (Iden [Name Nothing "a"])
->                            DirDefault NullsOrderDefault
->                       ,SortSpec (Iden [Name Nothing "b"])
->                            DirDefault NullsOrderDefault]})
->     ,("select * from t order by a asc,b"
->      ,qe {qeOrderBy = [SortSpec (Iden [Name Nothing "a"])
->                            Asc NullsOrderDefault
->                       ,SortSpec (Iden [Name Nothing "b"])
->                            DirDefault NullsOrderDefault]})
->     ,("select * from t order by a desc,b"
->      ,qe {qeOrderBy = [SortSpec (Iden [Name Nothing "a"])
->                            Desc NullsOrderDefault
->                       ,SortSpec (Iden [Name Nothing "b"])
->                            DirDefault NullsOrderDefault]})
->     ,("select * from t order by a collate x desc,b"
->      ,qe {qeOrderBy = [SortSpec
->                            (Collate (Iden [Name Nothing "a"]) [Name Nothing "x"])
->                            Desc NullsOrderDefault
->                       ,SortSpec (Iden [Name Nothing "b"])
->                            DirDefault NullsOrderDefault]})
->     ,("select * from t order by 1,2"
->      ,qe {qeOrderBy = [SortSpec (NumLit "1")
->                            DirDefault NullsOrderDefault
->                       ,SortSpec (NumLit "2")
->                            DirDefault NullsOrderDefault]})
->     ]
->   where
->     qe = makeSelect
->          {qeSelectList = [(Star,Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]}
diff --git a/tools/Language/SQL/SimpleSQL/SQL2011Schema.lhs b/tools/Language/SQL/SimpleSQL/SQL2011Schema.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/SQL2011Schema.lhs
+++ /dev/null
@@ -1,2057 +0,0 @@
-
-Section 11 in Foundation
-
-This module covers the tests for parsing schema and DDL statements.
-
-> module Language.SQL.SimpleSQL.SQL2011Schema (sql2011SchemaTests) where
-
-> import Language.SQL.SimpleSQL.TestTypes
-> import Language.SQL.SimpleSQL.Syntax
-
-> sql2011SchemaTests :: TestItem
-> sql2011SchemaTests = Group "sql 2011 schema tests"
->     [
-
-
-11.1 <schema definition>
-
-<schema definition> ::=
-  CREATE SCHEMA <schema name clause>
-      [ <schema character set or path> ]
-      [ <schema element>... ]
-
->      (TestStatement ansi2011 "create schema my_schema"
->      $ CreateSchema [Name Nothing "my_schema"])
-
-todo: schema name can have .
-schema name can be quoted iden or unicode quoted iden
-add schema element support:
-  create a list of schema elements
-  then do pairwise combinations in schema element list to test
-
-
-<schema character set or path> ::=
-    <schema character set specification>
-  | <schema path specification>
-  | <schema character set specification> <schema path specification>
-  | <schema path specification> <schema character set specification>
-
-<schema name clause> ::=
-    <schema name>
-  | AUTHORIZATION <schema authorization identifier>
-  | <schema name> AUTHORIZATION <schema authorization identifier>
-
-<schema authorization identifier> ::=
-  <authorization identifier>
-
-<schema character set specification> ::=
-  DEFAULT CHARACTER SET <character set specification>
-
-<schema path specification> ::=
-  <path specification>
-
-<schema element> ::=
-      <table definition>
-  |   <view definition>
-  |   <domain definition>
-  |   <character set definition>
-  |   <collation definition>
-  |   <transliteration definition>
-  |   <assertion definition>
-  |   <trigger definition>
-  |   <user-defined type definition>
-  |   <user-defined cast definition>
-  |   <user-defined ordering definition>
-  |   <transform definition>
-  |   <schema routine>
-  |   <sequence generator definition>
-  |   <grant statement>
-  |   <role definition>
-
-
-11.2 <drop schema statement>
-
-<drop schema statement> ::=
-  DROP SCHEMA <schema name> <drop behavior>
-
-<drop behavior> ::=
-    CASCADE
-  | RESTRICT
-
-
->     ,(TestStatement ansi2011 "drop schema my_schema"
->      $ DropSchema [Name Nothing "my_schema"] DefaultDropBehaviour)
->     ,(TestStatement ansi2011 "drop schema my_schema cascade"
->      $ DropSchema [Name Nothing "my_schema"] Cascade)
->     ,(TestStatement ansi2011 "drop schema my_schema restrict"
->      $ DropSchema [Name Nothing "my_schema"] Restrict)
-
-11.3 <table definition>
-
-
-<table definition> ::=
-  CREATE [ <table scope> ] TABLE <table name> <table contents source>
-      [ WITH <system versioning clause> ]
-      [ ON COMMIT <table commit action> ROWS ]
-
->     ,(TestStatement ansi2011 "create table t (a int, b int);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []])
-
-
-<table contents source> ::=
-    <table element list>
-  | <typed table clause>
-  | <as subquery clause>
-
-<table scope> ::=
-  <global or local> TEMPORARY
-
-<global or local> ::=
-    GLOBAL
-  | LOCAL
-
-<system versioning clause> ::=
-  SYSTEM VERSIONING
-
-defintely skip
-
-<table commit action> ::=
-    PRESERVE
-  | DELETE
-
-defintely skip
-
-<table element list> ::=
-  <left paren> <table element> [ { <comma> <table element> }... ] <right paren>
-
-<table element> ::=
-    <column definition>
-  | <table period definition>
-  | <table constraint definition>
-  | <like clause>
-
-<typed table clause> ::=
-  OF <path-resolved user-defined type name> [ <subtable clause> ]
-      [ <typed table element list> ]
-
-defintely skip
-
-<typed table element list> ::=
-  <left paren> <typed table element>
-      [ { <comma> <typed table element> }... ] <right paren>
-
-defintely skip
-
-<typed table element> ::=
-    <column options>
- | <table constraint definition>
-  | <self-referencing column specification>
-
-defintely skip
-
-<self-referencing column specification> ::=
-  REF IS <self-referencing column name> [ <reference generation> ]
-
-defintely skip
-
-<reference generation> ::=
-    SYSTEM GENERATED
-  | USER GENERATED
-  | DERIVED
-
-defintely skip
-
-<self-referencing column name> ::=
-  <column name>
-
-defintely skip
-
-<column options> ::=
-  <column name> WITH OPTIONS <column option list>
-
-defintely skip
-
-<column option list> ::=
-  [ <scope clause> ] [ <default clause> ] [ <column constraint definition>... ]
-
-defintely skip
-
-<subtable clause> ::=
-  UNDER <supertable clause>
-
-defintely skip
-
-<supertable clause> ::=
-  <supertable name>
-
-defintely skip
-
-<supertable name> ::=
-  <table name>
-
-defintely skip
-
-<like clause> ::=
-  LIKE <table name> [ <like options> ]
-
-<like options> ::=
-  <like option>...
-
-<like option> ::=
-    <identity option>
-  | <column default option>
-  | <generation option>
-
-<identity option> ::=
-    INCLUDING IDENTITY
-  | EXCLUDING IDENTITY
-
-<column default option> ::=
-    INCLUDING DEFAULTS
-  | EXCLUDING DEFAULTS
-
-<generation option> ::=
-    INCLUDING GENERATED
-  | EXCLUDING GENERATED
-
-<as subquery clause> ::=
-  [ <left paren> <column name list> <right paren> ] AS <table subquery>
-      <with or without data>
-
-<with or without data> ::=
-    WITH NO DATA
-  | WITH DATA
-
-<table period definition> ::=
-  <system or application time period specification>
-      <left paren> <period begin column name> <comma> <period end column name> <right paren>
-
-defintely skip
-
-<system or application time period specification> ::=
-    <system time period specification>
-  | <application time period specification>
-
-defintely skip
-
-<system time period specification> ::=
-  PERIOD FOR SYSTEM_TIME
-
-defintely skip
-
-<application time period specification> ::=
-  PERIOD FOR <application time period name>
-
-defintely skip
-
-<application time period name> ::=
-  <identifier>
-
-defintely skip
-
-<period begin column name> ::=
-  <column name>
-
-defintely skip
-
-<period end column name> ::=
-  <column name>
-
-defintely skip
-
-
-11.4 <column definition>
-
-<column definition> ::=
-  <column name> [ <data type or domain name> ]
-      [ <default clause> | <identity column specification> | <generation clause>
-      | <system time period start column specification>
-      | <system time period end column specification> ]
-      [ <column constraint definition>... ]
-      [ <collate clause> ]
-
-<data type or domain name> ::=
-    <data type>
-  | <domain name>
-
-<system time period start column specification> ::=
-  <timestamp generation rule> AS ROW START
-
-defintely skip
-
-<system time period end column specification> ::=
-  <timestamp generation rule> AS ROW END
-
-defintely skip
-
-<timestamp generation rule> ::=
-  GENERATED ALWAYS
-
-defintely skip
-
-<column constraint definition> ::=
-  [ <constraint name definition> ] <column constraint> [ <constraint characteristics> ]
-
-<column constraint> ::=
-    NOT NULL
-  | <unique specification>
-  | <references specification>
-  | <check constraint definition>
-
-
-can have more than one
-whitespace separated
-
-one constratint:
-optional name: constraint [Name]
-not null | unique | references | check
-todo: constraint characteristics
-
-
->     ,(TestStatement ansi2011
->       "create table t (a int not null);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing ColNotNullConstraint]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int constraint a_not_null not null);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef (Just [Name Nothing "a_not_null"]) ColNotNullConstraint]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int unique);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing ColUniqueConstraint]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int primary key);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing ColPrimaryKeyConstraint]])
-
-references t(a,b)
-  [ Full |partial| simepl]
-  [perm: on update [cascade | set null | set default | restrict | no action]
-         on delete ""
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing DefaultReferenceMatch
->          DefaultReferentialAction DefaultReferentialAction]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u(a));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] (Just $ Name Nothing "a") DefaultReferenceMatch
->          DefaultReferentialAction DefaultReferentialAction]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u match full);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing MatchFull
->          DefaultReferentialAction DefaultReferentialAction]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u match partial);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing MatchPartial
->          DefaultReferentialAction DefaultReferentialAction]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u match simple);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing MatchSimple
->          DefaultReferentialAction DefaultReferentialAction]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u on update cascade );"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing DefaultReferenceMatch
->          RefCascade DefaultReferentialAction]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u on update set null );"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing DefaultReferenceMatch
->          RefSetNull DefaultReferentialAction]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u on update set default );"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing DefaultReferenceMatch
->          RefSetDefault DefaultReferentialAction]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u on update no action );"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing DefaultReferenceMatch
->          RefNoAction DefaultReferentialAction]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u on delete cascade );"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing DefaultReferenceMatch
->          DefaultReferentialAction RefCascade]])
-
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u on update cascade on delete restrict );"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing DefaultReferenceMatch
->          RefCascade RefRestrict]])
-
->     ,(TestStatement ansi2011
->       "create table t (a int references u on delete restrict on update cascade );"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing $ ColReferencesConstraint
->          [Name Nothing "u"] Nothing DefaultReferenceMatch
->          RefCascade RefRestrict]])
-
-TODO: try combinations and permutations of column constraints and
-options
-
-
->     ,(TestStatement ansi2011
->       "create table t (a int check (a>5));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing
->         [ColConstraintDef Nothing
->          (ColCheckConstraint $ BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (NumLit "5"))]])
-
-
-
-
-
-<identity column specification> ::=
-  GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY
-      [ <left paren> <common sequence generator options> <right paren> ]
-
->     ,(TestStatement ansi2011 "create table t (a int generated always as identity);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
->         (Just $ IdentityColumnSpec GeneratedAlways []) []])
-
->     ,(TestStatement ansi2011 "create table t (a int generated by default as identity);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
->         (Just $ IdentityColumnSpec GeneratedByDefault []) []])
-
-
->     ,(TestStatement ansi2011
->       "create table t (a int generated always as identity\n\
->       \  ( start with 5 increment by 5 maxvalue 500 minvalue 5 cycle ));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
->         (Just $ IdentityColumnSpec GeneratedAlways
->          [SGOStartWith 5
->          ,SGOIncrementBy 5
->          ,SGOMaxValue 500
->          ,SGOMinValue 5
->          ,SGOCycle]) []])
-
->     ,(TestStatement ansi2011
->       "create table t (a int generated always as identity\n\
->       \  ( start with -4 no maxvalue no minvalue no cycle ));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
->         (Just $ IdentityColumnSpec GeneratedAlways
->          [SGOStartWith (-4)
->          ,SGONoMaxValue
->          ,SGONoMinValue
->          ,SGONoCycle]) []])
-
-I think <common sequence generator options> is supposed to just
-whitespace separated. In db2 it seems to be csv, but the grammar here
-just seems to be whitespace separated, and it is just whitespace
-separated in oracle... Not completely sure though. Usually db2 is
-closer than oracle?
-
-generated always (valueexpr)
-
-<generation clause> ::=
-  <generation rule> AS <generation expression>
-
-<generation rule> ::=
-  GENERATED ALWAYS
-
-<generation expression> ::=
-  <left paren> <value expression> <right paren>
-
->     ,(TestStatement ansi2011
->       "create table t (a int, \n\
->       \                a2 int generated always as (a * 2));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableColumnDef $ ColumnDef (Name Nothing "a2") (TypeName [Name Nothing "int"])
->         (Just $ GenerationClause
->          (BinOp (Iden [Name Nothing "a"]) [Name Nothing "*"] (NumLit "2"))) []])
-
-
-
-11.5 <default clause>
-
-<default clause> ::=
-  DEFAULT <default option>
-
-<default option> ::=
-    <literal>
-  | <datetime value function>
-  | USER
-  | CURRENT_USER
-  | CURRENT_ROLE
-  | SESSION_USER
-  | SYSTEM_USER
-  | CURRENT_CATALOG
-  | CURRENT_SCHEMA
-  | CURRENT_PATH
-  | <implicitly typed value specification>
-
-
->     ,(TestStatement ansi2011 "create table t (a int default 0);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"])
->         (Just $ DefaultClause $ NumLit "0") []])
-
-
-
-11.6 <table constraint definition>
-
-<table constraint definition> ::=
-  [ <constraint name definition> ] <table constraint>
-      [ <constraint characteristics> ]
-
-<table constraint> ::=
-    <unique constraint definition>
-  | <referential constraint definition>
-  | <check constraint definition>
-
-11.7 <unique constraint definition>
-
-<unique constraint definition> ::=
-    <unique specification> <left paren> <unique column list> [ <comma> <without overlap
-    specification> ] <right paren>
-  | UNIQUE ( VALUE )
-
-<unique specification> ::=
-    UNIQUE
-  | PRIMARY KEY
-
-<unique column list> ::=
-  <column name list>
-
->     ,(TestStatement ansi2011
->       "create table t (a int, unique (a));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableConstraintDef Nothing $ TableUniqueConstraint [Name Nothing "a"]
->         ])
-
->     ,(TestStatement ansi2011
->       "create table t (a int, constraint a_unique unique (a));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableConstraintDef (Just [Name Nothing "a_unique"]) $
->             TableUniqueConstraint [Name Nothing "a"]
->         ])
-
-todo: test permutations of column defs and table constraints
-
->     ,(TestStatement ansi2011
->       "create table t (a int, b int, unique (a,b));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableConstraintDef Nothing $
->             TableUniqueConstraint [Name Nothing "a", Name Nothing "b"]
->         ])
-
->     ,(TestStatement ansi2011
->       "create table t (a int, b int, primary key (a,b));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableConstraintDef Nothing $
->             TablePrimaryKeyConstraint [Name Nothing "a", Name Nothing "b"]
->         ])
-
-
-<without overlap specification> ::=
-  <application time period name> WITHOUT OVERLAPS
-
-defintely skip
-
-
-11.8 <referential constraint definition>
-
-<referential constraint definition> ::=
-  FOREIGN KEY <left paren> <referencing column list>
-      [ <comma> <referencing period specification> ] <right paren>
-      <references specification>
-
-
->     ,(TestStatement ansi2011
->       "create table t (a int, b int,\n\
->       \                foreign key (a,b) references u(c,d) match full on update cascade on delete restrict );"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableConstraintDef Nothing $
->             TableReferencesConstraint
->                 [Name Nothing "a", Name Nothing "b"]
->                 [Name Nothing "u"]
->                 (Just [Name Nothing "c", Name Nothing "d"])
->                 MatchFull RefCascade RefRestrict
->        ])
-
->     ,(TestStatement ansi2011
->       "create table t (a int,\n\
->       \                constraint tfku1 foreign key (a) references u);"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableConstraintDef (Just [Name Nothing "tfku1"]) $
->             TableReferencesConstraint
->                 [Name Nothing "a"]
->                 [Name Nothing "u"]
->                 Nothing DefaultReferenceMatch
->                 DefaultReferentialAction DefaultReferentialAction
->        ])
-
-
-
-<references specification> ::=
-  REFERENCES <referenced table and columns>
-      [ MATCH <match type> ] [ <referential triggered action> ]
-
-<match type> ::=
-    FULL
-  | PARTIAL
-  | SIMPLE
-
-<referencing column list> ::=
-  <column name list>
-
-<referencing period specification> ::=
-  PERIOD <application time period name>
-
-defintely skip
-
-<referenced table and columns> ::=
-  <table name> [ <left paren> <referenced column list>
-      [ <comma> <referenced period specification> ] <right paren> ]
-
-<referenced column list> ::=
-  <column name list>
-
-<referenced period specification> ::=
-  PERIOD <application time period name>
-
-defintely skip
-
-<referential triggered action> ::=
-    <update rule> [ <delete rule> ]
-  | <delete rule> [ <update rule> ]
-
-<update rule> ::=
-  ON UPDATE <referential action>
-
-<delete rule> ::=
-  ON DELETE <referential action>
-
-<referential action> ::=
-    CASCADE
-  | SET NULL
-  | SET DEFAULT
-  | RESTRICT
-  | NO ACTION
-
-
-
-11.9 <check constraint definition>
-
-<check  constraint definition> ::=
-     CHECK <left paren> <search condition> <right paren>
-
->     ,(TestStatement ansi2011
->       "create table t (a int, b int, \n\
->       \                check (a > b));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableConstraintDef Nothing $
->             TableCheckConstraint
->             (BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (Iden [Name Nothing "b"]))
->        ])
-
-
->     ,(TestStatement ansi2011
->       "create table t (a int, b int, \n\
->       \                constraint agtb check (a > b));"
->      $ CreateTable [Name Nothing "t"]
->        [TableColumnDef $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableColumnDef $ ColumnDef (Name Nothing "b") (TypeName [Name Nothing "int"]) Nothing []
->        ,TableConstraintDef (Just [Name Nothing "agtb"]) $
->             TableCheckConstraint
->             (BinOp (Iden [Name Nothing "a"]) [Name Nothing ">"] (Iden [Name Nothing "b"]))
->        ])
-
-
-TODO: lots more combos of table elements
-and types and the other bits in a column def
-
-11.10 <alter table statement>
-
-<alter table statement> ::=
-  ALTER TABLE <table name> <alter table action>
-
-<alter table action> ::=
-    <add column definition>
-  | <alter column definition>
-  | <drop column definition>
-  | <add table constraint definition>
-  | <alter table constraint definition>
-  | <drop table constraint definition>
-  | <add table period definition>
-  | <drop table period definition>
-  | <add system versioning clause>
-  | <drop system versioning clause>
-
-11.11 <add column definition>
-
-<add column definition> ::=
-  ADD [ COLUMN ] <column definition>
-
-alter table t add column a int
-alter table t add a int
-alter table t add a int unique not null check (a>0)
-
->     ,(TestStatement ansi2011
->       "alter table t add column a int"
->      $ AlterTable [Name Nothing "t"] $ AddColumnDef
->        $ ColumnDef (Name Nothing "a") (TypeName [Name Nothing "int"]) Nothing []
->        )
-
-todo: more add column
-
-11.12 <alter column definition>
-
-<alter column definition> ::=
-  ALTER [ COLUMN ] <column name> <alter column action>
-
-<alter column action> ::=
-    <set column default clause>
-  | <drop column default clause>
-  | <set column not null clause>
-  | <drop column not null clause>
-  | <add column scope clause>
-  | <drop column scope clause>
-  | <alter column data type clause>
-  | <alter identity column specification>
-  | <drop identity property clause>
-  | <drop column generation expression clause>
-
-
-11.13 <set column default clause>
-
-<set column default clause> ::=
-  SET <default clause>
-
-
->     ,(TestStatement ansi2011
->       "alter table t alter column c set default 0"
->      $ AlterTable [Name Nothing "t"] $ AlterColumnSetDefault (Name Nothing "c")
->        $ NumLit "0")
-
-11.14 <drop column default clause>
-
-<drop column default clause> ::=
-  DROP DEFAULT
-
->     ,(TestStatement ansi2011
->       "alter table t alter column c drop default"
->      $ AlterTable [Name Nothing "t"] $ AlterColumnDropDefault (Name Nothing "c"))
-
-
-11.15 <set column not null clause>
-
-<set column not null clause> ::=
-  SET NOT NULL
-
->     ,(TestStatement ansi2011
->       "alter table t alter column c set not null"
->      $ AlterTable [Name Nothing "t"] $ AlterColumnSetNotNull (Name Nothing "c"))
-
-11.16 <drop column not null clause>
-
-<drop column not null clause> ::=
-  DROP NOT NULL
-
->     ,(TestStatement ansi2011
->       "alter table t alter column c drop not null"
->      $ AlterTable [Name Nothing "t"] $ AlterColumnDropNotNull (Name Nothing "c"))
-
-11.17 <add column scope clause>
-
-<add column scope clause> ::=
-  ADD <scope clause>
-
-11.18 <drop column scope clause>
-
-<drop column scope clause> ::=
-  DROP SCOPE <drop behavior>
-
-11.19 <alter column data type clause>
-
-<alter column data type clause> ::=
-  SET DATA TYPE <data type>
-
->     ,(TestStatement ansi2011
->       "alter table t alter column c set data type int;"
->      $ AlterTable [Name Nothing "t"] $
->        AlterColumnSetDataType (Name Nothing "c") (TypeName [Name Nothing "int"]))
-
-
-
-11.20 <alter identity column specification>
-
-<alter identity column specification> ::=
-    <set identity column generation clause> [ <alter identity column option>... ]
-  | <alter identity column option>...
-
-<set identity column generation clause> ::=
-  SET GENERATED { ALWAYS | BY DEFAULT }
-
-so you have to write set generated for alter identity?
-and you have to use always or by default
-
-makes no sense: if you just want to restart you have to explicitly set
-the always or by default? you can't just leave it unchanged?
-
-you don't write as identity like with create table, this is wrong:
-
-alter table t alter column c set generated always as identity
-
-but these are ok?
-
-alter table t alter column c set generated always
-
-alter table t alter column c set generated by default
-
-<alter identity column option> ::=
-    <alter sequence generator restart option>
-  | SET <basic sequence generator option>
-
-alter table t alter column c set generated always restart
-alter table t alter column c set generated always restart with 4
-
-you can just write restart
-
-but to write others you have to repeat set? each time?
-
-alter table t alter column c set generated always set increment by 5 set minvalue 0 set maxvalue 5 set cycle restart with 5
-(no set before the restart
-
-in create table, it looks like this:
-
-c int generated generated always as identity (increment by 5 minvalue 0 maxvalue 5 cycle restart with 5)
-
-why gratuituous differences???
-
-is there no way to do this:
-
-alter table t alter column c set generated as (a * 3)
-??
-
-UPDATE: alter sequence uses same syntax as create sequence, which is
-the same sytnax as identity in create table, so overrule the sql
-standard and use the same syntax in alter identity.
-
-PLAN: TODO
-
-don't implement alter table alter column generated now
-
-review the syntax for generated in db2, oracle, sql server, postgres, others?
-
-observe which features are supported, and the consistency between
-create table and alter table
-
-try to find some people to ask if the standard really is this much of
-a mess or I have misunderstood the grammer, or maybe there is a good
-reason for the inconsistencies?
-
-
-11.21 <drop identity property clause>
-
-<drop identity property clause> ::=
-  DROP IDENTITY
-
-alter table t alter column c drop identity
-
-included in the generated plan above
-
-11.22 <drop column generation expression clause>
-
-<drop column generation expression clause> ::=
-  DROP EXPRESSION
-
-alter table t alter column c drop expression
-
-included in the generated plan above
-
-
-11.23 <drop column definition>
-
-<drop column definition> ::=
-  DROP [ COLUMN ] <column name> <drop behavior>
-
->     ,(TestStatement ansi2011
->       "alter table t drop column c"
->      $ AlterTable [Name Nothing "t"] $
->        DropColumn (Name Nothing "c") DefaultDropBehaviour)
-
->     ,(TestStatement ansi2011
->       "alter table t drop c cascade"
->      $ AlterTable [Name Nothing "t"] $
->        DropColumn (Name Nothing "c") Cascade)
-
->     ,(TestStatement ansi2011
->       "alter table t drop c restrict"
->      $ AlterTable [Name Nothing "t"] $
->        DropColumn (Name Nothing "c") Restrict)
-
-
-
-11.24 <add table constraint definition>
-
-<add table constraint definition> ::=
-  ADD <table constraint definition>
-
->     ,(TestStatement ansi2011
->       "alter table t add constraint c unique (a,b)"
->      $ AlterTable [Name Nothing "t"] $
->        AddTableConstraintDef (Just [Name Nothing "c"])
->             $ TableUniqueConstraint [Name Nothing "a", Name Nothing "b"])
-
->     ,(TestStatement ansi2011
->       "alter table t add unique (a,b)"
->      $ AlterTable [Name Nothing "t"] $
->        AddTableConstraintDef Nothing
->             $ TableUniqueConstraint [Name Nothing "a", Name Nothing "b"])
-
-
-11.25 <alter table constraint definition>
-<alter table constraint definition> ::=
-  ALTER CONSTRAINT <constraint name> <constraint enforcement>
-
-todo
-
-11.26 <drop table constraint definition>
-
-<drop table constraint definition> ::=
-  DROP CONSTRAINT <constraint name> <drop behavior>
-
->     ,(TestStatement ansi2011
->       "alter table t drop constraint c"
->      $ AlterTable [Name Nothing "t"] $
->        DropTableConstraintDef [Name Nothing "c"] DefaultDropBehaviour)
-
->     ,(TestStatement ansi2011
->       "alter table t drop constraint c restrict"
->      $ AlterTable [Name Nothing "t"] $
->        DropTableConstraintDef [Name Nothing "c"] Restrict)
-
-11.27 <add table period definition>
-
-<add table period definition> ::=
-  ADD <table period definition> [ <add system time period column list> ]
-
-defintely skip
-
-<add system time period column list> ::=
-  ADD [ COLUMN ] <column definition 1> ADD [ COLUMN ] <column definition 2>
-
-defintely skip
-
-<column definition 1> ::=
-  <column definition>
-
-defintely skip
-
-<column definition 2> ::=
-  <column definition>
-
-defintely skip
-
-11.28 <drop table period definition>
-
-<drop table period definition> ::=
-  DROP <system or application time period specification> <drop behavior>
-
-defintely skip
-
-11.29 <add system versioning clause>
-
-<add system versioning clause> ::=
-  ADD <system versioning clause>
-
-defintely skip
-
-11.30 <drop system versioning clause>
-
-<drop system versioning clause> ::=
-  DROP SYSTEM VERSIONING <drop behavior>
-
-defintely skip
-
-11.31 <drop table statement>
-
-<drop table statement> ::=
-  DROP TABLE <table name> <drop behavior>
-
->     ,(TestStatement ansi2011
->       "drop table t"
->      $ DropTable [Name Nothing "t"] DefaultDropBehaviour)
-
->     ,(TestStatement ansi2011
->       "drop table t restrict"
->      $ DropTable [Name Nothing "t"] Restrict)
-
-
-11.32 <view definition>
-
-<view definition> ::=
-  CREATE [ RECURSIVE ] VIEW <table name> <view specification>
-      AS <query expression> [ WITH [ <levels clause> ] CHECK OPTION ]
-
-<view specification> ::=
-    <regular view specification>
-  | <referenceable view specification>
-
-<regular view specification> ::=
-  [ <left paren> <view column list> <right paren> ]
-
-<referenceable view specification> ::=
-  OF <path-resolved user-defined type name> [ <subview clause> ]
-      [ <view element list> ]
-
-<subview clause> ::=
-  UNDER <table name>
-
-<view element list> ::=
-  <left paren> <view element> [ { <comma> <view element> }... ] <right paren>
-
-<view element> ::=
-    <self-referencing column specification>
-  | <view column option>
-
-<view column option> ::=
-  <column name> WITH OPTIONS <scope clause>
-
-<levels clause> ::=
-    CASCADED
-  | LOCAL
-
-<view column list> ::=
-  <column name list>
-
->     ,(TestStatement ansi2011
->       "create view v as select * from t"
->      $ CreateView False [Name Nothing "v"] Nothing (makeSelect
->          {qeSelectList = [(Star, Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]
->          }) Nothing)
-
-
->     ,(TestStatement ansi2011
->       "create recursive view v as select * from t"
->      $ CreateView True [Name Nothing "v"] Nothing (makeSelect
->          {qeSelectList = [(Star, Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]
->          }) Nothing)
-
->     ,(TestStatement ansi2011
->       "create view v(a,b) as select * from t"
->      $ CreateView False [Name Nothing "v"] (Just [Name Nothing "a", Name Nothing "b"])
->          (makeSelect
->          {qeSelectList = [(Star, Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]
->          }) Nothing)
-
-
->     ,(TestStatement ansi2011
->       "create view v as select * from t with check option"
->      $ CreateView False [Name Nothing "v"] Nothing (makeSelect
->          {qeSelectList = [(Star, Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]
->          }) (Just DefaultCheckOption))
-
->     ,(TestStatement ansi2011
->       "create view v as select * from t with cascaded check option"
->      $ CreateView False [Name Nothing "v"] Nothing (makeSelect
->          {qeSelectList = [(Star, Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]
->          }) (Just CascadedCheckOption))
-
->     ,(TestStatement ansi2011
->       "create view v as select * from t with local check option"
->      $ CreateView False [Name Nothing "v"] Nothing
->          (makeSelect
->          {qeSelectList = [(Star, Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]
->          }) (Just LocalCheckOption))
-
-
-11.33 <drop view statement>
-
-<drop view statement> ::=
-  DROP VIEW <table name> <drop behavior>
-
-
->     ,(TestStatement ansi2011
->       "drop view v"
->      $ DropView [Name Nothing "v"] DefaultDropBehaviour)
-
->     ,(TestStatement ansi2011
->       "drop view v cascade"
->      $ DropView [Name Nothing "v"] Cascade)
-
-
-11.34 <domain definition>
-
-<domain definition> ::=
-  CREATE DOMAIN <domain name> [ AS ] <predefined type>
-      [ <default clause> ]
-      [ <domain constraint>... ]
-      [ <collate clause> ]
-
-<domain constraint> ::=
-  [ <constraint name definition> ] <check constraint definition> [
-      <constraint characteristics> ]
-
->     ,(TestStatement ansi2011
->       "create domain my_int int"
->      $ CreateDomain [Name Nothing "my_int"]
->           (TypeName [Name Nothing "int"])
->           Nothing [])
-
->     ,(TestStatement ansi2011
->       "create domain my_int as int"
->      $ CreateDomain [Name Nothing "my_int"]
->           (TypeName [Name Nothing "int"])
->           Nothing [])
-
->     ,(TestStatement ansi2011
->       "create domain my_int int default 0"
->      $ CreateDomain [Name Nothing "my_int"]
->           (TypeName [Name Nothing "int"])
->           (Just (NumLit "0")) [])
-
->     ,(TestStatement ansi2011
->       "create domain my_int int check (value > 5)"
->      $ CreateDomain [Name Nothing "my_int"]
->           (TypeName [Name Nothing "int"])
->           Nothing [(Nothing
->                    ,BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "5"))])
-
->     ,(TestStatement ansi2011
->       "create domain my_int int constraint gt5 check (value > 5)"
->      $ CreateDomain [Name Nothing "my_int"]
->           (TypeName [Name Nothing "int"])
->           Nothing [(Just [Name Nothing "gt5"]
->                    ,BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "5"))])
-
-
-
-11.35 <alter domain statement>
-
-<alter domain statement> ::=
-  ALTER DOMAIN <domain name> <alter domain action>
-
-<alter domain action> ::=
-    <set domain default clause>
-  | <drop domain default clause>
-  | <add domain constraint definition>
-  | <drop domain constraint definition>
-
-11.36 <set domain default clause>
-
-<set domain default clause> ::=
-  SET <default clause>
-
->     ,(TestStatement ansi2011
->       "alter domain my_int set default 0"
->      $ AlterDomain [Name Nothing "my_int"]
->        $ ADSetDefault $ NumLit "0")
-
-
-11.37 <drop domain default clause>
-
-<drop domain default clause> ::=
-  DROP DEFAULT
-
->     ,(TestStatement ansi2011
->       "alter domain my_int drop default"
->      $ AlterDomain [Name Nothing "my_int"]
->        $ ADDropDefault)
-
-
-11.38 <add domain constraint definition>
-
-<add domain constraint definition> ::=
-  ADD <domain constraint>
-
->     ,(TestStatement ansi2011
->       "alter domain my_int add check (value > 6)"
->      $ AlterDomain [Name Nothing "my_int"]
->        $ ADAddConstraint Nothing
->          $ BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "6"))
-
->     ,(TestStatement ansi2011
->       "alter domain my_int add constraint gt6 check (value > 6)"
->      $ AlterDomain [Name Nothing "my_int"]
->        $ ADAddConstraint (Just [Name Nothing "gt6"])
->          $ BinOp (Iden [Name Nothing "value"]) [Name Nothing ">"] (NumLit "6"))
-
-
-11.39 <drop domain constraint definition>
-
-<drop domain constraint definition> ::=
-  DROP CONSTRAINT <constraint name>
-
->     ,(TestStatement ansi2011
->       "alter domain my_int drop constraint gt6"
->      $ AlterDomain [Name Nothing "my_int"]
->        $ ADDropConstraint [Name Nothing "gt6"])
-
-11.40 <drop domain statement>
-
-<drop domain statement> ::=
-  DROP DOMAIN <domain name> <drop behavior>
-
->     ,(TestStatement ansi2011
->       "drop domain my_int"
->      $ DropDomain [Name Nothing "my_int"] DefaultDropBehaviour)
-
->     ,(TestStatement ansi2011
->       "drop domain my_int cascade"
->      $ DropDomain [Name Nothing "my_int"] Cascade)
-
-
-
-11.41 <character set definition>
-
-<character set definition> ::=
-  CREATE CHARACTER SET <character set name> [ AS ]
-      <character set source> [ <collate clause> ]
-
-<character set source> ::=
-  GET <character set specification>
-
-11.42 <drop character set statement>
-
-<drop character set statement> ::=
-  DROP CHARACTER SET <character set name>
-
-11.43 <collation definition>
-
-<collation definition> ::=
-  CREATE COLLATION <collation name> FOR <character set specification>
-      FROM <existing collation name> [ <pad characteristic> ]
-
-<existing collation name> ::=
-  <collation name>
-
-<pad characteristic> ::=
-    NO PAD
-  | PAD SPACE
-
-11.44 <drop collation statement>
-
-<drop collation statement> ::=
-  DROP COLLATION <collation name> <drop behavior>
-
-11.45 <transliteration definition>
-
-<transliteration definition> ::=
-  CREATE TRANSLATION <transliteration name> FOR <source character set specification>
-      TO <target character set specification> FROM <transliteration source>
-
-<source character set specification> ::=
-  <character set specification>
-
-<target character set specification> ::=
-  <character set specification>
-
-<transliteration source> ::=
-    <existing transliteration name>
-  | <transliteration routine>
-
-<existing transliteration name> ::=
-  <transliteration name>
-
-<transliteration routine> ::=
-  <specific routine designator>
-
-11.46 <drop transliteration statement>
-
-<drop transliteration statement> ::=
-  DROP TRANSLATION <transliteration name>
-
-11.47 <assertion definition>
-
-<assertion definition> ::=
-     CREATE ASSERTION <constraint name>
-         CHECK <left paren> <search condition> <right paren>
-         [ <constraint characteristics> ]
-
->     ,(TestStatement ansi2011
->       "create assertion t1_not_empty CHECK ((select count(*) from t1) > 0);"
->      $ CreateAssertion [Name Nothing "t1_not_empty"]
->         $ BinOp (SubQueryExpr SqSq $
->                  makeSelect
->                  {qeSelectList = [(App [Name Nothing "count"] [Star],Nothing)]
->                  ,qeFrom = [TRSimple [Name Nothing "t1"]]
->                  })
->                 [Name Nothing ">"] (NumLit "0"))
-
-11.48 <drop assertion statement>
-
-<drop assertion statement> ::=
-  DROP ASSERTION <constraint name> [ <drop behavior> ]
-
->     ,(TestStatement ansi2011
->       "drop assertion t1_not_empty;"
->      $ DropAssertion [Name Nothing "t1_not_empty"] DefaultDropBehaviour)
-
->     ,(TestStatement ansi2011
->       "drop assertion t1_not_empty cascade;"
->      $ DropAssertion [Name Nothing "t1_not_empty"] Cascade)
-
-
-11.49 <trigger definition>
-
-<trigger definition> ::=
-  CREATE TRIGGER <trigger name> <trigger action time> <trigger event>
-      ON <table name> [ REFERENCING <transition table or variable list> ]
-      <triggered action>
-
-<trigger action time> ::=
-    BEFORE
-  | AFTER
-  | INSTEAD OF
-
-<trigger event> ::=
-    INSERT
-  | DELETE
-  | UPDATE [ OF <trigger column list> ]
-
-<trigger column list> ::=
-  <column name list>
-
-<triggered action> ::=
-  [ FOR EACH { ROW | STATEMENT } ]
-      [ <triggered when clause> ]
-      <triggered SQL statement>
-
-<triggered when clause> ::=
-  WHEN <left paren> <search condition> <right paren>
-
-<triggered SQL statement> ::=
-    <SQL procedure statement>
-  | BEGIN ATOMIC { <SQL procedure statement> <semicolon> }... END
-
-<transition table or variable list> ::=
-  <transition table or variable>...
-
-<transition table or variable> ::=
-    OLD [ ROW ] [ AS     ] <old transition variable name>
-  | NEW [ ROW ] [ AS     ] <new transition variable name>
-  | OLD TABLE [ AS ]     <old transition table name>
-  | NEW TABLE [ AS ]     <new transition table name>
-
-<old transition table name> ::=
-  <transition table name>
-
-<new transition table name> ::=
-  <transition table name>
-
-<transition table name> ::=
-  <identifier>
-
-<old transition variable name> ::=
-  <correlation name>
-
-<new transition variable name> ::=
-  <correlation name>
-
-11.50 <drop trigger statement>
-
-<drop trigger statement> ::=
-  DROP TRIGGER <trigger name>
-
-11.51 <user-defined type definition>
-
-<user-defined type definition> ::=
-  CREATE TYPE <user-defined type body>
-
- <user-defined type body> ::=
-  <schema-resolved user-defined type name>
-      [ <subtype clause> ]
-      [ AS <representation> ]
-      [ <user-defined type option list> ]
-      [ <method specification list> ]
-
-<user-defined type option list> ::=
-  <user-defined type option> [ <user-defined type option>... ]
-
-<user-defined type option> ::=
-    <instantiable clause>
-  | <finality>
-  | <reference type specification>
-  | <cast to ref>
-  | <cast to type>
-  | <cast to distinct>
-  | <cast to source>
-
-<subtype clause> ::=
-  UNDER <supertype name>
-
-<supertype name> ::=
-  <path-resolved user-defined type name>
-
-<representation> ::=
-    <predefined type>
-  | <collection type>
-  | <member list>
-
-<member list> ::=
-  <left paren> <member> [ { <comma> <member> }... ] <right paren>
-
-<member> ::=
-  <attribute definition>
-
-<instantiable clause> ::=
-    INSTANTIABLE
-  | NOT INSTANTIABLE
-
-<finality> ::=
-    FINAL
-  | NOT FINAL
-
-<reference type specification> ::=
-    <user-defined representation>
-  | <derived representation>
-  | <system-generated representation>
-
-<user-defined representation> ::=
-  REF USING <predefined type>
-
-<derived representation> ::=
-  REF FROM <list of attributes>
-
-<system-generated representation> ::=
-  REF IS SYSTEM GENERATED
-
-<cast to ref> ::=
-  CAST <left paren> SOURCE AS REF <right paren> WITH <cast to ref identifier>
-
-<cast to ref identifier> ::=
-  <identifier>
-
-<cast to type> ::=
-  CAST <left paren> REF AS SOURCE <right paren> WITH <cast to type identifier>
-
-<cast to type identifier> ::=
-  <identifier>
-
-<list of attributes> ::=
-  <left paren> <attribute name> [ { <comma> <attribute name> }... ] <right paren>
-
-<cast to distinct> ::=
-  CAST <left paren> SOURCE AS DISTINCT <right paren>
-      WITH <cast to distinct identifier>
-
-<cast to distinct identifier> ::=
-  <identifier>
-
-<cast to source> ::=
-  CAST <left paren> DISTINCT AS SOURCE <right paren>
-      WITH <cast to source identifier>
-
-<cast to source identifier> ::=
-  <identifier>
-
-<method specification list> ::=
-  <method specification> [ { <comma> <method specification> }... ]
-
-<method specification> ::=
-    <original method specification>
-  | <overriding method specification>
-
-<original method specification> ::=
-  <partial method specification> [ SELF AS RESULT ] [ SELF AS LOCATOR ]
-      [ <method characteristics> ]
-
-<overriding method specification> ::=
-  OVERRIDING <partial method specification>
-                                                                                1<partial method specification> ::=
-  [ INSTANCE | STATIC | CONSTRUCTOR ]
-      METHOD <method name> <SQL parameter declaration list>
-      <returns clause>
-      [ SPECIFIC <specific method name> ]
-
-<specific method name> ::=
-  [ <schema name> <period> ] <qualified identifier>
-
-<method characteristics> ::=
-  <method characteristic>...
-
- <method characteristic> ::=
-         <language clause>
-     |   <parameter style clause>
-     |   <deterministic characteristic>
-     |   <SQL-data access indication>
-     |   <null-call clause>
-
-11.52 <attribute definition>
-
-<attribute definition> ::=
-     <attribute name> <data type>
-         [ <attribute default> ]
-         [ <collate clause> ]
-
-<attribute default> ::=
-  <default clause>
-
-11.53 <alter type statement>
-
-<alter type statement> ::=
-  ALTER TYPE <schema-resolved user-defined type name> <alter type action>
-
-<alter type action> ::=
-    <add attribute definition>
-  | <drop attribute definition>
-  | <add original method specification>
-  | <add overriding method specification>
-  | <drop method specification>
-
-11.54 <add attribute definition>
-
-<add attribute definition> ::=
-  ADD ATTRIBUTE <attribute definition>
-
-11.55 <drop attribute definition>
-
-<drop attribute definition> ::=
-  DROP ATTRIBUTE <attribute name> RESTRICT
-
-11.56 <add original method specification>
-
-<add original method specification> ::=
-  ADD <original method specification>
-
-11.57 <add overriding method specification>
-
-<add overriding method specification> ::=
-  ADD <overriding method specification>
-
-11.58 <drop method specification>
-
-<drop method specification> ::=
-  DROP <specific method specification designator> RESTRICT
-
-<specific method specification designator> ::=
-  [ INSTANCE | STATIC | CONSTRUCTOR ]
-      METHOD <method name> <data type list>
-
-11.59 <drop data type statement>
-
-<drop data type statement> ::=
-  DROP TYPE <schema-resolved user-defined type name> <drop behavior>
-
-11.60 <SQL-invoked routine>
-
-<SQL-invoked routine> ::=
-  <schema routine>
-
-<schema routine> ::=
-    <schema procedure>
-  | <schema function>
-
-<schema procedure> ::=
-  CREATE <SQL-invoked procedure>
-
-<schema function> ::=
-  CREATE <SQL-invoked function>
-
-<SQL-invoked procedure> ::=
-  PROCEDURE <schema qualified routine name> <SQL parameter declaration list>
-      <routine characteristics>
-      <routine body>
-
-<SQL-invoked function> ::=
-  { <function specification> | <method specification designator> } <routine body>
-
-<SQL parameter declaration list> ::=
-  <left paren> [ <SQL parameter declaration>
-      [ { <comma> <SQL parameter declaration> }... ] ] <right paren>
-
-<SQL parameter declaration> ::=
-  [ <parameter mode> ]
-      [ <SQL parameter name> ]
-      <parameter type> [ RESULT ]
-      [ DEFAULT <parameter default> ]
-
-<parameter default> ::=
-    <value expression>
-  | <contextually typed value specification>
-
-<parameter mode> ::=
-    IN
-  | OUT
-  | INOUT
-
-<parameter type> ::=
-  <data type> [ <locator indication> ]
-
-<locator indication> ::=
-  AS LOCATOR
-
-<function specification> ::=
-  FUNCTION <schema qualified routine name> <SQL parameter declaration list>
-      <returns clause>
-      <routine characteristics>
-      [ <dispatch clause> ]
-
-<method specification designator> ::=
-    SPECIFIC METHOD <specific method name>
-  | [ INSTANCE | STATIC | CONSTRUCTOR ]
-      METHOD <method name> <SQL parameter declaration list>
-      [ <returns clause> ]
-      FOR <schema-resolved user-defined type name>
-
-<routine characteristics> ::=
-  [ <routine characteristic>... ]
-
-<routine characteristic> ::=
-    <language clause>
-  | <parameter style clause>
-  | SPECIFIC <specific name>
-  | <deterministic characteristic>
-  | <SQL-data access indication>
-  | <null-call clause>
-  | <returned result sets characteristic>
-  | <savepoint level indication>
-
-<savepoint level indication> ::=
-    NEW SAVEPOINT LEVEL
-  | OLD SAVEPOINT LEVEL
-
-<returned result sets characteristic> ::=
-  DYNAMIC RESULT SETS <maximum returned result sets>
-
-<parameter style clause> ::=
-  PARAMETER STYLE <parameter style>
-
-<dispatch clause> ::=
-  STATIC DISPATCH
-
-<returns clause> ::=
-  RETURNS <returns type>
-
-<returns type> ::=
-    <returns data type> [ <result cast> ]
-  | <returns table type>
-
-<returns table type> ::=
-  TABLE <table function column list>
-
-<table function column list> ::=
-  <left paren> <table function column list element>
-      [ { <comma> <table function column list element> }... ] <right paren>
-
-<table function column list element> ::=
-  <column name> <data type>
-
-<result cast> ::=
-  CAST FROM <result cast from type>
-
-<result cast from type> ::=
-  <data type> [ <locator indication> ]
-
-<returns data type> ::=
-  <data type> [ <locator indication> ]
-
-<routine body> ::=
-    <SQL routine spec>
-  | <external body reference>
-
-<SQL routine spec> ::=
-  [ <rights clause> ] <SQL routine body>
-
-<rights clause> ::=
-    SQL SECURITY INVOKER
-  | SQL SECURITY DEFINER
-
-<SQL routine body> ::=
-  <SQL procedure statement>
-
-<external body reference> ::=
-  EXTERNAL [ NAME <external routine name> ]
-      [ <parameter style clause> ]
-      [ <transform group specification> ]
-      [ <external security clause> ]
-
-<external security clause> ::=
-    EXTERNAL SECURITY DEFINER
-  | EXTERNAL SECURITY INVOKER
-  | EXTERNAL SECURITY IMPLEMENTATION DEFINED
-
-<parameter style> ::=
-    SQL
-  | GENERAL
-
-<deterministic characteristic> ::=
-    DETERMINISTIC
-  | NOT DETERMINISTIC
-
-<SQL-data access indication> ::=
-    NO SQL
-  | CONTAINS SQL
-  | READS SQL DATA
-  | MODIFIES SQL DATA
-
-<null-call clause> ::=
-    RETURNS NULL ON NULL INPUT
-  | CALLED ON NULL INPUT
-
-<maximum returned result sets> ::=
-  <unsigned integer>
-
-<transform group specification> ::=
-  TRANSFORM GROUP { <single group specification> | <multiple group specification> }
-
-<single group specification> ::=
-  <group name>
-
-<multiple group specification> ::=
-  <group specification> [ { <comma> <group specification> }... ]
-
-<group specification> ::=
-  <group name> FOR TYPE <path-resolved user-defined type name>
-
-11.61 <alter routine statement>
-
-<alter routine statement> ::=
-  ALTER <specific routine designator>
-      <alter routine characteristics> <alter routine behavior>
-
-<alter routine characteristics> ::=
-  <alter routine characteristic>...
-
-<alter routine characteristic> ::=
-    <language clause>
-  | <parameter style clause>
-  | <SQL-data access indication>
-  | <null-call clause>
-  | <returned result sets characteristic>
-  | NAME <external routine name>
-
-<alter routine behavior> ::=
-  RESTRICT
-
-11.62 <drop routine statement>
-
-<drop routine statement> ::=
-  DROP <specific routine designator> <drop behavior>
-
-11.63 <user-defined cast definition>
-
-<user-defined cast definition> ::=
-  CREATE CAST <left paren> <source data type> AS <target data type> <right paren>
-      WITH <cast function>
-      [ AS ASSIGNMENT ]
-
-<cast function> ::=
-  <specific routine designator>
-
-<source data type> ::=
-  <data type>
-
-<target data type> ::=
-  <data type>
-
-11.64 <drop user-defined cast statement>
-
-<drop user-defined cast statement> ::=
-  DROP CAST <left paren> <source data type> AS <target data type> <right paren>
-      <drop behavior>
-
-11.65 <user-defined ordering definition>
-
-<user-defined ordering definition> ::=
-  CREATE ORDERING FOR <schema-resolved user-defined type name> <ordering form>
-
-<ordering form> ::=
-    <equals ordering form>
-  | <full ordering form>
-
-<equals ordering form> ::=
-  EQUALS ONLY BY <ordering category>
-
-<full ordering form> ::=
-  ORDER FULL BY <ordering category>
-
-<ordering category> ::=
-    <relative category>
-  | <map category>
-  | <state category>
-
-<relative category> ::=
-  RELATIVE WITH <relative function specification>
-
-<map category> ::=
-  MAP WITH <map function specification>
-
-<state category> ::=
-  STATE [ <specific name> ]
-
-<relative function specification> ::=
-  <specific routine designator>
-
-<map function specification> ::=
-  <specific routine designator>
-
-11.66 <drop user-defined ordering statement>
-
-<drop user-defined ordering statement> ::=
-  DROP ORDERING FOR <schema-resolved user-defined type name> <drop behavior>
-
-11.67 <transform definition>
-
-<transform definition> ::=
-  CREATE { TRANSFORM | TRANSFORMS } FOR
-      <schema-resolved user-defined type name> <transform group>...
-
-<transform group> ::=
-  <group name> <left paren> <transform element list> <right paren>
-
-<group name> ::=
-  <identifier>
-
-<transform element list> ::=
-  <transform element> [ <comma> <transform element> ]
-
-<transform element> ::=
-    <to sql>
-  | <from sql>
-
-<to sql> ::=
-  TO SQL WITH <to sql function>
-
-<from sql> ::=
-  FROM SQL WITH <from sql function>
-
-<to sql function> ::=
-  <specific routine designator>
-
-<from sql function> ::=
-  <specific routine designator>
-
-11.68 <alter transform statement>
-
-<alter transform statement> ::=
-  ALTER { TRANSFORM | TRANSFORMS }
-      FOR <schema-resolved user-defined type name> <alter group>...
-
-<alter group> ::=
-  <group name> <left paren> <alter transform action list> <right paren>
-
-<alter transform action list> ::=
-  <alter transform action> [ { <comma> <alter transform action> }... ]
-
-<alter transform action> ::=
-    <add transform element list>
-  | <drop transform element list>
-
-11.69 <add transform element list>
-
-<add transform element list> ::=
-  ADD <left paren> <transform element list> <right paren>
-
-11.70 <drop transform element list>
-
-<drop transform element list> ::=
-  DROP <left paren> <transform kind>
-      [ <comma> <transform kind> ] <drop behavior> <right paren>
-
-<transform kind> ::=
-    TO SQL
-  | FROM SQL
-
-11.71 <drop transform statement>
-
-<drop transform statement> ::=
-  DROP { TRANSFORM | TRANSFORMS } <transforms to be dropped>
-      FOR <schema-resolved user-defined type name> <drop behavior>
-
-<transforms to be dropped> ::=
-    ALL
-  | <transform group element>
-
-<transform group element> ::=
-  <group name>
-
-11.72 <sequence generator definition>
-
-<sequence generator definition> ::=
-  CREATE SEQUENCE <sequence generator name> [ <sequence generator options> ]
-
-<sequence generator options> ::=
-  <sequence generator option>...
-
-<sequence generator option> ::=
-    <sequence generator data type option>
-  | <common sequence generator options>
-
-<common sequence generator options> ::=
-  <common sequence generator option>...
-
-<common sequence generator option> ::=
-    <sequence generator start with option>
-  | <basic sequence generator option>
-
-<basic sequence generator option> ::=
-    <sequence generator increment by option>
-  | <sequence generator maxvalue option>
-  | <sequence generator minvalue option>
-  | <sequence generator cycle option>
-
-<sequence generator data type option> ::=
-  AS <data type>
-
-<sequence generator start with option> ::=
-  START WITH <sequence generator start value>
-
-<sequence generator start value> ::=
-  <signed numeric literal>
-
-<sequence generator increment by option> ::=
-  INCREMENT BY <sequence generator increment>
-
-<sequence generator increment> ::=
-  <signed numeric literal>
-
-<sequence generator maxvalue option> ::=
-    MAXVALUE <sequence generator max value>
-  | NO MAXVALUE
-
-<sequence generator max value> ::=
-  <signed numeric literal>
-
-<sequence generator minvalue option> ::=
-    MINVALUE <sequence generator min value>
-  | NO MINVALUE
-
-<sequence generator min value> ::=
-  <signed numeric literal>
-
-<sequence generator cycle option> ::=
-    CYCLE
-  | NO CYCLE
-
->     ,(TestStatement ansi2011
->       "create sequence seq"
->      $ CreateSequence [Name Nothing "seq"] [])
-
->     ,(TestStatement ansi2011
->       "create sequence seq as bigint"
->      $ CreateSequence [Name Nothing "seq"]
->         [SGODataType $ TypeName [Name Nothing "bigint"]])
-
->     ,(TestStatement ansi2011
->       "create sequence seq as bigint start with 5"
->      $ CreateSequence [Name Nothing "seq"]
->         [SGOStartWith 5
->         ,SGODataType $ TypeName [Name Nothing "bigint"]
->         ])
-
-
-11.73 <alter sequence generator statement>
-
-<alter sequence generator statement> ::=
-  ALTER SEQUENCE <sequence generator name> <alter sequence generator options>
-
-<alter sequence generator options> ::=
-  <alter sequence generator option>...
-
-<alter sequence generator option> ::=
-    <alter sequence generator restart option>
-  | <basic sequence generator option>
-
-<alter sequence generator restart option> ::=
-  RESTART [ WITH <sequence generator restart value> ]
-
-<sequence generator restart value> ::=
-  <signed numeric literal>
-
->     ,(TestStatement ansi2011
->       "alter sequence seq restart"
->      $ AlterSequence [Name Nothing "seq"]
->         [SGORestart Nothing])
-
->     ,(TestStatement ansi2011
->       "alter sequence seq restart with 5"
->      $ AlterSequence [Name Nothing "seq"]
->         [SGORestart $ Just 5])
-
->     ,(TestStatement ansi2011
->       "alter sequence seq restart with 5 increment by 5"
->      $ AlterSequence [Name Nothing "seq"]
->         [SGORestart $ Just 5
->         ,SGOIncrementBy 5])
-
-
-11.74 <drop sequence generator statement>
-
-<drop sequence generator statement> ::=
-  DROP SEQUENCE <sequence generator name> <drop behavior>
-
->     ,(TestStatement ansi2011
->       "drop sequence seq"
->      $ DropSequence [Name Nothing "seq"] DefaultDropBehaviour)
-
->     ,(TestStatement ansi2011
->       "drop sequence seq restrict"
->      $ DropSequence [Name Nothing "seq"] Restrict)
-
-
->     ]
diff --git a/tools/Language/SQL/SimpleSQL/ScalarExprs.lhs b/tools/Language/SQL/SimpleSQL/ScalarExprs.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/ScalarExprs.lhs
+++ /dev/null
@@ -1,417 +0,0 @@
-
-Tests for parsing scalar expressions
-
-> 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
->     ,parameter
->     ,dots
->     ,app
->     ,caseexp
->     ,operators
->     ,parens
->     ,subqueries
->     ,aggregates
->     ,windowFunctions
->     ,functionsWithReservedNames
->     ]
-
-> literals :: TestItem
-> literals = Group "literals" $ map (uncurry (TestScalarExpr ansi2011))
->     [("3", NumLit "3")
->      ,("3.", NumLit "3.")
->      ,("3.3", NumLit "3.3")
->      ,(".3", NumLit ".3")
->      ,("3.e3", NumLit "3.e3")
->      ,("3.3e3", NumLit "3.3e3")
->      ,(".3e3", NumLit ".3e3")
->      ,("3e3", NumLit "3e3")
->      ,("3e+3", NumLit "3e+3")
->      ,("3e-3", NumLit "3e-3")
->      ,("'string'", StringLit "'" "'" "string")
->      ,("'string with a '' quote'", StringLit "'" "'" "string with a '' quote")
->      ,("'1'", StringLit "'" "'" "1")
->      ,("interval '3' day"
->       ,IntervalLit Nothing "3" (Itf "day" Nothing) Nothing)
->      ,("interval '3' day (3)"
->       ,IntervalLit Nothing "3" (Itf "day" $ Just (3,Nothing)) Nothing)
->      ,("interval '3 weeks'", TypedLit (TypeName [Name Nothing "interval"]) "3 weeks")
->     ]
-
-> identifiers :: TestItem
-> identifiers = Group "identifiers" $ map (uncurry (TestScalarExpr ansi2011))
->     [("iden1", Iden [Name Nothing "iden1"])
->     --,("t.a", Iden2 "t" "a")
->     ,("\"quoted identifier\"", Iden [Name (Just ("\"","\"")) "quoted identifier"])
->     ,("\"from\"", Iden [Name (Just ("\"","\"")) "from"])
->     ]
-
-> star :: TestItem
-> star = Group "star" $ map (uncurry (TestScalarExpr ansi2011))
->     [("*", Star)
->     --,("t.*", Star2 "t")
->     --,("ROW(t.*,42)", App "ROW" [Star2 "t", NumLit "42"])
->     ]
-
-> parameter :: TestItem
-> parameter = Group "parameter"
->     [TestScalarExpr ansi2011 "?" Parameter
->     ,TestScalarExpr postgres "$13" $ PositionalArg 13]
-
-
-> dots :: TestItem
-> dots = Group "dot" $ map (uncurry (TestScalarExpr ansi2011))
->     [("t.a", Iden [Name Nothing "t",Name Nothing "a"])
->     ,("t.*", BinOp (Iden [Name Nothing "t"]) [Name Nothing "."] Star)
->     ,("a.b.c", Iden [Name Nothing "a",Name Nothing "b",Name Nothing "c"])
->     ,("ROW(t.*,42)", App [Name Nothing "ROW"] [BinOp (Iden [Name Nothing "t"]) [Name Nothing "."] Star, NumLit "42"])
->     ]
-
-> app :: TestItem
-> app = Group "app" $ map (uncurry (TestScalarExpr ansi2011))
->     [("f()", App [Name Nothing "f"] [])
->     ,("f(a)", App [Name Nothing "f"] [Iden [Name Nothing "a"]])
->     ,("f(a,b)", App [Name Nothing "f"] [Iden [Name Nothing "a"], Iden [Name Nothing "b"]])
->     ]
-
-> caseexp :: TestItem
-> caseexp = Group "caseexp" $ map (uncurry (TestScalarExpr ansi2011))
->     [("case a when 1 then 2 end"
->      ,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"]
->                               ,NumLit "2")] Nothing)
-
->     ,("case a when 1 then 2 when 3 then 4 end"
->      ,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"], NumLit "2")
->                              ,([NumLit "3"], NumLit "4")] Nothing)
-
->     ,("case a when 1 then 2 when 3 then 4 else 5 end"
->      ,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1"], NumLit "2")
->                              ,([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 [Name Nothing "a"]) [Name Nothing "="] (NumLit "1")], NumLit "2")
->                    ,([BinOp (Iden [Name Nothing "a"]) [Name Nothing "="] (NumLit "3")], NumLit "4")]
->                    (Just $ NumLit "5"))
-
->     ,("case a when 1,2 then 10 when 3,4 then 20 end"
->      ,Case (Just $ Iden [Name Nothing "a"]) [([NumLit "1",NumLit "2"]
->                                ,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 ansi2011))
->     [("a + b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"] (Iden [Name Nothing "b"]))
->      -- sanity check fixities
->      -- todo: add more fixity checking
-
->     ,("a + b * c"
->      ,BinOp  (Iden [Name Nothing "a"]) [Name Nothing "+"]
->              (BinOp (Iden [Name Nothing "b"]) [Name Nothing "*"] (Iden [Name Nothing "c"])))
-
->     ,("a * b + c"
->      ,BinOp (BinOp (Iden [Name Nothing "a"]) [Name Nothing "*"] (Iden [Name Nothing "b"]))
->             [Name Nothing "+"] (Iden [Name Nothing "c"]))
->     ]
-
-> unaryOperators :: TestItem
-> unaryOperators = Group "unaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
->     [("not a", PrefixOp [Name Nothing "not"] $ Iden [Name Nothing "a"])
->     ,("not not a", PrefixOp [Name Nothing "not"] $ PrefixOp [Name Nothing "not"] $ Iden [Name Nothing "a"])
->     ,("+a", PrefixOp [Name Nothing "+"] $ Iden [Name Nothing "a"])
->     ,("-a", PrefixOp [Name Nothing "-"] $ Iden [Name Nothing "a"])
->     ]
-
-
-> casts :: TestItem
-> casts = Group "operators" $ map (uncurry (TestScalarExpr ansi2011))
->     [("cast('1' as int)"
->      ,Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "int"])
-
->     ,("int '3'"
->      ,TypedLit (TypeName [Name Nothing "int"]) "3")
-
->     ,("cast('1' as double precision)"
->      ,Cast (StringLit "'" "'" "1") $ TypeName [Name Nothing "double precision"])
-
->     ,("cast('1' as float(8))"
->      ,Cast (StringLit "'" "'" "1") $ PrecTypeName [Name Nothing "float"] 8)
-
->     ,("cast('1' as decimal(15,2))"
->      ,Cast (StringLit "'" "'" "1") $ PrecScaleTypeName [Name Nothing "decimal"] 15 2)
-
-
->     ,("double precision '3'"
->      ,TypedLit (TypeName [Name Nothing "double precision"]) "3")
->     ]
-
-> subqueries :: TestItem
-> subqueries = Group "unaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
->     [("exists (select a from t)", SubQueryExpr SqExists ms)
->     ,("(select a from t)", SubQueryExpr SqSq ms)
-
->     ,("a in (select a from t)"
->      ,In True (Iden [Name Nothing "a"]) (InQueryExpr ms))
-
->     ,("a not in (select a from t)"
->      ,In False (Iden [Name Nothing "a"]) (InQueryExpr ms))
-
->     ,("a > all (select a from t)"
->      ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing ">"] CPAll ms)
-
->     ,("a = some (select a from t)"
->      ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "="] CPSome ms)
-
->     ,("a <= any (select a from t)"
->      ,QuantifiedComparison (Iden [Name Nothing "a"]) [Name Nothing "<="] CPAny ms)
->     ]
->   where
->     ms = makeSelect
->          {qeSelectList = [(Iden [Name Nothing "a"],Nothing)]
->          ,qeFrom = [TRSimple [Name Nothing "t"]]
->          }
-
-> miscOps :: TestItem
-> miscOps = Group "unaryOperators" $ map (uncurry (TestScalarExpr ansi2011))
->     [("a in (1,2,3)"
->      ,In True (Iden [Name Nothing "a"]) $ InList $ map NumLit ["1","2","3"])
-
->     ,("a is null", PostfixOp [Name Nothing "is null"] (Iden [Name Nothing "a"]))
->     ,("a is not null", PostfixOp [Name Nothing "is not null"] (Iden [Name Nothing "a"]))
->     ,("a is true", PostfixOp [Name Nothing "is true"] (Iden [Name Nothing "a"]))
->     ,("a is not true", PostfixOp [Name Nothing "is not true"] (Iden [Name Nothing "a"]))
->     ,("a is false", PostfixOp [Name Nothing "is false"] (Iden [Name Nothing "a"]))
->     ,("a is not false", PostfixOp [Name Nothing "is not false"] (Iden [Name Nothing "a"]))
->     ,("a is unknown", PostfixOp [Name Nothing "is unknown"] (Iden [Name Nothing "a"]))
->     ,("a is not unknown", PostfixOp [Name Nothing "is not unknown"] (Iden [Name Nothing "a"]))
->     ,("a is distinct from b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "is distinct from"] (Iden [Name Nothing "b"]))
-
->     ,("a is not distinct from b"
->      ,BinOp (Iden [Name Nothing "a"]) [Name Nothing "is not distinct from"] (Iden [Name Nothing "b"]))
-
->     ,("a like b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "like"] (Iden [Name Nothing "b"]))
->     ,("a not like b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "not like"] (Iden [Name Nothing "b"]))
->     ,("a is similar to b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "is similar to"] (Iden [Name Nothing "b"]))
-
->     ,("a is not similar to b"
->      ,BinOp (Iden [Name Nothing "a"]) [Name Nothing "is not similar to"] (Iden [Name Nothing "b"]))
-
->     ,("a overlaps b", BinOp (Iden [Name Nothing "a"]) [Name Nothing "overlaps"] (Iden [Name Nothing "b"]))
-
-
-special operators
-
->     ,("a between b and c", SpecialOp [Name Nothing "between"] [Iden [Name Nothing "a"]
->                                                ,Iden [Name Nothing "b"]
->                                                ,Iden [Name Nothing "c"]])
-
->     ,("a not between b and c", SpecialOp [Name Nothing "not between"] [Iden [Name Nothing "a"]
->                                                        ,Iden [Name Nothing "b"]
->                                                        ,Iden [Name Nothing "c"]])
->     ,("(1,2)"
->      ,SpecialOp [Name Nothing "rowctor"] [NumLit "1", NumLit "2"])
-
-
-keyword special operators
-
->     ,("extract(day from t)"
->      , SpecialOpK [Name Nothing "extract"] (Just $ Iden [Name Nothing "day"]) [("from", Iden [Name Nothing "t"])])
-
->     ,("substring(x from 1 for 2)"
->      ,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("from", NumLit "1")
->                                                ,("for", NumLit "2")])
-
->     ,("substring(x from 1)"
->      ,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("from", NumLit "1")])
-
->     ,("substring(x for 2)"
->      ,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"]) [("for", NumLit "2")])
-
->     ,("substring(x from 1 for 2 collate C)"
->      ,SpecialOpK [Name Nothing "substring"] (Just $ Iden [Name Nothing "x"])
->           [("from", NumLit "1")
->           ,("for", Collate (NumLit "2") [Name Nothing "C"])])
-
-this doesn't work because of a overlap in the 'in' parser
-
->     ,("POSITION( string1 IN string2 )"
->      ,SpecialOpK [Name Nothing "position"] (Just $ Iden [Name Nothing "string1"]) [("in", Iden [Name Nothing "string2"])])
-
->     ,("CONVERT(char_value USING conversion_char_name)"
->      ,SpecialOpK [Name Nothing "convert"] (Just $ Iden [Name Nothing "char_value"])
->           [("using", Iden [Name Nothing "conversion_char_name"])])
-
->     ,("TRANSLATE(char_value USING translation_name)"
->      ,SpecialOpK [Name Nothing "translate"] (Just $ Iden [Name Nothing "char_value"])
->           [("using", Iden [Name Nothing "translation_name"])])
-
-OVERLAY(string PLACING embedded_string FROM start
-[FOR length])
-
->     ,("OVERLAY(string PLACING embedded_string FROM start)"
->      ,SpecialOpK [Name Nothing "overlay"] (Just $ Iden [Name Nothing "string"])
->           [("placing", Iden [Name Nothing "embedded_string"])
->           ,("from", Iden [Name Nothing "start"])])
-
->     ,("OVERLAY(string PLACING embedded_string FROM start FOR length)"
->      ,SpecialOpK [Name Nothing "overlay"] (Just $ Iden [Name Nothing "string"])
->           [("placing", Iden [Name Nothing "embedded_string"])
->           ,("from", Iden [Name Nothing "start"])
->           ,("for", Iden [Name Nothing "length"])])
-
-TRIM( [ [{LEADING | TRAILING | BOTH}] [removal_char] FROM ]
-target_string
-[COLLATE collation_name] )
-
-
-
->     ,("trim(from target_string)"
->      ,SpecialOpK [Name Nothing "trim"] Nothing
->           [("both", StringLit "'" "'" " ")
->           ,("from", Iden [Name Nothing "target_string"])])
-
->     ,("trim(leading from target_string)"
->      ,SpecialOpK [Name Nothing "trim"] Nothing
->           [("leading", StringLit "'" "'" " ")
->           ,("from", Iden [Name Nothing "target_string"])])
-
->     ,("trim(trailing from target_string)"
->      ,SpecialOpK [Name Nothing "trim"] Nothing
->           [("trailing", StringLit "'" "'" " ")
->           ,("from", Iden [Name Nothing "target_string"])])
-
->     ,("trim(both from target_string)"
->      ,SpecialOpK [Name Nothing "trim"] Nothing
->           [("both", StringLit "'" "'" " ")
->           ,("from", Iden [Name Nothing "target_string"])])
-
-
->     ,("trim(leading 'x' from target_string)"
->      ,SpecialOpK [Name Nothing "trim"] Nothing
->           [("leading", StringLit "'" "'" "x")
->           ,("from", Iden [Name Nothing "target_string"])])
-
->     ,("trim(trailing 'y' from target_string)"
->      ,SpecialOpK [Name Nothing "trim"] Nothing
->           [("trailing", StringLit "'" "'" "y")
->           ,("from", Iden [Name Nothing "target_string"])])
-
->     ,("trim(both 'z' from target_string collate C)"
->      ,SpecialOpK [Name Nothing "trim"] Nothing
->           [("both", StringLit "'" "'" "z")
->           ,("from", Collate (Iden [Name Nothing "target_string"]) [Name Nothing "C"])])
-
->     ,("trim(leading from target_string)"
->      ,SpecialOpK [Name Nothing "trim"] Nothing
->           [("leading", StringLit "'" "'" " ")
->           ,("from", Iden [Name Nothing "target_string"])])
-
-
->     ]
-
-> aggregates :: TestItem
-> aggregates = Group "aggregates" $ map (uncurry (TestScalarExpr ansi2011))
->     [("count(*)",App [Name Nothing "count"] [Star])
-
->     ,("sum(a order by a)"
->     ,AggregateApp [Name Nothing "sum"] SQDefault [Iden [Name Nothing "a"]]
->                   [SortSpec (Iden [Name Nothing "a"]) DirDefault NullsOrderDefault] Nothing)
-
->     ,("sum(all a)"
->     ,AggregateApp [Name Nothing "sum"] All [Iden [Name Nothing "a"]] [] Nothing)
-
->     ,("count(distinct a)"
->     ,AggregateApp [Name Nothing "count"] Distinct [Iden [Name Nothing "a"]] [] Nothing)
->     ]
-
-> windowFunctions :: TestItem
-> windowFunctions = Group "windowFunctions" $ map (uncurry (TestScalarExpr ansi2011))
->     [("max(a) over ()", WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [] [] Nothing)
->     ,("count(*) over ()", WindowApp [Name Nothing "count"] [Star] [] [] Nothing)
-
->     ,("max(a) over (partition by b)"
->      ,WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]] [] Nothing)
-
->     ,("max(a) over (partition by b,c)"
->      ,WindowApp [Name Nothing "max"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"],Iden [Name Nothing "c"]] [] Nothing)
-
->     ,("sum(a) over (order by b)"
->      ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] []
->           [SortSpec (Iden [Name Nothing "b"]) DirDefault NullsOrderDefault] Nothing)
-
->     ,("sum(a) over (order by b desc,c)"
->      ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] []
->           [SortSpec (Iden [Name Nothing "b"]) Desc NullsOrderDefault
->           ,SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault] Nothing)
-
->     ,("sum(a) over (partition by b order by c)"
->      ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
->           [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault] Nothing)
-
->     ,("sum(a) over (partition by b order by c range unbounded preceding)"
->      ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
->       [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
->       $ Just $ FrameFrom FrameRange UnboundedPreceding)
-
->     ,("sum(a) over (partition by b order by c range 5 preceding)"
->      ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
->       [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
->       $ Just $ FrameFrom FrameRange $ Preceding (NumLit "5"))
-
->     ,("sum(a) over (partition by b order by c range current row)"
->      ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
->       [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
->       $ Just $ FrameFrom FrameRange Current)
-
->     ,("sum(a) over (partition by b order by c rows 5 following)"
->      ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
->       [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
->       $ Just $ FrameFrom FrameRows $ Following (NumLit "5"))
-
->     ,("sum(a) over (partition by b order by c range unbounded following)"
->      ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
->       [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
->       $ Just $ FrameFrom FrameRange UnboundedFollowing)
-
->     ,("sum(a) over (partition by b order by c \n\
->       \range between 5 preceding and 5 following)"
->      ,WindowApp [Name Nothing "sum"] [Iden [Name Nothing "a"]] [Iden [Name Nothing "b"]]
->       [SortSpec (Iden [Name Nothing "c"]) DirDefault NullsOrderDefault]
->       $ Just $ FrameBetween FrameRange
->                             (Preceding (NumLit "5"))
->                             (Following (NumLit "5")))
-
->     ]
-
-> parens :: TestItem
-> parens = Group "parens" $ map (uncurry (TestScalarExpr ansi2011))
->     [("(a)", Parens (Iden [Name Nothing "a"]))
->     ,("(a + b)", Parens (BinOp (Iden [Name Nothing "a"]) [Name Nothing "+"] (Iden [Name Nothing "b"])))
->     ]
-
-> functionsWithReservedNames :: TestItem
-> functionsWithReservedNames = Group "functionsWithReservedNames" $ map t
->     ["abs"
->     ,"char_length"
->     ]
->   where
->     t fn = TestScalarExpr ansi2011 (fn ++ "(a)") $ App [Name Nothing fn] [Iden [Name Nothing "a"]]
-
diff --git a/tools/Language/SQL/SimpleSQL/TableRefs.lhs b/tools/Language/SQL/SimpleSQL/TableRefs.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/TableRefs.lhs
+++ /dev/null
@@ -1,105 +0,0 @@
-
-These are the tests for parsing focusing on the from part of query
-expression
-
-> 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 ansi2011))
->     [("select a from t"
->      ,ms [TRSimple [Name Nothing "t"]])
-
->      ,("select a from f(a)"
->       ,ms [TRFunction [Name Nothing "f"] [Iden [Name Nothing "a"]]])
-
->     ,("select a from t,u"
->      ,ms [TRSimple [Name Nothing "t"], TRSimple [Name Nothing "u"]])
-
->     ,("select a from s.t"
->      ,ms [TRSimple [Name Nothing "s", Name Nothing "t"]])
-
-these lateral queries make no sense but the syntax is valid
-
->     ,("select a from lateral a"
->      ,ms [TRLateral $ TRSimple [Name Nothing "a"]])
-
->     ,("select a from lateral a,b"
->      ,ms [TRLateral $ TRSimple [Name Nothing "a"], TRSimple [Name Nothing "b"]])
-
->     ,("select a from a, lateral b"
->      ,ms [TRSimple [Name Nothing "a"], TRLateral $ TRSimple [Name Nothing "b"]])
-
->     ,("select a from a natural join lateral b"
->      ,ms [TRJoin (TRSimple [Name Nothing "a"]) True JInner
->                  (TRLateral $ TRSimple [Name Nothing "b"])
->                  Nothing])
-
->     ,("select a from lateral a natural join lateral b"
->      ,ms [TRJoin (TRLateral $ TRSimple [Name Nothing "a"]) True JInner
->                  (TRLateral $ TRSimple [Name Nothing "b"])
->                  Nothing])
-
-
->     ,("select a from t inner join u on expr"
->      ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
->                        (Just $ JoinOn $ Iden [Name Nothing "expr"])])
-
->     ,("select a from t join u on expr"
->      ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
->                        (Just $ JoinOn $ Iden [Name Nothing "expr"])])
-
->     ,("select a from t left join u on expr"
->      ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JLeft (TRSimple [Name Nothing "u"])
->                        (Just $ JoinOn $ Iden [Name Nothing "expr"])])
-
->     ,("select a from t right join u on expr"
->      ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JRight (TRSimple [Name Nothing "u"])
->                        (Just $ JoinOn $ Iden [Name Nothing "expr"])])
-
->     ,("select a from t full join u on expr"
->      ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JFull (TRSimple [Name Nothing "u"])
->                        (Just $ JoinOn $ Iden [Name Nothing "expr"])])
-
->     ,("select a from t cross join u"
->      ,ms [TRJoin (TRSimple [Name Nothing "t"]) False
->                        JCross (TRSimple [Name Nothing "u"]) Nothing])
-
->     ,("select a from t natural inner join u"
->      ,ms [TRJoin (TRSimple [Name Nothing "t"]) True JInner (TRSimple [Name Nothing "u"])
->                        Nothing])
-
->     ,("select a from t inner join u using(a,b)"
->      ,ms [TRJoin (TRSimple [Name Nothing "t"]) False JInner (TRSimple [Name Nothing "u"])
->                        (Just $ JoinUsing [Name Nothing "a", Name Nothing "b"])])
-
->     ,("select a from (select a from t)"
->      ,ms [TRQueryExpr $ ms [TRSimple [Name Nothing "t"]]])
-
->     ,("select a from t as u"
->      ,ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") Nothing)])
-
->     ,("select a from t u"
->      ,ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") Nothing)])
-
->     ,("select a from t u(b)"
->      ,ms [TRAlias (TRSimple [Name Nothing "t"]) (Alias (Name Nothing "u") $ Just [Name Nothing "b"])])
-
->     ,("select a from (t cross join u) as u"
->      ,ms [TRAlias (TRParens $
->                    TRJoin (TRSimple [Name Nothing "t"]) False JCross (TRSimple [Name Nothing "u"]) Nothing)
->                           (Alias (Name Nothing "u") Nothing)])
->      -- todo: not sure if the associativity is correct
-
->     ,("select a from t cross join u cross join v",
->        ms [TRJoin
->            (TRJoin (TRSimple [Name Nothing "t"]) False
->                    JCross (TRSimple [Name Nothing "u"]) Nothing)
->            False JCross (TRSimple [Name Nothing "v"]) Nothing])
->     ]
->   where
->     ms f = makeSelect {qeSelectList = [(Iden [Name Nothing "a"],Nothing)]
->                       ,qeFrom = f}
diff --git a/tools/Language/SQL/SimpleSQL/TestTypes.lhs b/tools/Language/SQL/SimpleSQL/TestTypes.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/TestTypes.lhs
+++ /dev/null
@@ -1,37 +0,0 @@
-
-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
->     (TestItem(..)
->     ,module Language.SQL.SimpleSQL.Dialect
->     ) where
-
-> import Language.SQL.SimpleSQL.Syntax
-> import Language.SQL.SimpleSQL.Lex (Token)
-> import Language.SQL.SimpleSQL.Dialect
-
-TODO: maybe make the dialect args into [dialect], then each test
-checks all the dialects mentioned work, and all the dialects not
-mentioned give a parse error. Not sure if this will be too awkward due
-to lots of tricky exceptions/variationsx.
-
-> data TestItem = Group String [TestItem]
->               | TestScalarExpr Dialect String ScalarExpr
->               | TestQueryExpr Dialect String QueryExpr
->               | TestStatement Dialect String Statement
->               | TestStatements Dialect String [Statement]
-
-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 Dialect String
-
-check that the string given fails to parse
-
->               | ParseQueryExprFails Dialect String
->               | ParseScalarExprFails Dialect String
->               | LexTest Dialect String [Token]
->               | LexFails Dialect String
->                 deriving (Eq,Show)
diff --git a/tools/Language/SQL/SimpleSQL/Tests.lhs b/tools/Language/SQL/SimpleSQL/Tests.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/Tests.lhs
+++ /dev/null
@@ -1,166 +0,0 @@
-
-This is the main tests module which exposes the test data plus the
-Test.Framework tests. It also contains the code which converts the
-test data to the Test.Framework tests.
-
-> module Language.SQL.SimpleSQL.Tests
->     (testData
->     ,tests
->     ,TestItem(..)
->     ) where
-
-> import qualified Test.Tasty as T
-> import qualified Test.Tasty.HUnit as H
-
-> --import Language.SQL.SimpleSQL.Syntax
-> import Language.SQL.SimpleSQL.Pretty
-> import Language.SQL.SimpleSQL.Parse
-> import Language.SQL.SimpleSQL.Lex
-
-> import Language.SQL.SimpleSQL.TestTypes
-
-> 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.Odbc
-> import Language.SQL.SimpleSQL.Tpch
-> import Language.SQL.SimpleSQL.LexerTests
-
-> import Language.SQL.SimpleSQL.SQL2011Queries
-> import Language.SQL.SimpleSQL.SQL2011AccessControl
-> import Language.SQL.SimpleSQL.SQL2011Bits
-> import Language.SQL.SimpleSQL.SQL2011DataManipulation
-> import Language.SQL.SimpleSQL.SQL2011Schema
-
-> import Language.SQL.SimpleSQL.MySQL
-> import Language.SQL.SimpleSQL.Oracle
-> import Language.SQL.SimpleSQL.CustomDialect
-
-Order the tests to start from the simplest first. This is also the
-order on the generated documentation.
-
-> testData :: TestItem
-> testData =
->     Group "parserTest"
->     [lexerTests
->     ,scalarExprTests
->     ,odbcTests
->     ,queryExprComponentTests
->     ,queryExprsTests
->     ,tableRefTests
->     ,groupByTests
->     ,fullQueriesTests
->     ,postgresTests
->     ,tpchTests
->     ,sql2011QueryTests
->     ,sql2011DataManipulationTests
->     ,sql2011SchemaTests
->     ,sql2011AccessControlTests
->     ,sql2011BitsTests
->     ,mySQLTests
->     ,oracleTests
->     ,customDialectTests
->     ]
-
-> tests :: T.TestTree
-> tests = itemToTest testData
-
-> --runTests :: IO ()
-> --runTests = void $ H.runTestTT $ itemToTest testData
-
-> itemToTest :: TestItem -> T.TestTree
-> itemToTest (Group nm ts) =
->     T.testGroup nm $ map itemToTest ts
-> itemToTest (TestScalarExpr d str expected) =
->     toTest parseScalarExpr prettyScalarExpr d str expected
-> itemToTest (TestQueryExpr d str expected) =
->     toTest parseQueryExpr prettyQueryExpr d str expected
-> itemToTest (TestStatement d str expected) =
->     toTest parseStatement prettyStatement d str expected
-> itemToTest (TestStatements d str expected) =
->     toTest parseStatements prettyStatements d str expected
-> itemToTest (ParseQueryExpr d str) =
->     toPTest parseQueryExpr prettyQueryExpr d str
-
-> itemToTest (ParseQueryExprFails d str) =
->     toFTest parseQueryExpr prettyQueryExpr d str
-
-> itemToTest (ParseScalarExprFails d str) =
->     toFTest parseScalarExpr prettyScalarExpr d str
-
-> itemToTest (LexTest d s ts) = makeLexerTest d s ts
-> itemToTest (LexFails d s) = makeLexingFailsTest d s
-
-> makeLexerTest :: Dialect -> String -> [Token] -> T.TestTree
-> makeLexerTest d s ts = H.testCase s $ do
->     let lx = either (error . show) id $ lexSQL d "" Nothing s
->     H.assertEqual "" ts $ map snd lx
->     let s' = prettyTokens d $ map snd lx
->     H.assertEqual "pretty print" s s'
-
-> makeLexingFailsTest :: Dialect -> String -> T.TestTree
-> makeLexingFailsTest d s = H.testCase s $ do
->     case lexSQL d "" Nothing s of
->          Right x -> H.assertFailure $ "lexing should have failed: " ++ s ++ "\ngot: " ++ show x
->          Left _ -> return ()
-
-
-> toTest :: (Eq a, Show a) =>
->           (Dialect -> String -> Maybe (Int,Int) -> String -> Either ParseError a)
->        -> (Dialect -> a -> String)
->        -> Dialect
->        -> String
->        -> a
->        -> T.TestTree
-> toTest parser pp d str expected = H.testCase str $ do
->         let egot = parser d "" Nothing str
->         case egot of
->             Left e -> H.assertFailure $ peFormattedError e
->             Right got -> do
->                 H.assertEqual "" expected got
->                 let str' = pp d got
->                 let egot' = parser d "" Nothing str'
->                 case egot' of
->                     Left e' -> H.assertFailure $ "pp roundtrip"
->                                                  ++ "\n" ++ str'
->                                                  ++ peFormattedError e'
->                     Right got' -> H.assertEqual
->                                   ("pp roundtrip" ++ "\n" ++ str')
->                                    expected got'
-
-> toPTest :: (Eq a, Show a) =>
->           (Dialect -> String -> Maybe (Int,Int) -> String -> Either ParseError a)
->        -> (Dialect -> a -> String)
->        -> Dialect
->        -> String
->        -> T.TestTree
-> toPTest parser pp d str = H.testCase str $ do
->         let egot = parser d "" Nothing str
->         case egot of
->             Left e -> H.assertFailure $ peFormattedError e
->             Right got -> do
->                 let str' = pp d got
->                 let egot' = parser d "" Nothing str'
->                 case egot' of
->                     Left e' -> H.assertFailure $ "pp roundtrip "
->                                                  ++ "\n" ++ str' ++ "\n"
->                                                  ++ peFormattedError e'
->                     Right _got' -> return ()
-
-
-> toFTest :: (Eq a, Show a) =>
->           (Dialect -> String -> Maybe (Int,Int) -> String -> Either ParseError a)
->        -> (Dialect -> a -> String)
->        -> Dialect
->        -> String
->        -> T.TestTree
-> toFTest parser _pp d str = H.testCase str $ do
->         let egot = parser d "" Nothing str
->         case egot of
->             Left _e -> return ()
->             Right _got ->
->               H.assertFailure $ "parse didn't fail: " ++ show d ++ "\n" ++ str
diff --git a/tools/Language/SQL/SimpleSQL/Tpch.lhs b/tools/Language/SQL/SimpleSQL/Tpch.lhs
deleted file mode 100644
--- a/tools/Language/SQL/SimpleSQL/Tpch.lhs
+++ /dev/null
@@ -1,683 +0,0 @@
-
-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 ansi2011 . 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/RunTests.lhs b/tools/RunTests.lhs
deleted file mode 100644
--- a/tools/RunTests.lhs
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-> import Test.Tasty
-
-> import Language.SQL.SimpleSQL.Tests
-
-> main :: IO ()
-> main = defaultMain tests
diff --git a/tools/SimpleSqlParserTool.lhs b/tools/SimpleSqlParserTool.lhs
deleted file mode 100644
--- a/tools/SimpleSqlParserTool.lhs
+++ /dev/null
@@ -1,93 +0,0 @@
-
-Simple command line tool to experiment with simple-sql-parser
-
-Commands:
-
-parse: parse sql from file, stdin or from command line
-lex: lex sql same
-indent: parse then pretty print sql
-
-> {-# LANGUAGE TupleSections #-}
-> import System.Environment
-> import Control.Monad
-> import Data.Maybe
-> import System.Exit
-> import Data.List
-> import Text.Show.Pretty
-> --import Control.Applicative
-
-> import Language.SQL.SimpleSQL.Pretty
-> import Language.SQL.SimpleSQL.Parse
-> import Language.SQL.SimpleSQL.Lex
-
-
-> main :: IO ()
-> main = do
->     args <- getArgs
->     case args of
->         [] -> do
->               showHelp $ Just "no command given"
->         (c:as) -> do
->              let cmd = lookup c commands
->              maybe (showHelp (Just "command not recognised"))
->                    (\(_,cmd') -> cmd' as)
->                    cmd
-
-> commands :: [(String, (String,[String] -> IO ()))]
-> commands =
->     [("help", helpCommand)
->     ,("parse", parseCommand)
->     ,("lex", lexCommand)
->     ,("indent", indentCommand)]
-
-> showHelp :: Maybe String -> IO ()
-> showHelp msg = do
->           maybe (return ()) (\e -> putStrLn $ "Error: " ++ e) msg
->           putStrLn "Usage:\n SimpleSqlParserTool command args"
->           forM_ commands $ \(c, (h,_)) -> do
->                putStrLn $ c ++ "\t" ++ h
->           when (isJust msg) $ exitFailure
-
-> helpCommand :: (String,[String] -> IO ())
-> helpCommand =
->     ("show help for this progam", \_ -> showHelp Nothing)
-
-> getInput :: [String] -> IO (FilePath,String)
-> getInput as =
->     case as of
->       ["-"] -> ("",) <$> getContents
->       ("-c":as') -> return ("", unwords as')
->       [filename] -> (filename,) <$> readFile filename
->       _ -> showHelp (Just "arguments not recognised") >> error ""
-
-> parseCommand :: (String,[String] -> IO ())
-> parseCommand =
->   ("parse SQL from file/stdin/command line (use -c to parse from command line)"
->   ,\args -> do
->       (f,src) <- getInput args
->       either (error . peFormattedError)
->           (putStrLn . ppShow)
->           $ parseStatements ansi2011 f Nothing src
->   )
-
-> lexCommand :: (String,[String] -> IO ())
-> lexCommand =
->   ("lex SQL from file/stdin/command line (use -c to parse from command line)"
->   ,\args -> do
->       (f,src) <- getInput args
->       either (error . peFormattedError)
->              (putStrLn . intercalate ",\n" . map show)
->              $ lexSQL ansi2011 f Nothing src
->   )
-
-
-> indentCommand :: (String,[String] -> IO ())
-> indentCommand =
->   ("parse then pretty print SQL from file/stdin/command line (use -c to parse from command line)"
->   ,\args -> do
->       (f,src) <- getInput args
->       either (error . peFormattedError)
->           (putStrLn . prettyStatements ansi2011)
->           $ parseStatements ansi2011 f Nothing src
-
->   )
