fix(plugin): EXPOSED-1033 Child tables that reference parent with index generate duplicate ALTER statements - #2919
Conversation
…ex generate duplicate ALTER statements
| ) | ||
|
|
||
| val generated = generator.generate() | ||
| val generatedFilenames = generated.map { it.substringAfter("__").substringBeforeLast('.') } |
There was a problem hiding this comment.
This test only covered the number of generated files and their SQL caused no issues because it's ok to execute CREATE TABLE IF NOT EXISTS multiple times. So multiple scripts held the parent table name description, as well as duplicate CREATE statements. This now checks that the lack of duplicates generates unique name descriptions.
| .getClassesInPackage(config.tablesPackage) | ||
| .mapNotNull { it.tableOrNull() } | ||
| val sortedTables = SchemaUtils.sortTablesByReferences(foundTables.toList()) | ||
| val generatedSQL = mutableSetOf<String>() |
There was a problem hiding this comment.
The most ideal scenario would be to apply the output statements below to a TestContainer to ensure the next method call is run against an accurate state database.
This solution covers the case when containers are not being used, by storing statements as they come for a future check. It might not be the most performant if there are many tables to check. But it should avoid more edge cases compared to calling the method once for all tables and attempting to separate the output logically (not all scripts will start with CREATE for example).
An alternative could be to attempt to create a variant of sortTablesByReferences() that returns grouped tables (that reference each other) to be handled together, for a potentially smaller temp store.
Description
Summary of the change: Check generated SQL from task
generateMigrationsto ensure duplicate statements are not written across multiple migration scripts.Detailed description:
generateMigrationscallsMigrationUtils.statementsRequiredForDatabaseMigration()for each detected table, so it can create a new migration script for that table. If this method was called on a group of tables that reference each other, it would ensure that no duplicate statements are generated. Since this method is instead called once per table, a child table generates all SQL needed to create the table it references, as well as itself. This causes no issue for basic tables, asCREATE TABLE IF NOT EXISTScan be called repeatedly. But if the table holds something like an index constraint, the child script will attempt to call the same parent'sALTER, resulting inconstraint already existsissues.A side effect of this is naming, as the script takes its descriptive name from the first compatible SQL statement. So the script may end up having a similar name distinguishable only by versioning. Take for example the following tables:
Running the task with default settings would generate the following files:
V20260908203419__CREATE_TABLE_TABLE_A.sql<-- create + alter for table_a ✅V20260908203419.1__CREATE_TABLE_TABLE_B.sql<-- create + alter for table_b ✅V20260908203419.2__CREATE_TABLE_TABLE_A.sql<-- create + alter for table_a + table_b + table_c ❌Type of Change
Please mark the relevant options with an "X":
Affected databases:
Checklist
Related Issues
EXPOSED-1033