Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #10164 -- Made AutoField increase monotonically on SQLite

Thanks malte for the report.
  • Loading branch information...
commit eade315da1c8372ac1dfcf1fd20ea87f454d71ac 1 parent 630eb05
Chris Wilson authored September 06, 2013 timgraham committed September 06, 2013
4  django/db/backends/creation.py
@@ -23,6 +23,7 @@ class BaseDatabaseCreation(object):
23 23
     destruction of test databases.
24 24
     """
25 25
     data_types = {}
  26
+    data_types_suffix = {}
26 27
     data_type_check_constraints = {}
27 28
 
28 29
     def __init__(self, connection):
@@ -53,6 +54,7 @@ def sql_create_model(self, model, style, known_models=set()):
53 54
         qn = self.connection.ops.quote_name
54 55
         for f in opts.local_fields:
55 56
             col_type = f.db_type(connection=self.connection)
  57
+            col_type_suffix = f.db_type_suffix(connection=self.connection)
56 58
             tablespace = f.db_tablespace or opts.db_tablespace
57 59
             if col_type is None:
58 60
                 # Skip ManyToManyFields, because they're not represented as
@@ -88,6 +90,8 @@ def sql_create_model(self, model, style, known_models=set()):
88 90
                         (model, f))
89 91
                 else:
90 92
                     field_output.extend(ref_output)
  93
+            if col_type_suffix:
  94
+                field_output.append(style.SQL_KEYWORD(col_type_suffix))
91 95
             table_output.append(' '.join(field_output))
92 96
         for field_constraints in opts.unique_together:
93 97
             table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' %
1  django/db/backends/sqlite3/base.py
@@ -106,6 +106,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
106 106
     supports_check_constraints = False
107 107
     autocommits_when_autocommit_is_off = True
108 108
     supports_paramstyle_pyformat = False
  109
+    supports_sequence_reset = False
109 110
 
110 111
     @cached_property
111 112
     def uses_savepoints(self):
3  django/db/backends/sqlite3/creation.py
@@ -34,6 +34,9 @@ class DatabaseCreation(BaseDatabaseCreation):
34 34
         'TextField': 'text',
35 35
         'TimeField': 'time',
36 36
     }
  37
+    data_types_suffix = {
  38
+        'AutoField': 'AUTOINCREMENT',
  39
+    }
37 40
 
38 41
     def sql_for_pending_references(self, model, style, pending_references):
39 42
         "SQLite3 doesn't support constraints"
2  django/db/backends/sqlite3/introspection.py
@@ -175,7 +175,7 @@ def get_primary_key_column(self, cursor, table_name):
175 175
         results = results[results.index('(') + 1:results.rindex(')')]
176 176
         for field_desc in results.split(','):
177 177
             field_desc = field_desc.strip()
178  
-            m = re.search('"(.*)".*PRIMARY KEY$', field_desc)
  178
+            m = re.search('"(.*)".*PRIMARY KEY( AUTOINCREMENT)?$', field_desc)
179 179
             if m:
180 180
                 return m.groups()[0]
181 181
         return None
3  django/db/models/fields/__init__.py
@@ -395,6 +395,9 @@ def db_parameters(self, connection):
395 395
             "check": check_string,
396 396
         }
397 397
 
  398
+    def db_type_suffix(self, connection):
  399
+        return connection.creation.data_types_suffix.get(self.get_internal_type())
  400
+
398 401
     @property
399 402
     def unique(self):
400 403
         return self._unique or self.primary_key
8  docs/releases/1.7.txt
@@ -336,6 +336,14 @@ Miscellaneous
336 336
   when called on an instance without a primary key value. This is done to
337 337
   avoid mutable ``__hash__`` values in containers.
338 338
 
  339
+* The :meth:`django.db.backends.sqlite3.DatabaseCreation.sql_create_model`
  340
+  will now create :class:`~django.db.models.AutoField` columns in SQLite
  341
+  databases using the ``AUTOINCREMENT`` option, which guarantees monotonic
  342
+  increments. This will cause primary key numbering behavior to change on
  343
+  SQLite, becoming consistent with most other SQL databases. If you have a
  344
+  database created with an older version of Django, you will need to migrate
  345
+  it to take advantage of this feature. See ticket #10164 for details.
  346
+
339 347
 * ``django.contrib.auth.models.AbstractUser`` no longer defines a
340 348
   :meth:`~django.db.models.Model.get_absolute_url()` method. The old definition
341 349
   returned  ``"/users/%s/" % urlquote(self.username)`` which was arbitrary
20  tests/backends/tests.py
@@ -4,6 +4,7 @@
4 4
 
5 5
 import datetime
6 6
 from decimal import Decimal
  7
+import re
7 8
 import threading
8 9
 import unittest
9 10
 
@@ -108,6 +109,25 @@ def test_order_of_nls_parameters(self):
108 109
         self.assertEqual(c.fetchone()[0], 1)
109 110
 
110 111
 
  112
+class SQLiteTests(TestCase):
  113
+    longMessage = True
  114
+
  115
+    @unittest.skipUnless(connection.vendor == 'sqlite',
  116
+                        "Test valid only for SQLite")
  117
+    def test_autoincrement(self):
  118
+        """
  119
+        Check that auto_increment fields are created with the AUTOINCREMENT
  120
+        keyword in order to be monotonically increasing. Refs #10164.
  121
+        """
  122
+        statements = connection.creation.sql_create_model(models.Square,
  123
+            style=no_style())
  124
+        match = re.search('"id" ([^,]+),', statements[0][0])
  125
+        self.assertIsNotNone(match)
  126
+        self.assertEqual('integer NOT NULL PRIMARY KEY AUTOINCREMENT',
  127
+            match.group(1), "Wrong SQL used to create an auto-increment "
  128
+            "column on SQLite")
  129
+
  130
+
111 131
 class MySQLTests(TestCase):
112 132
     @unittest.skipUnless(connection.vendor == 'mysql',
113 133
                         "Test valid only for MySQL")

0 notes on commit eade315

Aymeric Augustin

If we want to point to the Trac ticket, we should include an http:// link. But in fact the ticket doesn't provide any migration instructions, so we're really being mean to our users.

I would prefer that we say:

  • this only applies to new databases, not to existing ones;
  • the easiest way to migrate is to dumpdata, rename the database file (keep it as a backup), migrate, loaddata.

It's not hard to explain!

Tim Graham

thanks for the poke, updated in 910a576

Please sign in to comment.
Something went wrong with that request. Please try again.