Commits

Andrew Godwin  committed c92d62e

Fixed #144: Unique_together support on SQLite.

  • Participants
  • Parent commits 9922086

Comments (0)

Files changed (1)

File south/db/sqlite3.py

             field.column: self._column_sql_for_create(table_name, name, field, False),
         })
     
-    def _remake_table(self, table_name, added={}, renames={}, deleted=[], altered={}, primary_key_override=None):
+    def _remake_table(self, table_name, added={}, renames={}, deleted=[], altered={},
+                      primary_key_override=None, uniques_deleted=[]):
         """
         Given a table and three sets of changes (renames, deletes, alters),
         recreates it with the modified schema.
         cursor = self._get_connection().cursor()
         # Get the index descriptions
         indexes = self._get_connection().introspection.get_indexes(cursor, table_name)
+        multi_indexes = self._get_multi_indexes(table_name)
         # Work out new column defs.
         for column_info in self._get_connection().introspection.get_table_description(cursor, table_name):
             name = column_info[0]
             # Get the type, ignoring PRIMARY KEY (we need to be consistent)
             type = column_info[1].replace("PRIMARY KEY", "")
             # Add on unique or primary key if needed.
-            if indexes[name]['unique']:
+            if indexes[name]['unique'] and name not in uniques_deleted:
                 type += " UNIQUE"
             if (primary_key_override and primary_key_override == name) or \
                (not primary_key_override and indexes[name]['primary_key']):
         # Delete the old table, move our new one over it
         self.delete_table(table_name)
         self.rename_table(temp_name, table_name)
+        # Recreate multi-valued indexes
+        # We can't do that before since it's impossible to rename indexes
+        # and index name scope is global
+        self._make_multi_indexes(table_name, multi_indexes, renames=renames, deleted=deleted, uniques_deleted=uniques_deleted)
+
     
     def _copy_data(self, src, dst, field_renames={}):
         "Used to copy data into a new table"
             ', '.join(src_fields_new),
             self.quote_name(src),
         ))
+
+    def _create_unique(self, table_name, columns):
+        self.execute("CREATE UNIQUE INDEX %s ON %s(%s);" % (
+                self.quote_name('%s_%s' % (table_name, '__'.join(columns))),
+                self.quote_name(table_name),
+                ', '.join(columns)
+        ))
+
+    def _get_multi_indexes(self, table_name):
+        indexes = []
+        cursor = self._get_connection().cursor()
+        cursor.execute('PRAGMA index_list(%s)' % self.quote_name(table_name))
+        # seq, name, unique
+        for index, unique in [(field[1], field[2]) for field in cursor.fetchall()]:
+            if not unique:
+                continue
+            cursor.execute('PRAGMA index_info(%s)' % self.quote_name(index))
+            info = cursor.fetchall()
+            if len(info) == 1:
+                continue
+            columns = []
+            for field in info:
+                columns.append(field[2])
+            indexes.append(columns)
+        return indexes
+
+    def _make_multi_indexes(self, table_name, indexes, deleted=[], renames={}, uniques_deleted=[]):
+        for index in indexes:
+            columns = []
+
+            for name in index:
+                # Handle deletion
+                if name in deleted:
+                    columns = []
+                    break
+
+                # Handle renames
+                if name in renames:
+                    name = renames[name]
+                columns.append(name)
+
+            if columns and columns != uniques_deleted:
+                self._create_unique(table_name, columns)
     
     def _column_sql_for_create(self, table_name, name, field, explicit_name=True):
         "Given a field and its name, returns the full type for the CREATE TABLE."
         """
         self._remake_table(table_name, renames={old: new})
     
-    # Nor unique creation
     def create_unique(self, table_name, columns):
         """
-        Not supported under SQLite.
+        Create an unique index on columns
         """
-        print "   ! WARNING: SQLite does not support adding unique constraints. Ignored."
+        self._create_unique(table_name, columns)
     
-    # Nor unique deletion
     def delete_unique(self, table_name, columns):
         """
-        Not supported under SQLite.
+        Delete an unique index
         """
-        print "   ! WARNING: SQLite does not support removing unique constraints. Ignored."
+        self._remake_table(table_name, uniques_deleted=columns)
     
     def create_primary_key(self, table_name, columns):
         if not isinstance(columns, (list, tuple)):