-
Notifications
You must be signed in to change notification settings - Fork 1
[Enhance] Add support for renaming columns via the change
clause.
#22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good! I've left some recommendations below with regards to code simplicity and developer experience, however as you noted in the PR description more time does need to be spent making sure reversal / application of the new operation works.
I'm happy to help out here, but I'll explain how this is intended to work first.
Reversal
Reversal is simply concerned with transforming an operation so that it can be used in a rollback. For the CHANGE COLUMN old_col_name new_col_name column_definition
operation, reversal involves switching the order of names and replacing column_definition
with whatever it was prior to the change. If MODIFY COLUMN
has been implemented correctly already, this should be trivial as you'll just have to deal with swapping the names.
Application
This is concerned with 'reducing' multiple operations for the same thing (e.g. a table or column) such that a single SQL query needs to be executed rather than several individual queries. This is most useful when creating a new tenant database as each table can be created according to the entire history of migrations through one CREATE TABLE
command, instead of a CREATE TABLE
followed by several ALTER TABLE
commands.
What's unique about this new operation is that after it is executed, the column has a different name. Since we look for previous operations by column name, we might end up missing previous operations or reducing operations that aren't relevant. I say might because I haven't had time to think through this thoroughly. Given that we only apply one operation to another at a time, this might not be a problem.
At the very least, you'll need some logic added around TableOperation.php:219
as well as what you've already got, because this handles the application of an ALTER TABLE
to a CREATE TABLE
.
Here are some scenarios to think about:
- Applying a
CHANGE COLUMN
to anADD COLUMN
should result in anADD COLUMN
for the new name and column definition. - Applying a
CHANGE COLUMN
to aMODIFY COLUMN
should result in aMODIFY COLUMN
for the new name and column definition. - Applying a
CHANGE COLUMN
to aCHANGE COLUMN
should result in aCHANGE COLUMN
for the new name and definition. In the case that the names are the same (but switched) between them and the column definitions are identical, the operation shouldn't happen at all because there's no net change.
Writing all of this down, it seems to me like this should work identically to MODIFY COLUMN
except that the column operation's name (column name) should be the new column name.
See if you can work this logic into your changes and I'll help out if needed.
@@ -104,6 +104,18 @@ public function reverse(?ReversibleOperationInterface $originalOperation = null) | |||
$originalColumn->getOptions() | |||
); | |||
} | |||
|
|||
if ($columnOperation->getOperation() === ColumnOperation::CHANGE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition could be combined with the previous (as you've done with the switch statement lower down).
The second argument to ColumnOperation
just needs to be changed to $columnOperation->getOperation()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like it's gonna work!
P.S. @ryanrigby17 do you think we should add more test cases to test Josh's example scenarios?
@saria3rabbi Definitely, I think more tests would be great. I'm still trying to fully wrap my head around the intricacies of them 😅 |
@joshmcrae Thanks for the excellent write up. I've made changes based on what you've written and your review recommendations. This feels more correct now. I feel like the best way to test these now will be via running the actual migration commands. How are the migrations within the tests folder actually used? I was wonder if I should add some change command and see if they are working correctly. |
@ryanrigby17 those migrations are only used by A class like To reiterate what my concern is with renaming columns, I want to make sure the logic for applying table operations takes a column's new name into account once the change has been applied. Given that we only apply one migration to another at a time, and these are done in series, there's probably nothing to worry about here in reality. Take the following as an example: $migrations[] = \Exo\TableMigration::create('users')
->addColumn('username', ['type' => 'string']);
$migrations[] = \Exo\TableMigration::alter('users')
->changeColumn('username', 'email', ['type' => 'string']);
$migrations[] = \Exo\TableMigration::alter('users')
->addColumn('username', ['type' => 'integer']);
$migrations[] = \Exo\TableMigration::alter('users')
->changeColumn('username', 'user_id', ['type' => 'integer']); Reducing all of these into a single migration should be the equivalent of running: $migrations[] = \Exo\TableMigration::create('users')
->addColumn('email', ['type' => 'string'])
->addColumn('user_id', ['type' => 'integer']); Exo would get there by running If you think my reasoning is sound here, then there's no unique problem presented by |
…e Db and confirm it works.
@joshmcrae I've created a test case that utilizes migrations based on your provided examples. I'm happy that it's working how I still have one question regarding reducing ColumnOperations. The test case |
@ryanrigby17 It will have to be a single column operation, as you can't rename a column and then drop it by its new name in a single SQL statement. In other words, the following would not work: ALTER TABLE users
CHANGE user_id username varchar(255),
DROP COLUMN username; You'll get The reason it's not reducing those operations is because the name field for the
In this case they do share the same name and will be applied, but you cannot apply an We'll need to introduce the concept of 'before' and 'after' names for column operations. That way we can apply column operations for a given 'before' name to operations with a matching 'after' name. |
This PR adds support for the
change
clause to allow renaming columns.Portions I'm still not 100% on are the code parts concerning reversing/applying migrations on-top of other migrations. They will likely need closer scrutiny.
I hope to follow this up with another PR for renaming tables, if this PR proves valuable.