Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion persistent-mysql/Database/Persist/MySQL.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,35 @@ putManySql' (filter isFieldNotGenerated -> fields) ent n = q
, Util.commaSeparated updates
]

-- | Generate the default foreign key constraint name for a given source table and
-- source column name, truncated to fit MySQL's 64 character identifier limit.
--
-- MySQL, unlike Postgres, does not silently truncate identifiers that are too long;
-- it raises an error instead (\"Identifier name ... is too long\"). Without this,
-- persistent would suggest foreign key constraint names that MySQL simply refuses to
-- create. This mirrors the truncation approach @persistent-postgresql@ already uses
-- for the same underlying problem, just with MySQL's 64 character limit instead of
-- Postgres' 63.
refName :: EntityNameDB -> FieldNameDB -> ConstraintNameDB
refName (EntityNameDB table) (FieldNameDB column) =
let
overhead = T.length $ T.concat ["_", "_fkey"]
(fromTable, fromColumn) = shortenNames overhead (T.length table, T.length column)
in
ConstraintNameDB $
T.concat [T.take fromTable table, "_", T.take fromColumn column, "_fkey"]
where
maximumIdentifierLength :: Int
maximumIdentifierLength = 64

shortenNames :: Int -> (Int, Int) -> (Int, Int)
shortenNames overhead (x, y)
| x + y + overhead <= maximumIdentifierLength = (x, y)
| x > y = shortenNames overhead (x - 1, y)
| otherwise = shortenNames overhead (x, y - 1)

mysqlMkColumns
:: [EntityDef] -> EntityDef -> ([Column], [UniqueDef], [ForeignDef])
mysqlMkColumns allDefs t = mkColumns allDefs t emptyBackendSpecificOverrides
mysqlMkColumns allDefs t =
mkColumns allDefs t $
setBackendSpecificForeignKeyName refName emptyBackendSpecificOverrides
2 changes: 1 addition & 1 deletion persistent-mysql/persistent-mysql.cabal
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: persistent-mysql
version: 2.13.1.6
version: 2.13.1.7
license: MIT
license-file: LICENSE
author: Felipe Lessa <felipe.lessa@gmail.com>, Michael Snoyman
Expand Down
9 changes: 3 additions & 6 deletions persistent-mysql/test/main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ main = do
, CustomPrimaryKeyReferenceTest.migration
, MigrationColumnLengthTest.migration
, TransactionLevelTest.migration
, -- , LongIdentifierTest.migration
ForeignKey.compositeMigrate
, LongIdentifierTest.migration
, ForeignKey.compositeMigrate
]
PersistentTest.cleanDB
ForeignKey.cleanDB
Expand Down Expand Up @@ -237,10 +237,7 @@ main = do
MigrationIdempotencyTest.specsWith db
MigrationTest.specsWith db
CustomConstraintTest.specs db
-- TODO: implement automatic truncation for too long foreign keys, so we can run this test.
xdescribe
"The migration for this test currently fails because of MySQL's 64 character limit for identifiers. See https://github.com/yesodweb/persistent/issues/1000 for details"
$ LongIdentifierTest.specsWith db
LongIdentifierTest.specsWith db
GeneratedColumnTestSQL.specsWith db
JSONTest.specs

Expand Down
2 changes: 1 addition & 1 deletion persistent-test/src/LongIdentifierTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import Init

-- This test creates very long identifier names. The generated foreign key is over the length limit for Postgres and MySQL
-- persistent-postgresql handles this by truncating foreign key names using the same algorithm that Postgres itself does (see 'refName' in Postgresql.hs)
-- MySQL currently doesn't run this test, and needs truncation logic for it to pass.
-- persistent-mysql does the same, using MySQL's 64 character limit (see 'refName' in MySQL.hs)
share
[mkPersist sqlSettings, mkMigrate "migration"]
[persistLowerCase|
Expand Down