I'm using MySQL and want to use a combined primary-key where the
second column is autogenerated while the first part is an assigned
value. This is how my Table-definition looks like:
table_a = Table('table_a', metadata,
Column('assigned_id', Integer(), primary_key=True,
autoincrement=False),
Column('id', Integer(), primary_key=True, autoincrement=True),
mysql_engine='InnoDB'
)
However, the MySQL-Dialect is not able to generate this table:
OperationalError: (OperationalError) (1075, 'Incorrect table
definition; there can be only one auto column and it must be defined
as a key')
The DDL generated looks like this:
CREATE TABLE table_a (
assigned_id INTEGER NOT NULL,
id INTEGER NOT NULL AUTO_INCREMENT,
PRIMARY KEY (assigned_id, id)
)ENGINE=InnoDB
What's missing here is the explicit key for the autoincremented
column.
The correct code has to look like this:
CREATE TABLE table_a (
assigned_id INTEGER NOT NULL,
id INTEGER NOT NULL AUTO_INCREMENT,
PRIMARY KEY (assigned_id, id),
KEY key_autoinc(id)
)ENGINE=InnoDB
This error only occurs if the autoincremented column is not the first
column of the primary key and innodb is used as storage engine. I
tried to work around this problem by explicitly setting autoincrement
to False and altering the table with an DDL.execute_at()-call. That
worked for the DDL but afterwards the generated id was not propagated
to dependent relations (the docs say autoincrement is only used during
DDL generation but that's not what I experienced). Since this solution
is not very elegant and didn't work for me due to the broken id
propagation, I looked into the MySQL-Dialect of SQLA itself.
I've now fixed the problem locally by modifying the MySQL-Dialect that
comes with SQLA. I copied the visit_create_table method from
DDLCompiler ton the MySQLDDLCompiler class and modified it so that a
separate Key is appended if an autoincremented column exists that is
not the first PK column)
To reproduce the error:
import logging
import sqlalchemy
from sqlalchemy import *
from sqlalchemy.orm import *
metadata = MetaData()
table_a = Table('table_a', metadata,
Column('assigned_id', Integer(), primary_key=True,
autoincrement=False),
Column('id', Integer(), primary_key=True, autoincrement=True),
mysql_engine='InnoDB'
)
# Models
class ItemA(object):
def __init__(self, name):
self.name = name
# Mapping
mapper(ItemA, table_a)
# Init engine
engine = create_engine('mysql://localhost/sqltest', connect_args=
{'user':'root', 'passwd':''}, echo=True)
engine_logger = sqlalchemy.log.instance_logger(engine)
engine_logger.setLevel(logging.DEBUG)
metadata.drop_all(engine)
metadata.create_all(engine)
session_factory = scoped_session(sessionmaker(bind=engine))
session = session_factory()
itemA1 = ItemA(name = 'ItemA1')
session.add(itemA1)
session.commit()
In my modified method, I store the auto_incremented column in a local
variable ("auto_inc_column") and add the KEY-Part if this variable is
set. Most of the code is copy/pasted from the 0.6-version of the
DDLCompiler class, so this fix is probably not very elegant but it
works for me.
Here is the modified version that I currently use:
def visit_create_table(self, create):
table = create.element
preparer = self.dialect.identifier_preparer
text = "\n" + " ".join(['CREATE'] + \
table._prefixes + \
['TABLE',
preparer.format_table(table),
"("])
separator = "\n"
# if only one primary key, specify it along with the column
first_pk = False
auto_inc_column = None
for column in table.columns:
text += separator
separator = ", \n"
text += "\t" + self.get_column_specification(column,
first_pk=column.primary_key and not first_pk)
if column.primary_key and column.autoincrement and
first_pk:
auto_inc_column = column
if column.primary_key:
first_pk = True
const = " ".join(self.process(constraint) for constraint
in column.constraints)
if const:
text += " " + const
# On some DB order is significant: visit PK first, then the
# other constraints (engine.ReflectionTest.testbasic failed on
FB2)
if table.primary_key:
text += ", \n\t" + self.process(table.primary_key)
const = ", \n\t".join(
self.process(constraint) for constraint in
table.constraints
if constraint is not table.primary_key
and constraint.inline_ddl
and (not self.dialect.supports_alter or not
getattr(constraint, 'use_alter', False))
)
if const:
text += ", \n\t" + const
# append KEY for autoincrement column if necessary
if auto_inc_column is not None:
text += ", \n\t" + "KEY `idx_autoinc_%s`(`%s`)" %
(auto_inc_column.name, self.preparer.format_column(auto_inc_column))
text += "\n)%s\n\n" % self.post_create_table(table)
return text
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---