diff --git a/Language/SQL/SimpleSQL/Parser.lhs b/Language/SQL/SimpleSQL/Parser.lhs
index dd56105..5a82efb 100644
--- a/Language/SQL/SimpleSQL/Parser.lhs
+++ b/Language/SQL/SimpleSQL/Parser.lhs
@@ -12,6 +12,7 @@
 > import Data.Char
 > import Text.Parsec hiding (ParseError)
 > import qualified Text.Parsec as P
+> import Text.Parsec.Perm
 
 > import Language.SQL.SimpleSQL.Syntax
 > import Language.SQL.SimpleSQL.Fixity
@@ -572,11 +573,26 @@ where, having, limit, offset).
 >              <*> option Asc (choice [Asc <$ keyword_ "asc"
 >                                     ,Desc <$ keyword_ "desc"])
 
-> limit :: P ScalarExpr
-> limit = keywordScalarExpr "limit"
+allows offset and fetch in either order
++ postgresql offset without row(s) and limit instead of fetch also
+
+> offsetFetch :: P (Maybe ScalarExpr, Maybe ScalarExpr)
+> offsetFetch = permute ((,) <$?> (Nothing, Just <$> offset)
+>                            <|?> (Nothing, Just <$> fetch))
 
 > offset :: P ScalarExpr
-> offset = keywordScalarExpr "offset"
+> offset = try (keyword_ "offset") *> scalarExpr
+>          <* option () (try $ choice [try (keyword_ "rows"),keyword_ "row"])
+
+> fetch :: P ScalarExpr
+> fetch = choice [ansiFetch, limit]
+>   where
+>     ansiFetch = try (keyword_ "fetch") >>
+>         choice [keyword_ "first",keyword_ "next"]
+>         *> scalarExpr
+>         <* choice [keyword_ "rows",keyword_ "row"]
+>         <* keyword_ "only"
+>     limit = try (keyword_ "limit") *> scalarExpr
 
 == common table expressions
 
@@ -601,7 +617,7 @@ and union, etc..
 >           >>= optionSuffix queryExprSuffix]
 >   where
 >     select = try (keyword_ "select") >>
->         Select
+>         mkSelect
 >         <$> (fromMaybe All <$> duplicates)
 >         <*> selectList
 >         <*> option [] from
@@ -609,8 +625,9 @@ and union, etc..
 >         <*> option [] sgroupBy
 >         <*> optionMaybe having
 >         <*> option [] orderBy
->         <*> optionMaybe limit
->         <*> optionMaybe offset
+>         <*> offsetFetch
+>     mkSelect d sl f w g h od (ofs,fe) =
+>         Select d sl f w g h od ofs fe
 >     values = try (keyword_ "values")
 >              >> Values <$> commaSep (parens (commaSep scalarExpr))
 >     table = try (keyword_ "table") >> Table <$> name
