[MDEV-26224] InnoDB fails to remove AUTO_INCREMENT attribute Created: 2021-07-23 Updated: 2022-04-26 Resolved: 2022-04-21 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | Storage Engine - InnoDB |
| Affects Version/s: | 10.4.4, 10.5.0, 10.6.0 |
| Fix Version/s: | 10.4.25, 10.5.16, 10.6.8, 10.7.4, 10.8.3, 10.9.1 |
| Type: | Bug | Priority: | Critical |
| Reporter: | Marko Mäkelä | Assignee: | Vladislav Lesin |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | regression-10.4 | ||
| Issue Links: |
|
||||||||||||||||||||
| Description |
|
While analyzing
The value in the page would be wrongly updated like this:
Note: in the native ALTER TABLE API there is no flag for removing the AUTO_INCREMENT attribute. In this case, ha_innobase::check_if_supported_inplace_alter() would observe ha_alter_info->handler_flags == ALTER_CHANGE_COLUMN_DEFAULT. InnoDB can support ALGORITHM=INSTANT removal of the AUTO_INCREMENT attribute, but it needs to remove the attribute from dict_table_t in commit_cache_norebuild(). For a table rebuild, there is no issue: after
the function page_set_autoinc() will not be invoked. |
| Comments |
| Comment by Marko Mäkelä [ 2021-07-23 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Before
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-07-23 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The following test shows a problem with FORCE (table rebuild) as well. We would expect the fields to contain 0, but the Perl code is reading the value 1 from both files.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-07-23 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There exist assertions in dict_table_t::instant_column() and elsewhere that assume that the AUTO_INCREMENT attribute will not change, it is not trivial to fix this, and the fix will have to be tested thoroughly with RQG against assertion failures. To fix my test case, we should change commit_cache_norebuild() and commit_try_rebuild() and probably introduce a flag for changing (removing) an AUTO_INCREMENT attribute. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-07-23 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
It turns out that this bug is actually caused by | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Vladislav Lesin [ 2022-04-11 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The above test contains error in perl code. PAGE_ROOT_AUTO_INC fields is read int $t_ instead of $_ for t2 table, but $_ is unpacked into $t2, so $t2 shows t1's PAGE_ROOT_AUTO_INC. If we fix it, we will see that INPLACE algorithm is not affected. For INPLACE algorithm new index metadata is created in prepare_inplace_alter_table_dict(), so there is no need to reset table->persistent_autoinc:
For INSTANT algorithm ha_innobase::prepare_inplace_alter_table() even don't call prepare_inplace_alter_table_dict(), because !(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) condition is true. And this is also the reason why we can't call commit_cache_norebuild() in ha_innobase::commit_inplace_alter_table(). To call commit_cache_norebuild() we need ha_alter_info->handler_ctx, which, in turns, is created in ha_innobase::prepare_inplace_alter_table if ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE:
INNOBASE_INPLACE_IGNORE also contains ALTER_COLUMN_DEFAULT, which is set for "ALTER TABLE t1 MODIFY c INT NULL" statement:
Marko explained why ALTER_COLUMN_DEFAULT flag is ignored: So to call commit_cache_norebuild() in ha_innobase::commit_inplace_alter_table() we need to change the logic ha_innobase::prepare_inplace_alter_table(). At the other hand, why do we need to call ommit_cache_norebuild()? Why just don't reset table->persistent_autoinc in ha_innobase::commit_inplace_alter_table() directly:
persistent_autoinc is set if TABLE::found_next_number_field is set (during table open) in SQL layer. So persistent_autoinc reset should be crash-safe. And it's not supposed to be rolled back if ha_innobase::commit_inplace_alter_table() is finished successfully. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2022-04-20 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I think that it could be cleaner if the assignment to reset ha_alter_info->group_commit_ctx=NULL was not skipped in ha_innobase::commit_inplace_alter_table(), but the metadata for all partitions were updated. The proposed solution looks technically correct, but then the comment before the relaxed assertion in ha_partition::commit_inplace_alter_table() will have to be adjusted. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Vladislav Lesin [ 2022-04-20 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
marko, below is edited version of my message about partitions iteration for INNOBASE_INPLACE_IGNORE flags in ha_innobase::commit_inplace_alter_table() from our slack discussion. Why we can't do the same thing for INNOBASE_INPLACE_IGNORE alter table flags in ha_innobase::commit_inplace_alter_table() as for commit_cache_norebuild(), i.e. iterate partitions with ha_alter_info->group_commit_ctx and don't change the code in ha_partition? The question is the same as for none-partitioned tables. The answer is the same as here. ha_alter_info->group_commit_ctx is analogue of ha_alter_info->handler_ctx for partitioned tables. ha_alter_info->handler_ctx is filled in ha_innobase::prepare_inplace_alter_table(), and partition engine invokes ha_innobase::prepare_inplace_alter_table() for each partition and pushes created ha_alter_info->handler_ctx into ha_alter_info->group_commit_ctx array. ha_innobase::prepare_inplace_alter_table() don't create ha_alter_info->handler_ctx for INNOBASE_INPLACE_IGNORE flags(the details are here), so ha_alter_info->group_commit_ctx array is filled with NULL's for INNOBASE_INPLACE_IGNORE. To change metadata for all partitions for INNOBASE_INPLACE_IGNORE flags in ha_innobase::commit_inplace_alter_table() we need to change the logic in ha_innobase::prepare_inplace_alter_table() for those flags. I would not do this for such small change as resetting persistent_autoinc. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2022-04-20 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
vlad.lesin, I agree that your proposed solution is technically correct, and possibly also the simplest solution. The reason why changes to partitioned tables are committed in a single call is durability. This change does not affect durability, because it is solely about updating the InnoDB data dictionary caches (one InnoDB dict_table_t per partition or sub-partition). I would only request you to revise the comment in ha_partition::commit_inplace_alter_table() to match the revised design. |