From 8f75984c263b89336cdf919c246a56ce49d07ddb Mon Sep 17 00:00:00 2001
From: Hajime Nakagami <nakagami@gmail.com>
Date: Wed, 14 Mar 2018 11:46:41 +0900
Subject: [PATCH] Quoted field names in cache db backend SQL.

On Firebird, 'value' is a reserved word and must be quoted.
---
 django/core/cache/backends/db.py | 75 ++++++++++++++++++++++++--------
 1 file changed, 57 insertions(+), 18 deletions(-)

diff --git a/django/core/cache/backends/db.py b/django/core/cache/backends/db.py
index 041445aa56..cc92284709 100644
--- a/django/core/cache/backends/db.py
+++ b/django/core/cache/backends/db.py
@@ -51,11 +51,20 @@ class DatabaseCache(BaseDatabaseCache):
         self.validate_key(key)
         db = router.db_for_read(self.cache_model_class)
         connection = connections[db]
-        table = connection.ops.quote_name(self._table)
+        quote_name = connection.ops.quote_name
+        table = quote_name(self._table)
 
         with connection.cursor() as cursor:
-            cursor.execute("SELECT cache_key, value, expires FROM %s "
-                           "WHERE cache_key = %%s" % table, [key])
+            cursor.execute(
+                'SELECT %s, %s, %s FROM %s WHERE %s = %%s' % (
+                    quote_name('cache_key'),
+                    quote_name('value'),
+                    quote_name('expires'),
+                    table,
+                    quote_name('cache_key'),
+                ),
+                [key]
+            )
             row = cursor.fetchone()
         if row is None:
             return default
@@ -73,8 +82,13 @@ class DatabaseCache(BaseDatabaseCache):
             db = router.db_for_write(self.cache_model_class)
             connection = connections[db]
             with connection.cursor() as cursor:
-                cursor.execute("DELETE FROM %s "
-                               "WHERE cache_key = %%s" % table, [key])
+                cursor.execute(
+                    'DELETE FROM %s WHERE %s = %%s' % (
+                        table,
+                        quote_name('cache_key'),
+                    ),
+                    [key]
+                )
             return default
 
         value = connection.ops.process_clob(row[1])
@@ -94,7 +108,8 @@ class DatabaseCache(BaseDatabaseCache):
         timeout = self.get_backend_timeout(timeout)
         db = router.db_for_write(self.cache_model_class)
         connection = connections[db]
-        table = connection.ops.quote_name(self._table)
+        quote_name = connection.ops.quote_name
+        table = quote_name(self._table)
 
         with connection.cursor() as cursor:
             cursor.execute("SELECT COUNT(*) FROM %s" % table)
@@ -120,8 +135,15 @@ class DatabaseCache(BaseDatabaseCache):
                 # so be careful about changes here - test suite will NOT pick
                 # regressions.
                 with transaction.atomic(using=db):
-                    cursor.execute("SELECT cache_key, expires FROM %s "
-                                   "WHERE cache_key = %%s" % table, [key])
+                    cursor.execute(
+                        'SELECT %s, %s FROM %s WHERE %s = %%s' % (
+                            quote_name('cache_key'),
+                            quote_name('expires'),
+                            table,
+                            quote_name('cache_key'),
+                        ),
+                        [key]
+                    )
                     result = cursor.fetchone()
 
                     if result:
@@ -136,13 +158,25 @@ class DatabaseCache(BaseDatabaseCache):
 
                     exp = connection.ops.adapt_datetimefield_value(exp)
                     if result and (mode == 'set' or (mode == 'add' and current_expires < now)):
-                        cursor.execute("UPDATE %s SET value = %%s, expires = %%s "
-                                       "WHERE cache_key = %%s" % table,
-                                       [b64encoded, exp, key])
+                        cursor.execute(
+                            'UPDATE %s SET %s = %%s, %s = %%s WHERE %s = %%s' % (
+                                table,
+                                quote_name('value'),
+                                quote_name('expires'),
+                                quote_name('cache_key'),
+                            ),
+                            [b64encoded, exp, key]
+                        )
                     else:
-                        cursor.execute("INSERT INTO %s (cache_key, value, expires) "
-                                       "VALUES (%%s, %%s, %%s)" % table,
-                                       [key, b64encoded, exp])
+                        cursor.execute(
+                            'INSERT INTO %s (%s, %s, %s) VALUES (%%s, %%s, %%s)' % (
+                                table,
+                                quote_name('cache_key'),
+                                quote_name('value'),
+                                quote_name('expires'),
+                            ),
+                            [key, b64encoded, exp]
+                        )
             except DatabaseError:
                 # To be threadsafe, updates/inserts are allowed to fail silently
                 return False
@@ -166,7 +200,7 @@ class DatabaseCache(BaseDatabaseCache):
 
         db = router.db_for_read(self.cache_model_class)
         connection = connections[db]
-        table = connection.ops.quote_name(self._table)
+        quote_name = connection.ops.quote_name
 
         if settings.USE_TZ:
             now = datetime.utcnow()
@@ -175,9 +209,14 @@ class DatabaseCache(BaseDatabaseCache):
         now = now.replace(microsecond=0)
 
         with connection.cursor() as cursor:
-            cursor.execute("SELECT cache_key FROM %s "
-                           "WHERE cache_key = %%s and expires > %%s" % table,
-                           [key, connection.ops.adapt_datetimefield_value(now)])
+            cursor.execute(
+                'SELECT %s FROM %s WHERE %s = %%s and expires > %%s' % (
+                    quote_name('cache_key'),
+                    quote_name(self._table),
+                    quote_name('cache_key'),
+                ),
+                [key, connection.ops.adapt_datetimefield_value(now)]
+            )
             return cursor.fetchone() is not None
 
     def _cull(self, db, cursor, now):