summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/backends/__init__.py8
-rw-r--r--django/db/backends/mysql/base.py4
-rw-r--r--django/db/backends/mysql_old/base.py4
-rw-r--r--django/db/backends/postgresql/operations.py3
-rw-r--r--django/db/backends/sqlite3/base.py3
-rw-r--r--django/db/models/sql/query.py6
-rw-r--r--docs/db-api.txt8
-rw-r--r--tests/modeltests/basic/models.py8
8 files changed, 38 insertions, 6 deletions
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index a4c5a25589..6ba06f083a 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -183,6 +183,14 @@ class BaseDatabaseOperations(object):
"""
return None
+ def no_limit_value(self):
+ """
+ Returns the value to use for the LIMIT when we are wanting "LIMIT
+ infinity". Returns None if the limit clause can be omitted in this case.
+ """
+ # FIXME: API may need to change once Oracle backend is repaired.
+ raise NotImplementedError()
+
def pk_default_value(self):
"""
Returns the value to use during an INSERT statement to specify that
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index 697dcd3a67..c2b2c7ab1c 100644
--- a/django/db/backends/mysql/base.py
+++ b/django/db/backends/mysql/base.py
@@ -93,6 +93,10 @@ class DatabaseOperations(BaseDatabaseOperations):
sql += "%s," % offset
return sql + str(limit)
+ def no_limit_value(self):
+ # 2**64 - 1, as recommended by the MySQL documentation
+ return 18446744073709551615L
+
def quote_name(self, name):
if name.startswith("`") and name.endswith("`"):
return name # Quoting once is enough.
diff --git a/django/db/backends/mysql_old/base.py b/django/db/backends/mysql_old/base.py
index c22094b968..374e9af1f5 100644
--- a/django/db/backends/mysql_old/base.py
+++ b/django/db/backends/mysql_old/base.py
@@ -98,6 +98,10 @@ class DatabaseOperations(BaseDatabaseOperations):
sql += "%s," % offset
return sql + str(limit)
+ def no_limit_value(self):
+ # 2**64 - 1, as recommended by the MySQL documentation
+ return 18446744073709551615L
+
def quote_name(self, name):
if name.startswith("`") and name.endswith("`"):
return name # Quoting once is enough.
diff --git a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py
index 20392b238e..d2c65a8753 100644
--- a/django/db/backends/postgresql/operations.py
+++ b/django/db/backends/postgresql/operations.py
@@ -31,6 +31,9 @@ class DatabaseOperations(BaseDatabaseOperations):
cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
return cursor.fetchone()[0]
+ def no_limit_value(self):
+ return None
+
def quote_name(self, name):
if name.startswith('"') and name.endswith('"'):
return name # Quoting once is enough.
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index b4b445cd16..b8bf5c8f0b 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -63,6 +63,9 @@ class DatabaseOperations(BaseDatabaseOperations):
return name # Quoting once is enough.
return '"%s"' % name
+ def no_limit_value(self):
+ return -1
+
def sql_flush(self, style, tables, sequences):
# NB: The generated SQL below is specific to SQLite
# Note: The DELETE FROM... SQL generated below works for SQLite databases
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 20dcb4b71d..f251730c48 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -262,11 +262,15 @@ class Query(object):
if ordering:
result.append('ORDER BY %s' % ', '.join(ordering))
+ # FIXME: Pull this out to make life easier for Oracle et al.
if with_limits:
if self.high_mark:
result.append('LIMIT %d' % (self.high_mark - self.low_mark))
if self.low_mark:
- assert self.high_mark, "'offset' is not allowed without 'limit'"
+ if not self.high_mark:
+ val = self.connection.ops.no_limit_value()
+ if val:
+ result.append('LIMIT %d' % val)
result.append('OFFSET %d' % self.low_mark)
params.extend(self.extra_params)
diff --git a/docs/db-api.txt b/docs/db-api.txt
index 887a06a67e..d96541d58b 100644
--- a/docs/db-api.txt
+++ b/docs/db-api.txt
@@ -422,6 +422,14 @@ This returns the sixth through tenth objects (``OFFSET 5 LIMIT 5``)::
Entry.objects.all()[5:10]
+You can also slice from the item ''N'' to the end of the queryset. For
+example, to return everything from the fixth item onwards::
+
+ Entry.objects.all()[5:]
+
+How this last example is implemented in SQL varies depending upon the database
+used, but it is supported in all cases.
+
Generally, slicing a ``QuerySet`` returns a new ``QuerySet`` -- it doesn't
evaluate the query. An exception is if you use the "step" parameter of Python
slice syntax. For example, this would actually execute the query in order to
diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py
index 557331a36e..51de8a50f8 100644
--- a/tests/modeltests/basic/models.py
+++ b/tests/modeltests/basic/models.py
@@ -292,11 +292,9 @@ datetime.datetime(2005, 7, 28, 0, 0)
>>> Article.objects.all()[2:][2:3]
[<Article: Default headline>]
-# Note that you can't use 'offset' without 'limit' (on some dbs), so this doesn't work:
->>> Article.objects.all()[2:]
-Traceback (most recent call last):
- ...
-AssertionError: 'offset' is not allowed without 'limit'
+# Using an offset without a limit is also possible.
+>>> Article.objects.all()[5:]
+[<Article: Fourth article>, <Article: Article 7>, <Article: Updated article 8>]
# Also, once you have sliced you can't filter, re-order or combine
>>> Article.objects.all()[0:5].filter(id=1)