fix(SQLite): preserve indexes and foreign keys when altering an introspected table - #7392
Conversation
…spected table
The table-recreation path maps index and foreign-key columns to the
altered table through getDiffColumnNameMap(), which is keyed by the
bare column names (Column::getName()). The index and foreign-key
column accessors of an introspected table return the names in their
quoted form ('"sku"'), so every map lookup missed and the branch
meant to drop indexes referencing dropped columns silently dropped
every index and foreign key of the table instead.
Unquote the column names before the lookup, so only indexes and
foreign keys whose columns were actually dropped are removed.
Signed-off-by: Fabrizio Balliano <fabrizio.balliano@gmail.com>
6d58dd6 to
4f8d03e
Compare
8502adc to
abc5d06
Compare
…spected table
When the altered table is obtained through the introspection API, its
index and foreign-key column names are marked as quoted, so the legacy
Index::getColumns() / ForeignKeyConstraint::getLocalColumns() accessors
return them in quoted form ('"sku"'). The SQLite table-recreation path
maps those columns to the altered table through getDiffColumnNameMap(),
which is keyed by the unquoted column names, so every lookup missed and
the branch meant to drop indexes referencing dropped columns silently
dropped every index and foreign key (and the primary key) of the table
instead.
Read the referencing column names through the non-deprecated
Index::getIndexedColumns() and
ForeignKeyConstraint::getReferencingColumnNames() accessors and compare
their unquoted identifier values, so the lookup is agnostic of whether
the table was introspected or built in memory.
Add a regression test to SchemaManagerFunctionalTestCase so the
scenario, which is not platform-specific, runs against every platform.
Signed-off-by: Fabrizio Balliano <fabrizio.balliano@gmail.com>
abc5d06 to
a03a27d
Compare
|
Reworked again after running the suite on CI. Normalizing the legacy accessors ( Instead the fix stays in the SQLite table-recreation path but no longer relies on the quoted legacy accessors. It reads the referencing column names via the non-deprecated The regression test lives in I ran the full suite on my fork to check CI: fballiano#1, all green. |
|
Sorry @morozov I forgot to mark this for review 🤦🏻 Hope it's ok now, I'm not sure about your previous note about the test suite actually. |
morozov
left a comment
There was a problem hiding this comment.
Directionally, this looks right. Still needs some polishing.
…ospection The referential actions of a foreign key created without explicit actions are reported inconsistently across platforms (MariaDB reports RESTRICT, MySQL reports NO ACTION), so the post-alteration foreign keys are compared against the pre-alteration introspection instead of the in-memory definition.
|
@morozov thank you, I hope I addressed all the issues in the right way, CI is green again :-) |
…me columns
When an index is dropped and another one over the same columns is added,
AbstractMySQLPlatform combines both into a single ALTER TABLE statement,
so that the columns are never left unindexed. It compares the two column
lists through the legacy Index::getColumns() accessor, which returns the
raw map keys: the introspection API marks them as quoted ('"parent_id"'),
while an index built in memory keeps them bare ('parent_id'). The two
lists therefore never match when the dropped index comes from an
introspected table, the statements are not combined, and the drop is
emitted on its own.
That makes the alteration fail whenever the dropped index is the one
backing a foreign key: InnoDB rejects the drop with "Cannot drop index
'X': needed in a foreign key constraint" (which disabling
foreign_key_checks does not lift), even though the added index would
provide the same cover.
Read the column names through the non-deprecated
Index::getIndexedColumns() accessor and compare their unquoted identifier
values, so the comparison is agnostic of whether the index was
introspected or built in memory, as done in #7392 for the same accessor
in the SQLite table-recreation path.
Signed-off-by: Fabrizio Balliano <fabrizio.balliano@gmail.com>
Summary
alterTable()on SQLite silently drops every index and foreign key (and the primary key) of the altered table whenever the table was obtained through the new introspection API. Rows survive the recreation; the indexes and foreign keys are gone, with no error or deprecation.Root cause
The legacy column-name accessors of an introspected table are inconsistent.
Column::getName()returns the bare, unquoted name (sku), butIndex::getColumns()andForeignKeyConstraint::getLocalColumns()/getForeignColumns()return the raw map keys, which the new introspection API marks as quoted ("sku"). The SQLite table-recreation path maps index and foreign-key columns to the altered table throughgetDiffColumnNameMap(), which is keyed by the unquoted column names, so every lookup misses and the branch meant to drop indexes referencing dropped columns:fires for every index and foreign key of the table.
The existing
testNonSimpleAlterTableCreatedFromDDLdoes not catch this because it uses the deprecatedintrospectTable(), which returns legacy-unquoted names that happen to match the map.Fix
The table-recreation path now reads the referencing column names through the non-deprecated
Index::getIndexedColumns()andForeignKeyConstraint::getReferencingColumnNames()accessors and compares their unquoted identifier values. This makes the lookup agnostic of whether the table was introspected (which marks its column names as quoted) or built in memory, without relying on the quoted form of the deprecatedgetColumns()/getLocalColumns()accessors.Note: normalizing those deprecated accessors to return unquoted names was considered but rejected, because their quoted form is load-bearing for quoting propagation in user-built schemas (it would break
testQuotedColumnInForeignKeyPropagationacross all platforms).The regression test, which is not platform-specific, is added to
SchemaManagerFunctionalTestCaseso it runs against every platform. It is built through the Schema API and fails without the fix (indexes and foreign keys dropped) and passes with it.