From cab044c66ca3d1f3cfc704f64364aced2b9645af Mon Sep 17 00:00:00 2001
From: Andrew Godwin <andrew@aeracode.org>
Date: Sat, 18 Aug 2012 12:29:31 +0100
Subject: [PATCH] First stab at MySQL support

---
 django/db/backends/mysql/base.py              |  5 +++
 django/db/backends/mysql/introspection.py     | 32 +++++++++++++++++++
 django/db/backends/mysql/schema.py            | 24 ++++++++++++++
 .../postgresql_psycopg2/introspection.py      |  2 +-
 tests/modeltests/schema/tests.py              |  3 --
 5 files changed, 62 insertions(+), 4 deletions(-)
 create mode 100644 django/db/backends/mysql/schema.py

diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index 2222f89cf0..b3e09edc57 100644
--- a/django/db/backends/mysql/base.py
+++ b/django/db/backends/mysql/base.py
@@ -37,6 +37,7 @@ from django.db.backends.mysql.client import DatabaseClient
 from django.db.backends.mysql.creation import DatabaseCreation
 from django.db.backends.mysql.introspection import DatabaseIntrospection
 from django.db.backends.mysql.validation import DatabaseValidation
+from django.db.backends.mysql.schema import DatabaseSchemaEditor
 from django.utils.functional import cached_property
 from django.utils.safestring import SafeString, SafeUnicode
 from django.utils import six
@@ -488,3 +489,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
                         % (table_name, bad_row[0],
                         table_name, column_name, bad_row[1],
                         referenced_table_name, referenced_column_name))
+
+    def schema_editor(self):
+        "Returns a new instance of this backend's SchemaEditor"
+        return DatabaseSchemaEditor(self)
diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py
index 6aab0b99ab..61ab3038c4 100644
--- a/django/db/backends/mysql/introspection.py
+++ b/django/db/backends/mysql/introspection.py
@@ -102,3 +102,35 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
             indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
         return indexes
 
+    def get_constraints(self, cursor, table_name):
+        """
+        Retrieves any constraints (unique, pk, fk, check) across one or more columns.
+        Returns {'cnname': {'columns': set(columns), 'primary_key': bool, 'unique': bool}}
+        """
+        constraints = {}
+        # Loop over the constraint tables, collecting things as constraints
+        ifsc_tables = ["constraint_column_usage", "key_column_usage"]
+        for ifsc_table in ifsc_tables:
+            cursor.execute("""
+                SELECT kc.constraint_name, kc.column_name, c.constraint_type
+                FROM information_schema.%s AS kc
+                JOIN information_schema.table_constraints AS c ON
+                    kc.table_schema = c.table_schema AND
+                    kc.table_name = c.table_name AND
+                    kc.constraint_name = c.constraint_name
+                WHERE
+                    kc.table_schema = %%s AND
+                    kc.table_name = %%s
+            """ % ifsc_table, [self.connection.settings_dict['NAME'], table_name])
+            for constraint, column, kind in cursor.fetchall():
+                # If we're the first column, make the record
+                if constraint not in constraints:
+                    constraints[constraint] = {
+                        "columns": set(),
+                        "primary_key": kind.lower() == "primary key",
+                        "foreign_key": kind.lower() == "foreign key",
+                        "unique": kind.lower() in ["primary key", "unique"],
+                    }
+                # Record the details
+                constraints[constraint]['columns'].add(column)
+        return constraints
diff --git a/django/db/backends/mysql/schema.py b/django/db/backends/mysql/schema.py
new file mode 100644
index 0000000000..2c0ad5b8af
--- /dev/null
+++ b/django/db/backends/mysql/schema.py
@@ -0,0 +1,24 @@
+from django.db.backends.schema import BaseDatabaseSchemaEditor
+
+
+class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
+
+    sql_rename_table = "RENAME TABLE %(old_table)s TO %(new_table)s"
+
+    sql_alter_column_null = "MODIFY %(column)s %(type)s NULL"
+    sql_alter_column_not_null = "MODIFY %(column)s %(type)s NULL"
+
+    sql_delete_unique = "ALTER TABLE %(table)s DROP INDEX %(name)s"
+
+    sql_create_fk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s FOREIGN KEY (%(column)s) REFERENCES %(to_table)s (%(to_column)s)"
+    sql_delete_fk = "ALTER TABLE %(table)s DROP FOREIGN KEY %(name)s"
+
+    sql_delete_index = "DROP INDEX %(name)s ON %(table_name)s"
+
+    sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY"
+
+
+
+
+    alter_string_set_null = 'MODIFY %(column)s %(type)s NULL;'
+    alter_string_drop_null = 'MODIFY %(column)s %(type)s NOT NULL;'
diff --git a/django/db/backends/postgresql_psycopg2/introspection.py b/django/db/backends/postgresql_psycopg2/introspection.py
index c8b8ec833b..d85bdc9d6b 100644
--- a/django/db/backends/postgresql_psycopg2/introspection.py
+++ b/django/db/backends/postgresql_psycopg2/introspection.py
@@ -91,7 +91,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
 
     def get_constraints(self, cursor, table_name):
         """
-        Retrieves any constraints (unique, pk, check) across one or more columns.
+        Retrieves any constraints (unique, pk, fk, check) across one or more columns.
         Returns {'cnname': {'columns': set(columns), 'primary_key': bool, 'unique': bool}}
         """
         constraints = {}
diff --git a/tests/modeltests/schema/tests.py b/tests/modeltests/schema/tests.py
index 8813d3ca23..7b1de268a4 100644
--- a/tests/modeltests/schema/tests.py
+++ b/tests/modeltests/schema/tests.py
@@ -167,7 +167,6 @@ class SchemaTests(TestCase):
         # Ensure the field is right to begin with
         columns = self.column_classes(Author)
         self.assertEqual(columns['name'][0], "CharField")
-        self.assertEqual(columns['name'][1][3], 255)
         self.assertEqual(columns['name'][1][6], False)
         # Alter the name field to a TextField
         new_field = TextField(null=True)
@@ -197,7 +196,6 @@ class SchemaTests(TestCase):
         # Ensure the field is right to begin with
         columns = self.column_classes(Author)
         self.assertEqual(columns['name'][0], "CharField")
-        self.assertEqual(columns['name'][1][3], 255)
         self.assertNotIn("display_name", columns)
         # Alter the name field's name
         new_field = CharField(max_length=254)
@@ -213,7 +211,6 @@ class SchemaTests(TestCase):
         # Ensure the field is right afterwards
         columns = self.column_classes(Author)
         self.assertEqual(columns['display_name'][0], "CharField")
-        self.assertEqual(columns['display_name'][1][3], 254)
         self.assertNotIn("name", columns)
 
     def test_m2m(self):