From a81f62b94074835684803b3096bedeca64fb2866 Mon Sep 17 00:00:00 2001 From: Jake Wheat Date: Fri, 13 Dec 2013 17:27:02 +0200 Subject: [PATCH] add support for limit and offset --- Language/SQL/SimpleSQL/Parser.lhs | 30 +++++++++++++++++++++++------ Language/SQL/SimpleSQL/Pretty.lhs | 26 +++++++++++++------------ Language/SQL/SimpleSQL/Syntax.lhs | 14 +++++++++++--- TODO | 17 ++++++++-------- Tests.lhs | 32 ++++++++++++++++++++++++++++++- 5 files changed, 88 insertions(+), 31 deletions(-) diff --git a/Language/SQL/SimpleSQL/Parser.lhs b/Language/SQL/SimpleSQL/Parser.lhs index e79b6e0..7a4c69c 100644 --- a/Language/SQL/SimpleSQL/Parser.lhs +++ b/Language/SQL/SimpleSQL/Parser.lhs @@ -73,7 +73,8 @@ digitse[+-]digits > blacklist :: [String] > blacklist = ["as", "from", "where", "having", "group", "order" > ,"inner", "left", "right", "full", "natural", "join" -> ,"on", "using", "when", "then", "case", "end", "order"] +> ,"on", "using", "when", "then", "case", "end", "order" +> ,"limit", "offset"] TODO: talk about what must be in the blacklist, and what doesn't need to be. @@ -209,12 +210,16 @@ to be. = query expressions +> duplicates :: P Duplicates +> duplicates = option All $ try $ choice [All <$ keyword_ "all" +> ,Distinct <$ keyword "distinct"] + > selectItem :: P (Maybe String, ScalarExpr) > selectItem = flip (,) <$> scalarExpr <*> optionMaybe (try alias) > where alias = optional (try (keyword_ "as")) *> identifierString > selectList :: P [(Maybe String,ScalarExpr)] -> selectList = try (keyword_ "select") *> commaSep1 selectItem +> selectList = commaSep1 selectItem > from :: P [TableRef] > from = option [] (try (keyword_ "from") *> commaSep1 tref) @@ -256,8 +261,11 @@ to be. > alias j = let a1 = optional (try (keyword_ "as")) *> identifierString > in option j (JoinAlias j <$> try a1) +> optionalScalarExpr :: String -> P (Maybe ScalarExpr) +> optionalScalarExpr k = optionMaybe (try (keyword_ k) *> scalarExpr) + > swhere :: P (Maybe ScalarExpr) -> swhere = optionMaybe (try (keyword_ "where") *> scalarExpr) +> swhere = optionalScalarExpr "where" > sgroupBy :: P [ScalarExpr] > sgroupBy = option [] (try (keyword_ "group") @@ -265,7 +273,7 @@ to be. > *> commaSep1 scalarExpr) > having :: P (Maybe ScalarExpr) -> having = optionMaybe (try (keyword_ "having") *> scalarExpr) +> having = optionalScalarExpr "having" > orderBy :: P [(ScalarExpr,Direction)] > orderBy = option [] (try (keyword_ "order") @@ -276,16 +284,26 @@ to be. > <*> option Asc (choice [Asc <$ keyword_ "asc" > ,Desc <$ keyword_ "desc"]) +> limit :: P (Maybe ScalarExpr) +> limit = optionalScalarExpr "limit" + +> offset :: P (Maybe ScalarExpr) +> offset = optionalScalarExpr "offset" + + > queryExpr :: P QueryExpr > queryExpr = +> try (keyword_ "select") >> > Select -> <$> selectList +> <$> duplicates +> <*> selectList > <*> from > <*> swhere > <*> sgroupBy > <*> having > <*> orderBy - +> <*> limit +> <*> offset ------------------------------------------------ diff --git a/Language/SQL/SimpleSQL/Pretty.lhs b/Language/SQL/SimpleSQL/Pretty.lhs index 84fd064..68d21d3 100644 --- a/Language/SQL/SimpleSQL/Pretty.lhs +++ b/Language/SQL/SimpleSQL/Pretty.lhs @@ -51,14 +51,20 @@ back into SQL source text. It attempts to format the output nicely. = query expressions > queryExpr :: QueryExpr -> Doc -> queryExpr (Select sl fr wh gb hv od) = +> queryExpr (Select d sl fr wh gb hv od lm off) = > sep [text "select" +> ,case d of +> All -> empty +> Distinct -> text "distinct" > ,nest 4 $ sep [selectList sl] > ,from fr -> ,whr wh +> ,maybeScalarExpr "where" wh > ,grpBy gb -> ,having hv -> ,orderBy od] +> ,maybeScalarExpr "having" hv +> ,orderBy od +> ,maybeScalarExpr "limit" lm +> ,maybeScalarExpr "offset" off +> ] > selectList :: [(Maybe String, ScalarExpr)] -> Doc > selectList is = commaSep $ map si is @@ -97,20 +103,16 @@ back into SQL source text. It attempts to format the output nicely. > joinCond Nothing = empty > joinCond (Just JoinNatural) = empty -> whr :: Maybe ScalarExpr -> Doc -> whr = maybe empty -> (\w -> sep [text "where" -> ,nest 4 $ scalarExpr w]) +> maybeScalarExpr :: String -> Maybe ScalarExpr -> Doc +> maybeScalarExpr k = maybe empty +> (\e -> sep [text k +> ,nest 4 $ scalarExpr e]) > grpBy :: [ScalarExpr] -> Doc > grpBy [] = empty > grpBy gs = sep [text "group by" > ,nest 4 $ commaSep $ map scalarExpr gs] -> having :: Maybe ScalarExpr -> Doc -> having = maybe empty -> (\w -> sep [text "having" -> ,nest 4 $ scalarExpr w]) > orderBy :: [(ScalarExpr,Direction)] -> Doc > orderBy [] = empty > orderBy os = sep [text "order by" diff --git a/Language/SQL/SimpleSQL/Syntax.lhs b/Language/SQL/SimpleSQL/Syntax.lhs index dc100e4..3d6944d 100644 --- a/Language/SQL/SimpleSQL/Syntax.lhs +++ b/Language/SQL/SimpleSQL/Syntax.lhs @@ -6,6 +6,7 @@ > ,TableRef(..) > ,JoinType(..) > ,JoinCondition(..) +> ,Duplicates(..) > ,Direction(..) > ) where @@ -26,23 +27,30 @@ > data QueryExpr > = Select -> {qeSelectList :: [(Maybe String,ScalarExpr)] +> {qeDuplicates :: Duplicates +> ,qeSelectList :: [(Maybe String,ScalarExpr)] > ,qeFrom :: [TableRef] > ,qeWhere :: Maybe ScalarExpr > ,qeGroupBy :: [ScalarExpr] > ,qeHaving :: Maybe ScalarExpr > ,qeOrderBy :: [(ScalarExpr,Direction)] +> ,qeLimit :: Maybe ScalarExpr +> ,qeOffset :: Maybe ScalarExpr > } deriving (Eq,Show) +> data Duplicates = Distinct | All deriving (Eq,Show) > data Direction = Asc | Desc deriving (Eq,Show) > makeSelect :: QueryExpr -> makeSelect = Select {qeSelectList = [] +> makeSelect = Select {qeDuplicates = All +> ,qeSelectList = [] > ,qeFrom = [] > ,qeWhere = Nothing > ,qeGroupBy = [] > ,qeHaving = Nothing -> ,qeOrderBy = []} +> ,qeOrderBy = [] +> ,qeLimit = Nothing +> ,qeOffset = Nothing} > data TableRef = SimpleTableRef String diff --git a/TODO b/TODO index 3a39dc9..6ddf73b 100644 --- a/TODO +++ b/TODO @@ -22,12 +22,7 @@ emacs parse error formatting = sql support -distinct/all -limit,offset, top - scalar function syntax: - aggregate app - window app cast exists, in subquery scalar subquery @@ -35,16 +30,20 @@ scalar function syntax: any/some/all between is null/ is not null - review all ansi sql operators interval literal - typed string lit? + typed string lit + + aggregate app + window app + review all ansi sql operators placeholder/positional arg other missing operators - unary + - - row constructors? extract substring + unary + - + + row constructors? except, intersect, union diff --git a/Tests.lhs b/Tests.lhs index 6dc992b..840b54a 100644 --- a/Tests.lhs +++ b/Tests.lhs @@ -89,15 +89,31 @@ > queryExprParserTests :: TestItem > queryExprParserTests = Group "queryExprParserTests" -> [selectLists +> [duplicates +> ,selectLists > ,from > ,whereClause > ,groupByClause > ,having > ,orderBy +> ,limit > ,fullQueries > ] + + +> duplicates :: TestItem +> duplicates = Group "duplicates" $ map (uncurry TestQueryExpr) +> [("select a from t" ,ms All) +> ,("select all a from t" ,ms All) +> ,("select distinct a from t", ms Distinct) +> ] +> where +> ms d = makeSelect +> {qeDuplicates = d +> ,qeSelectList = [(Nothing,Iden "a")] +> ,qeFrom = [SimpleTableRef "t"]} + > selectLists :: TestItem > selectLists = Group "selectLists" $ map (uncurry TestQueryExpr) > [("select 1", @@ -212,6 +228,20 @@ > ,qeFrom = [SimpleTableRef "t"] > ,qeOrderBy = o} +> limit :: TestItem +> limit = Group "limit" $ map (uncurry TestQueryExpr) +> [("select a from t limit 10" +> ,ms (Just $ NumLit "10") Nothing) +> ,("select a from t limit 10 offset 10" +> ,ms (Just $ NumLit "10") (Just $ NumLit "10")) +> ] +> where +> ms l o = makeSelect +> {qeSelectList = [(Nothing,Iden "a")] +> ,qeFrom = [SimpleTableRef "t"] +> ,qeLimit = l +> ,qeOffset = o} + > fullQueries :: TestItem > fullQueries = Group "queries" $ map (uncurry TestQueryExpr) > [("select count(*) from t"