Problem/Motivation
When altering a table, the MySQL Schema class needs to check for existing indexes and recreate them if
1. You alter a field and the new spec is varchar
2. There's already an index on this field
When a table is created a field or an index createKeySql fires getNormalizedIndexes which will in turn fire shortenIndex and shorten the index but if you just alter then it will never fire because the index is already there.
This issue came up in #2769603: Syntax error or access violation: 1071 Specified key was too long.
Proposed resolution
Use SHOW INDEX to check for existing indexes on fields being altered into a varchar.
Remaining tasks
User interface changes
None
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #20 | mysql_schema_class-2770321-20.patch | 2.32 KB | jibran |
| #20 | interdiff.txt | 2.42 KB | jibran |
Comments
Comment #2
jibranHere is a test only patch courtesy @chx
Comment #3
dawehnerThis gonna be fun to solve ... because in order to do that we would need the full schema of the table, which is not something we have.
Comment #4
chx commentedhttp://dev.mysql.com/doc/refman/5.7/en/show-index.html
return db_query('SHOW INDEX FROM {tablename} WHERE column_name = :new_column_name')->fetch();or somesuch. If there are results, checkSub_part.Comment #5
jibranIs it passing because of #2770319: Testbot MySQL config doesn't use large prefix hiding potential errors?
Comment #6
chx commentedRe-roll it with the SET GLOBAL commands by three db_query and we will see.
Comment #7
jibranHere we go.
Comment #9
chx commentedNote: the testbot my.cnf is http://cgit.drupalcode.org/drupalci_testbot/tree/containers/database/mys... this.
I do not understand anything. Not only does this fail when we supposedly switch on large index support, it fails just before row format alter, same for jibran. But for me, it fails *after* the alter, on line 706, not on line 703. To recap:
Comment #10
morgantocker commentedUpdating description based on 767 not being a limitation but a default.
Comment #11
morgantocker commented@chx: I believe above fails because the ROW_FORMAT=COMPACT instead of DYNAMIC.
Comment #12
chx commentedThere are two questions here, really: a) why does #2 pass? b) why does #7 fail before the row format change.
Comment #13
morgantocker commentedI apologize - I am not quite sure what you mean by #2 and #7 (not a drupal developer). But could this be what you are describing:
http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_inn...
(It now defaults strict, but it won't be in the version CI is using. It makes sense to turn it to strict for CI, since you typically want to catch these errors.)
Comment #14
chx commentedOh I meant #2, a patch and #7, another patch , it's the comment numbers of this issue. I apologize for not being clear.
Comment #15
morgantocker commentedSo I have an answer for you. The setting innodb_large_prefix is escalating a warning to an error. Here are the test cases in MySQL-only form, which is a little easier to follow:
Comment #16
chx commentedOK, we now understand what is happening. My local environment is MySQL 5.7.
So to recap:
innodb_large_prefixgot introduced in 5.5. This allows creating longer than 767 byte indexes if all of the following is in place:In 5.5 the default for
innodb_large_prefixwas OFF but in 5.7 it is ON. Soon you won't be able to switch it off at all as it is deprecated. Besides potentially allowing for larger indexes it also causes MySQL to stop truncating indexes with a warning and throw an error instead. This is visible for example in this https://bugs.mysql.com/bug.php?id=68453 bug report. This is the reason the bot doesn't fail in #2 but fails in #7. Note: it should fail and the bot needs to be reconfigured as in the sister issue, we are hiding a warning and if in a future MySQL release wheninnodb_large_prefixgets removed and hardwired to ON we are SOL.As for the
ROW_FORMATagain the difference is in 5.7 vs 5.5: 5.7 has a new setting calledinnodb_default_row_formatwhich isDYNAMICby default while previously the row format defaulted toCOMPACTand you needed to change this table by table. This is why bot / jibran fails before theROW_FORMATchange while my environment goes to the last error as visible in the screenshot attached. It also means that for complete testing this is necessary because people will have bothCOMPACTandDYNAMICtables lying around.Comment #17
chx commentedAnd , before I forget: thank you Morgan for your patient guidance, it has been invaluable help to understand this problem.
Comment #18
jibranThanks @morgantocker this is a perfect explanation.
Comment #19
morgantocker commented@chx: one quick comment in response to future changes -
The concept of file formats itself is really just backwards compatibility (the newer being the super-set to the previous, and there are not typically pros/cons of each. See my answer on StackExchange here). I think you should expect both innodb_large_prefix + innodb_file_format to disappear at some point.
Comment #20
jibranHere is new patch after the discussion courtesy @chx.
Comment #22
chx commentedIn fact... the alters after this is not needed because when we switch to COMPACT then the system dies if the index is 192 at this point. So just switch to COMPACT and assert the index is indeed 191.
Comment #23
jibranYou mean remove line 731,735 and 736?
Comment #24
stefan.r commentedDoesn't Schema::addIndex() silently set this to be 191 anyway? See Schema::getNormalizedIndexes().
Comment #25
jibranThe problem is not with Schema::addIndex(). When you call
Schema::changeField()and don't add new key and index, it doesn't callSchema::createKeysSql()which callsSchema::getNormalizedIndexes().Quoting the issue summary
From
core/lib/Drupal/Core/Database/Driver/mysql/Schema.phpSee
if ($keys_sql = $this->createKeysSql($keys_new)) {only adds new keys and index. We should also check existing index and specs of the new column.Comment #26
stefan.r commentedYes, I mean in the proposed fix, not the current situation -- when altering a field to be a varchar of 191+ characters, we would shorten the index to be 191 characters. So #22 would not apply (the system wouldn't die -- it would just silently shorten the index (or the field length, I don't remember).
Comment #27
chx commentedExactly, my comment might have been too terse but I said "and assert the index is indeed 191" . That's what I meant.
Comment #40
smustgrave commentedCurious if this one is still an issue?