apply truncation rules to all naming-convention names
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
def _column_names(constraint, table):
return '_'.join((c if isinstance(c, basestring) else c.name) for c in constraint.columns)
naming_convention = {
'fk': 'fk_%(table_name)s_%(column_names)s_%(referred_table_name)s',
'ix': 'ix_%(table_name)s_%(column_names)s',
'column_names': _column_names,
}
Base = declarative_base()
Base.metadata.naming_convention = naming_convention
class A(Base):
__tablename__ = 'test_very_long_name_abcdefghijklmnopqrstuvwxyz'
__table_args__ = Index(None, 'abcdefghijklmnopqrstuvwxyz'),
id = Column(Integer, primary_key=True)
abcdefghijklmnopqrstuvwxyz = Column(Integer)
class B(Base):
__tablename__ = 'test_very_long_name_abcdefghijklmnopqrstuvwxyz_b'
id = Column(Integer, primary_key=True)
abcdefghijklmnopqrstuvwxyz = Column(Integer, ForeignKey('test_very_long_name_abcdefghijklmnopqrstuvwxyz.id'))
e = create_engine('postgresql:///test', echo=True)
Base.metadata.create_all(e)
raw_input('check the names...')
e.execute('DROP TABLE test_very_long_name_abcdefghijklmnopqrstuvwxyz, test_very_long_name_abcdefghijklmnopqrstuvwxyz_b')
This results in the following SQL (removed unrelated parts):
CREATE INDEX ix_test_very_long_name_abcdefghijklmnopqrstuvwxyz_abcde_5f65 ON test_very_long_name_abcdefghijklmnopqrstuvwxyz (abcdefghijklmnopqrstuvwxyz)
CREATE TABLE test_very_long_name_abcdefghijklmnopqrstuvwxyz_b (
...
CONSTRAINT fk_test_very_long_name_abcdefghijklmnopqrstuvwxyz_b_abcdefghijklmnopqrstuvwxyz_test_very_long_name_abcdefghijklmnopqrstuvwxyz FOREIGN KEY(abcdefghijklmnopqrstuvwxyz) REFERENCES test_very_long_name_abcdefghijklmnopqrstuvwxyz (id)
)
SQLAlchemy properly truncates the index name and appends a hash for uniqueness, but the constraint name is left unmodified, resulting in Postgres truncating it to 63 characters (without adding a hash to the end to ensure uniqueness).
I think this behavior is quite inconsistent and may even result in problems depending on the structure since it may result in two truncated names being the same even though they were different before. It's also somewhat ugly in Alembic migration scripts as you'll have to use op.f('...')
for the index to ensure it's truncated by SA while it's optional for FK names since they are not touched even if too long.
Comments (6)
-
repo owner -
reporter Guess it's unavoidable
Yeah... for FKs or multi-column indexes it's sometimes easy to end up with very long index/constraint names...
-
repo owner - changed milestone to 1.2
- changed component to schema
- changed title to apply truncation rules to all naming-convention names
the truncation logic in compiler -> _prepared_index_name would need to be pulled out and generalized to all name prep for constraint names.
the 1.1 milestone is already overfull and needs to be shrinked a lot more. this needs tests in test_metadata->NamingConventionTest and an implementation in compiler.py provided in order to be pushed up sooner.
-
repo owner - changed milestone to 1.4
moving this out for lower priority
-
repo owner a note that Alembic needs rules added to compare.py for each constraint that does naming truncation, see https://bitbucket.org/zzzeek/alembic/issues/421/handle-or-track-postgres-renaming-of-long.
-
repo owner the DDLCompiler should be given some public facing method that supplies the end-result name of all constraints which Alembic can call into; right now we have to code to _prepared_index_name().
- Log in to comment
I'm surprised PG truncates it instead of raising an error. that's pretty weird.
the point of the truncation is when we're creating "throwaway" names that we aren't going to want to know later. This occurs for indexes because SQLA has for a long time had the "index=True" flag on Column which is the one (almost) single place where we break another one of SQLA's cardinal rules, that we never make up names :). to make an index we have to make up a name, so the truncation was added there so that the index can get created in all cases.
naming convention's purpose is so that we can know exactly the name of any constraint or object we build. if those names are truncating, I guess that doesn't defeat its purpose fully because at least Alembic can re-locate the same name, though I'd not want to use naming conventions that are winding up in truncation. Guess it's unavoidable.
the way the mechanics work here right now, to add the truncation in would mean that it only takes effect for naming-convention-led names. it still would not take effect for manually-specified names. the truncation here is generally not something that's at the "schema" level at all, it's used for on-the-fly column labels. its use in index names is idiosyncratic.