diff --git a/pypika/dialects.py b/pypika/dialects.py index 8894e562..76535777 100644 --- a/pypika/dialects.py +++ b/pypika/dialects.py @@ -1,4 +1,5 @@ import itertools +import warnings from copy import copy from typing import Any, Optional, Union, Tuple as TypedTuple @@ -347,6 +348,19 @@ def __str__(self) -> str: return self.get_sql() +class FetchNextAndOffsetRowsQueryBuilder(QueryBuilder): + def _limit_sql(self) -> str: + return " FETCH NEXT {limit} ROWS ONLY".format(limit=self._limit) + + def _offset_sql(self) -> str: + return " OFFSET {offset} ROWS".format(offset=self._offset or 0) + + @builder + def fetch_next(self, limit: int): + warnings.warn("`fetch_next` is deprecated - please use the `limit` method", DeprecationWarning) + self._limit = limit + + class OracleQuery(Query): """ Defines a query class for use with Oracle. @@ -357,7 +371,7 @@ def _builder(cls, **kwargs: Any) -> "OracleQueryBuilder": return OracleQueryBuilder(**kwargs) -class OracleQueryBuilder(QueryBuilder): +class OracleQueryBuilder(FetchNextAndOffsetRowsQueryBuilder): QUOTE_CHAR = None QUERY_CLS = OracleQuery @@ -370,6 +384,16 @@ def get_sql(self, *args: Any, **kwargs: Any) -> str: kwargs['groupby_alias'] = False return super().get_sql(*args, **kwargs) + def _apply_pagination(self, querystring: str) -> str: + # Note: Overridden as Oracle specifies offset before the fetch next limit + if self._offset: + querystring += self._offset_sql() + + if self._limit is not None: + querystring += self._limit_sql() + + return querystring + class PostgreSQLQuery(Query): """ @@ -670,7 +694,7 @@ def _builder(cls, **kwargs: Any) -> "MSSQLQueryBuilder": return MSSQLQueryBuilder(**kwargs) -class MSSQLQueryBuilder(QueryBuilder): +class MSSQLQueryBuilder(FetchNextAndOffsetRowsQueryBuilder): QUERY_CLS = MSSQLQuery def __init__(self, **kwargs: Any) -> None: @@ -695,17 +719,6 @@ def top(self, value: Union[str, int], percent: bool = False, with_ties: bool = F self._top_percent: bool = percent self._top_with_ties: bool = with_ties - @builder - def fetch_next(self, limit: int) -> "MSSQLQueryBuilder": - # Overridden to provide a more domain-specific API for T-SQL users - self._limit = limit - - def _offset_sql(self) -> str: - return " OFFSET {offset} ROWS".format(offset=self._offset or 0) - - def _limit_sql(self) -> str: - return " FETCH NEXT {limit} ROWS ONLY".format(limit=self._limit) - def _apply_pagination(self, querystring: str) -> str: # Note: Overridden as MSSQL specifies offset before the fetch next limit if self._limit is not None or self._offset: diff --git a/pypika/tests/dialects/test_mssql.py b/pypika/tests/dialects/test_mssql.py index f8940b08..7cf53b22 100644 --- a/pypika/tests/dialects/test_mssql.py +++ b/pypika/tests/dialects/test_mssql.py @@ -53,18 +53,13 @@ def test_limit(self): self.assertEqual('SELECT "def" FROM "abc" ORDER BY "def" OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', str(q)) - def test_fetch_next(self): - q = MSSQLQuery.from_("abc").select("def").orderby("def").fetch_next(10) - - self.assertEqual('SELECT "def" FROM "abc" ORDER BY "def" OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', str(q)) - def test_offset(self): q = MSSQLQuery.from_("abc").select("def").orderby("def").offset(10) self.assertEqual('SELECT "def" FROM "abc" ORDER BY "def" OFFSET 10 ROWS', str(q)) - def test_fetch_next_with_offset(self): - q = MSSQLQuery.from_("abc").select("def").orderby("def").fetch_next(10).offset(10) + def test_limit_with_offset(self): + q = MSSQLQuery.from_("abc").select("def").orderby("def").limit(10).offset(10) self.assertEqual('SELECT "def" FROM "abc" ORDER BY "def" OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY', str(q)) diff --git a/pypika/tests/dialects/test_oracle.py b/pypika/tests/dialects/test_oracle.py index c3a757a5..21b54f16 100644 --- a/pypika/tests/dialects/test_oracle.py +++ b/pypika/tests/dialects/test_oracle.py @@ -19,3 +19,33 @@ def test_groupby_alias_False_does_not_group_by_alias_when_subqueries_are_present q = OracleQuery.from_(subquery).select(col, Count('*')).groupby(col) self.assertEqual('SELECT sq0.abc a,COUNT(\'*\') FROM (SELECT abc FROM table1) sq0 GROUP BY sq0.abc', str(q)) + + def test_limit_query(self): + t = Table('table1') + limit = 5 + q = OracleQuery.from_(t).select(t.test).limit(limit) + + self.assertEqual(f'SELECT test FROM table1 FETCH NEXT {limit} ROWS ONLY', str(q)) + + def test_offset_query(self): + t = Table('table1') + offset = 5 + q = OracleQuery.from_(t).select(t.test).offset(offset) + + self.assertEqual(f'SELECT test FROM table1 OFFSET {offset} ROWS', str(q)) + + def test_limit_offset_query(self): + t = Table('table1') + limit = 5 + offset = 5 + q = OracleQuery.from_(t).select(t.test).limit(limit).offset(offset) + + self.assertEqual(f'SELECT test FROM table1 OFFSET {offset} ROWS FETCH NEXT {limit} ROWS ONLY', str(q)) + + def test_fetch_next_method_deprecated(self): + with self.assertWarns(DeprecationWarning): + t = Table('table1') + limit = 5 + q = OracleQuery.from_(t).select(t.test).fetch_next(limit) + + self.assertEqual(f'SELECT test FROM table1 FETCH NEXT {limit} ROWS ONLY', str(q))