Bug #80915 Foreign key not created if unique constraint not defined when using SQLAlchemy
Submitted: 31 Mar 2016 0:38 Modified: 8 Sep 2016 5:59
Reporter: David Turner Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Cluster: Cluster (NDB) storage engine Severity:S2 (Serious)
Version: 5.6.28-ndb-7.4.10-cluster-gpl OS:Oracle Linux
Assigned to: CPU Architecture:Any
Tags: ndb, OpenStack, SQLAlchemy

[31 Mar 2016 0:38] David Turner
Description:
Not sure if this is the correct place to log this, but it may be of some concern if there is interest in OpenStack compatiblity, which in turn requires SQLAlchemy compatibility....

Trying to use MySQL Cluster with OpenStack (per O3L's approach).  OpenStack services come with a set of DB migration scripts that use SQLAlchemy for defining / updating tables for a given service's database.  As part of the workarounds implemented by the O3L team for MySQL Cluster compatibility, we sometimes comment out the creation of unique constraints to avoid problems in other migration scripts where those same constraints can't be deleted due to the FK.  I've noticed that if we comment out the unique constraints in the SQLAlchemy script defining a table, the resulting table doesn't contain the FK, which of course causes problems in later migration scripts. I cannot repeat this when creating the table manually.

How to repeat:
MySQL Cluster / Python / SqlAlchemy versions used:
# python --version
Python 2.7.5

# pip freeze | grep -i sqlalchemy
SQLAlchemy==1.0.12
sqlalchemy-migrate==0.10.0

mysql> select @@version;                                                                                                                                
+-------------------------------+
| @@version                     |
+-------------------------------+
| 5.6.28-ndb-7.4.10-cluster-gpl |
+-------------------------------+
1 row in set (0.00 sec)

Example:

The SQLAlchmemy block for metadef_objects table creation (from Glance DB migration):

def define_metadef_objects_table(meta):

   _constr_kwargs = {}
   if meta.bind.name == 'ibm_db_sa':
       _constr_kwargs['name'] = 'ix_objects_namespace_id_name'

   objects = Table('metadef_objects',
                   meta,
                   Column('id', Integer(), primary_key=True, nullable=False),
                   Column('namespace_id', Integer(),
                          ForeignKey('metadef_namespaces.id'),
                          nullable=False),
                   Column('name', String(80), nullable=False),
                   Column('description', Text()),
                   Column('required', Text()),
                   Column('schema', Text(), nullable=False),
                   Column('created_at', DateTime(), nullable=False),
                   Column('updated_at', DateTime()),
                   UniqueConstraint('namespace_id', 'name',
                                    **_constr_kwargs),
                   mysql_engine='NDBCLUSTER',
                   mysql_charset='utf8',
                   extend_existing=True)

   if meta.bind.name != 'ibm_db_sa':
       Index('ix_objects_namespace_id_name',
             objects.c.namespace_id,
             objects.c.name)

   return objects

The above code results in the following table as described by the “show create table” SQL statement:

CREATE TABLE `metadef_objects` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `namespace_id` int(11) NOT NULL,
 `name` varchar(80) NOT NULL,
 `description` text,
 `required` text,
 `schema` text NOT NULL,
 `created_at` datetime NOT NULL,
 `updated_at` datetime DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `namespace_id` (`namespace_id`,`name`),
 KEY `ix_objects_namespace_id_name` (`namespace_id`,`name`),
 CONSTRAINT `FK_218_245` FOREIGN KEY (`namespace_id`) REFERENCES `metadef_namespaces` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=ndbcluster DEFAULT CHARSET=utf8 

Notice the FK and unique constraint.  One of the workarounds employed by Oracle OpenStack team in the event a DB migration script failed to delete a unique constraint was to comment out the creation of the constraint.  For example:

   objects = Table('metadef_objects',
                   meta,
                   Column('id', Integer(), primary_key=True, nullable=False),
                   Column('namespace_id', Integer(),
                          ForeignKey('metadef_namespaces.id'),
                          nullable=False),
                   Column('name', String(80), nullable=False),
                   Column('description', Text()),
                   Column('required', Text()),
                   Column('schema', Text(), nullable=False),
                   Column('created_at', DateTime(), nullable=False),
                   Column('updated_at', DateTime()),
                   # Don’t create for NDB 
                   # UniqueConstraint('namespace_id', 'name',
                   #                  **_constr_kwargs),
                   mysql_engine='NDBCLUSTER',
                   mysql_charset='utf8',
                   extend_existing=True)

   if meta.bind.name != 'ibm_db_sa':
       Index('ix_objects_namespace_id_name',
             objects.c.namespace_id,
             objects.c.name)

   return objects

Running this version of the script results in the following table (as described by the ’show create’ SQL command):

CREATE TABLE `metadef_objects` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `namespace_id` int(11) NOT NULL,
 `name` varchar(80) NOT NULL,
 `description` text,
 `required` text,
 `schema` text NOT NULL,
 `created_at` datetime NOT NULL,
 `updated_at` datetime DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `ix_objects_namespace_id_name` (`namespace_id`,`name`)
) ENGINE=ndbcluster DEFAULT CHARSET=utf8 

Notice there is no FK now!   But, if I run the above create table SQL statements manually, the FK is created regardless of whether or not the unique constraint is defined:

mysql> CREATE TABLE `metadef_objects` (
    ->   `id` int(11) NOT NULL AUTO_INCREMENT,
    ->   `namespace_id` int(11) NOT NULL,
    ->   `name` varchar(80) NOT NULL,
    ->   `description` text,
    ->   `required` text,
    ->   `schema` text NOT NULL,
    ->   `created_at` datetime NOT NULL,
    ->   `updated_at` datetime DEFAULT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY `ix_objects_namespace_id_name` (`namespace_id`,`name`),
    ->   CONSTRAINT `metadef_objects_ibfk_1` FOREIGN KEY (`namespace_id`) REFERENCES `metadef_namespaces` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
    -> ) ENGINE=ndbcluster DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.10 sec)

mysql> show create table metadef_objects;                                                                                                                                                      
| metadef_objects | CREATE TABLE `metadef_objects` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `namespace_id` int(11) NOT NULL,
  `name` varchar(80) NOT NULL,
  `description` text,
  `required` text,
  `schema` text NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `ix_objects_namespace_id_name` (`namespace_id`,`name`),
  CONSTRAINT `metadef_objects_ibfk_1` FOREIGN KEY (`namespace_id`) REFERENCES `metadef_namespaces` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=ndbcluster DEFAULT CHARSET=utf8 |