summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArsa <arsalan.ghassemi@gmail.com>2021-12-06 16:55:29 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-12-09 11:16:04 +0100
commit17df72114e222d63c2af9ed9780583f4cb0689eb (patch)
tree3c30852ef05c67eb291df8249cbbeb653bb3e6d2
parenteba9a9b7f72995206af867600d6685b5405f172a (diff)
Fixed #33340 -- Fixed unquoted column names in queries used by DatabaseCache.
-rw-r--r--AUTHORS1
-rw-r--r--django/core/cache/backends/db.py14
-rw-r--r--django/db/backends/base/operations.py3
-rw-r--r--django/db/backends/oracle/operations.py7
-rw-r--r--tests/cache/tests.py18
5 files changed, 36 insertions, 7 deletions
diff --git a/AUTHORS b/AUTHORS
index e13d13d4ae..272a530132 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -97,6 +97,7 @@ answer newbie questions, and generally made Django that much better:
arien <regexbot@gmail.com>
Armin Ronacher
Aron Podrigal <aronp@guaranteedplus.com>
+ Arsalan Ghassemi <arsalan.ghassemi@gmail.com>
Artem Gnilov <boobsd@gmail.com>
Arthur <avandorp@gmail.com>
Arthur Jovart <arthur@jovart.com>
diff --git a/django/core/cache/backends/db.py b/django/core/cache/backends/db.py
index ef95b1dd16..5bb1c5aec5 100644
--- a/django/core/cache/backends/db.py
+++ b/django/core/cache/backends/db.py
@@ -228,10 +228,11 @@ class DatabaseCache(BaseDatabaseCache):
with connection.cursor() as cursor:
cursor.execute(
- 'SELECT %s FROM %s WHERE %s = %%s and expires > %%s' % (
+ 'SELECT %s FROM %s WHERE %s = %%s and %s > %%s' % (
quote_name('cache_key'),
quote_name(self._table),
quote_name('cache_key'),
+ quote_name('expires'),
),
[key, connection.ops.adapt_datetimefield_value(now)]
)
@@ -243,8 +244,10 @@ class DatabaseCache(BaseDatabaseCache):
else:
connection = connections[db]
table = connection.ops.quote_name(self._table)
- cursor.execute("DELETE FROM %s WHERE expires < %%s" % table,
- [connection.ops.adapt_datetimefield_value(now)])
+ cursor.execute('DELETE FROM %s WHERE %s < %%s' % (
+ table,
+ connection.ops.quote_name('expires'),
+ ), [connection.ops.adapt_datetimefield_value(now)])
deleted_count = cursor.rowcount
remaining_num = num - deleted_count
if remaining_num > self._max_entries:
@@ -255,7 +258,10 @@ class DatabaseCache(BaseDatabaseCache):
last_cache_key = cursor.fetchone()
if last_cache_key:
cursor.execute(
- 'DELETE FROM %s WHERE cache_key < %%s' % table,
+ 'DELETE FROM %s WHERE %s < %%s' % (
+ table,
+ connection.ops.quote_name('cache_key'),
+ ),
[last_cache_key[0]],
)
diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py
index 27fe486e2f..66bb009175 100644
--- a/django/db/backends/base/operations.py
+++ b/django/db/backends/base/operations.py
@@ -88,7 +88,8 @@ class BaseDatabaseOperations:
This is used by the 'db' cache backend to determine where to start
culling.
"""
- return "SELECT cache_key FROM %s ORDER BY cache_key LIMIT 1 OFFSET %%s"
+ cache_key = self.quote_name('cache_key')
+ return f'SELECT {cache_key} FROM %s ORDER BY {cache_key} LIMIT 1 OFFSET %%s'
def unification_cast_sql(self, output_field):
"""
diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py
index f497390bea..c357c75351 100644
--- a/django/db/backends/oracle/operations.py
+++ b/django/db/backends/oracle/operations.py
@@ -72,7 +72,12 @@ END;
}
def cache_key_culling_sql(self):
- return 'SELECT cache_key FROM %s ORDER BY cache_key OFFSET %%s ROWS FETCH FIRST 1 ROWS ONLY'
+ cache_key = self.quote_name('cache_key')
+ return (
+ f'SELECT {cache_key} '
+ f'FROM %s '
+ f'ORDER BY {cache_key} OFFSET %%s ROWS FETCH FIRST 1 ROWS ONLY'
+ )
def date_extract_sql(self, lookup_type, field_name):
if lookup_type == 'week_day':
diff --git a/tests/cache/tests.py b/tests/cache/tests.py
index ddfe9ddfe6..7a98fc4ba1 100644
--- a/tests/cache/tests.py
+++ b/tests/cache/tests.py
@@ -1113,7 +1113,7 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase):
with self.assertNumQueries(1):
cache.delete_many(['a', 'b', 'c'])
- def test_cull_count_queries(self):
+ def test_cull_queries(self):
old_max_entries = cache._max_entries
# Force _cull to delete on first cached record.
cache._max_entries = -1
@@ -1124,6 +1124,13 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase):
cache._max_entries = old_max_entries
num_count_queries = sum('COUNT' in query['sql'] for query in captured_queries)
self.assertEqual(num_count_queries, 1)
+ # Column names are quoted.
+ for query in captured_queries:
+ sql = query['sql']
+ if 'expires' in sql:
+ self.assertIn(connection.ops.quote_name('expires'), sql)
+ if 'cache_key' in sql:
+ self.assertIn(connection.ops.quote_name('cache_key'), sql)
def test_delete_cursor_rowcount(self):
"""
@@ -1180,6 +1187,15 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase):
)
self.assertEqual(out.getvalue(), "Cache table 'test cache table' created.\n")
+ def test_has_key_query_columns_quoted(self):
+ with CaptureQueriesContext(connection) as captured_queries:
+ cache.has_key('key')
+ self.assertEqual(len(captured_queries), 1)
+ sql = captured_queries[0]['sql']
+ # Column names are quoted.
+ self.assertIn(connection.ops.quote_name('expires'), sql)
+ self.assertIn(connection.ops.quote_name('cache_key'), sql)
+
@override_settings(USE_TZ=True)
class DBCacheWithTimeZoneTests(DBCacheTests):