summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/backends/sqlite3/operations.py18
-rw-r--r--tests/backends/sqlite/tests.py32
2 files changed, 32 insertions, 18 deletions
diff --git a/django/db/backends/sqlite3/operations.py b/django/db/backends/sqlite3/operations.py
index 1500ae28aa..ac98324b2e 100644
--- a/django/db/backends/sqlite3/operations.py
+++ b/django/db/backends/sqlite3/operations.py
@@ -1,5 +1,6 @@
import datetime
import decimal
+import sqlite3
import uuid
from functools import lru_cache
from itertools import chain
@@ -143,16 +144,15 @@ class DatabaseOperations(BaseDatabaseOperations):
"""
Only for last_executed_query! Don't use this to execute SQL queries!
"""
- # This function is limited both by SQLITE_LIMIT_VARIABLE_NUMBER (the
- # number of parameters, default = 999) and SQLITE_MAX_COLUMN (the
- # number of return values, default = 2000). Since Python's sqlite3
- # module doesn't expose the get_limit() C API, assume the default
- # limits are in effect and split the work in batches if needed.
- BATCH_SIZE = 999
- if len(params) > BATCH_SIZE:
+ connection = self.connection.connection
+ variable_limit = self.connection.features.max_query_params
+ column_limit = connection.getlimit(sqlite3.SQLITE_LIMIT_COLUMN)
+ batch_size = min(variable_limit, column_limit)
+
+ if len(params) > batch_size:
results = ()
- for index in range(0, len(params), BATCH_SIZE):
- chunk = params[index : index + BATCH_SIZE]
+ for index in range(0, len(params), batch_size):
+ chunk = params[index : index + batch_size]
results += self._quote_params_for_last_executed_query(chunk)
return results
diff --git a/tests/backends/sqlite/tests.py b/tests/backends/sqlite/tests.py
index 37d95c0cb5..34c0eca0ff 100644
--- a/tests/backends/sqlite/tests.py
+++ b/tests/backends/sqlite/tests.py
@@ -1,5 +1,6 @@
import os
import re
+import sqlite3
import tempfile
import threading
import unittest
@@ -215,15 +216,28 @@ class LastExecutedQueryTest(TestCase):
substituted = "SELECT '\"''\\'"
self.assertEqual(connection.queries[-1]["sql"], substituted)
- def test_large_number_of_parameters(self):
- # If SQLITE_MAX_VARIABLE_NUMBER (default = 999) has been changed to be
- # greater than SQLITE_MAX_COLUMN (default = 2000), last_executed_query
- # can hit the SQLITE_MAX_COLUMN limit (#26063).
- with connection.cursor() as cursor:
- sql = "SELECT MAX(%s)" % ", ".join(["%s"] * 2001)
- params = list(range(2001))
- # This should not raise an exception.
- cursor.db.ops.last_executed_query(cursor.cursor, sql, params)
+ def test_parameter_count_exceeds_variable_or_column_limit(self):
+ sql = "SELECT MAX(%s)" % ", ".join(["%s"] * 1001)
+ params = list(range(1001))
+ for label, limit, current_limit in [
+ (
+ "variable",
+ sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER,
+ connection.features.max_query_params,
+ ),
+ (
+ "column",
+ sqlite3.SQLITE_LIMIT_COLUMN,
+ connection.connection.getlimit(sqlite3.SQLITE_LIMIT_COLUMN),
+ ),
+ ]:
+ with self.subTest(limit=label):
+ connection.connection.setlimit(limit, 1000)
+ self.addCleanup(connection.connection.setlimit, limit, current_limit)
+ with connection.cursor() as cursor:
+ # This should not raise an exception.
+ cursor.db.ops.last_executed_query(cursor.cursor, sql, params)
+ connection.connection.setlimit(limit, current_limit)
@unittest.skipUnless(connection.vendor == "sqlite", "SQLite tests")