@@ -698,7 +715,7 @@ keyword parser also
 > blacklist :: [String]
 > blacklist =
 >     ["select", "as", "from", "where", "having", "group", "order"
->     ,"limit", "offset"
+>     ,"limit", "offset", "fetch"
 >     ,"inner", "left", "right", "full", "natural", "join"
 >     ,"cross", "on", "using", "lateral"
 >     ,"when", "then", "case", "end", "in"
diff --git a/Language/SQL/SimpleSQL/Pretty.lhs b/Language/SQL/SimpleSQL/Pretty.lhs
index ebb7a7f..bc40e19 100644
--- a/Language/SQL/SimpleSQL/Pretty.lhs
+++ b/Language/SQL/SimpleSQL/Pretty.lhs
@@ -149,7 +149,7 @@
 = query expressions
 
 > queryExpr :: QueryExpr -> Doc
-> queryExpr (Select d sl fr wh gb hv od lm off) =
+> queryExpr (Select d sl fr wh gb hv od off fe) =
 >   sep [text "select"
 >       ,case d of
 >           All -> empty
@@ -160,8 +160,9 @@
 >       ,grpBy gb
 >       ,maybeScalarExpr "having" hv
 >       ,orderBy od
->       ,maybeScalarExpr "limit" lm
->       ,maybeScalarExpr "offset" off
+>       ,maybe empty (\e -> text "offset" <+> scalarExpr e <+> text "rows") off
+>       ,maybe empty (\e -> text "fetch next" <+> scalarExpr e
+>                           <+> text "rows only") fe
 >       ]
 > queryExpr (CombineQueryExpr q1 ct d c q2) =
 >   sep [queryExpr q1
diff --git a/Language/SQL/SimpleSQL/Syntax.lhs b/Language/SQL/SimpleSQL/Syntax.lhs
index 1734346..862a16b 100644
--- a/Language/SQL/SimpleSQL/Syntax.lhs
+++ b/Language/SQL/SimpleSQL/Syntax.lhs
@@ -145,8 +145,8 @@
 >       ,qeGroupBy :: [ScalarExpr]
 >       ,qeHaving :: Maybe ScalarExpr
 >       ,qeOrderBy :: [(ScalarExpr,Direction)]
->       ,qeLimit :: Maybe ScalarExpr
 >       ,qeOffset :: Maybe ScalarExpr
+>       ,qeFetch :: Maybe ScalarExpr
 >       }
 >     | CombineQueryExpr
 >       {qe0 :: QueryExpr
@@ -177,8 +177,8 @@ I'm not sure if this is valid syntax or not.
 >                     ,qeGroupBy = []
 >                     ,qeHaving = Nothing
 >                     ,qeOrderBy = []
->                     ,qeLimit = Nothing
->                     ,qeOffset = Nothing}
+>                     ,qeOffset = Nothing
+>                     ,qeFetch = Nothing}
 
 
 > -- | represents the Distinct or All keywords, which can be used
diff --git a/TODO b/TODO
index b2ed8c7..1c114f2 100644
--- a/TODO
+++ b/TODO
@@ -1,33 +1,17 @@
 
 next release:
 
-ansi standard versions of limit and offset
-
-OFFSET start { ROW | ROWS }
-FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY
--> + fix the abstract syntax to match this instead of postgres
-(keep the postgres syntax version parser)
-in the postgresql docs, the start and count must be in parens unless
-   they are a single integer
-
-select * from generate_series(0,99) offset 5 fetch next 5 row only;
-select * from generate_series(0,99) offset 5;
-select * from generate_series(0,99) fetch next 5 row only;
-
-+ sql server top syntax
-
-more dots: implement as dot operator
 
 more symbolic operators, array access a[5]? don't think this is
    standard sql, if not, leave for now. There is something about
    arrays in sql:2008
 
+row ctor: row(a,b) is fine, but also when there is 2 or more elements,
+   the word row can be omitted: (a,b)
 
 
 fix lateral binding issue
 
-row ctor: row(a,b) is fine, but also when there is 2 or more elements,
-   the word row can be omitted: (a,b)
 
 window frames and named windows
 
@@ -74,6 +58,8 @@ review abstract syntax (e.g. combine App with SpecialOp?)
 
 Later general tasks:
 
+sql server top syntax
+
 extended string literals, escapes and other flavours (like pg and
    oracle custom delimiters)
 
diff --git a/tools/Language/SQL/SimpleSQL/QueryExprComponents.lhs b/tools/Language/SQL/SimpleSQL/QueryExprComponents.lhs
index 452da03..a7f4dfe 100644
--- a/tools/Language/SQL/SimpleSQL/QueryExprComponents.lhs
+++ b/tools/Language/SQL/SimpleSQL/QueryExprComponents.lhs
@@ -126,18 +126,22 @@ These are a few misc tests which don't fit anywhere else.
 
 > 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"))
+>     [-- 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"))
 >     ]
 >   where
->     ms l o = makeSelect
+>     ms o l = makeSelect
 >              {qeSelectList = [(Nothing,Iden "a")]
 >              ,qeFrom = [TRSimple "t"]
->              ,qeLimit = l
->              ,qeOffset = o}
+>              ,qeOffset = o
+>              ,qeFetch = l}
 
 > combos :: TestItem
 > combos = Group "combos" $ map (uncurry TestQueryExpr)