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
Fix #8627: invalidate table schema cache on DDL #8652
Fix #8627: invalidate table schema cache on DDL #8652
Conversation
I am unsure about naming for |
@@ -537,6 +541,7 @@ public function createTable($table, $columns, $options = null) | |||
public function renameTable($table, $newName) | |||
{ | |||
$sql = $this->db->getQueryBuilder()->renameTable($table, $newName); | |||
$this->requireTableSchemaRefreshment($table); |
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.
I think we should drop $_refreshTableName
and simply call $this->db->invalidateTableSchema($table)
for the following reasons:
- The new
invalidateTableSchema()
can be used by users directly in case they use raw SQL to modify a table. - Although methods like
renameTable()
doesn't change the table (because it doesn't execute the SQL), it is not likely that you would call this method without executing it. Moreover, even if you don't execute the SQL, the only loss is that we incorrectly invalidated the table schema and thus cause an extra table schema reloading.
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.
Imagine following:
$db = Yii::$app->db;
$command = $db->createCommand()->dropTable('foo'); // prepare
// some manipulate 'foo' table data
// ...
$command->execute(); // actual execution.
Or:
$db = Yii::$app->db;
$dropCommand = $db->createCommand()->dropTable('tmp');
if ($db->schema->getTableSchema('tmp')) {
$dropCommand->execute(); // drop 1
}
$db->createCommand()->createTable('tmp', [...]);
// manipulate table 'tmp'
$dropCommand->execute(); // drop 2
Current implementation ensures cache invalidated in all cases, while simplifying it will produce unexpected results.
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.
Make sense.
I think _refreshTableName
should be set null when the command is being reused. Otherwise whenever you are reusing the command for other purposes, it will mark the table schema as invalid. Not very common, but should be similar to the use case you gave.
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.
Applied.
We also have Another arguable change is the addition of |
This can be done, but in this case
I can't find any other way as raw SQL may content any DDL statement. We may attempt to parse the SQL string, searching for some particular keywords, but DDL syntax may vary from one DBMS to other. I don't think such appraoch will be reliable. |
Refreshing schema at migration start is the safest approach I can think of. |
Well, it's very rare that you would need to refresh all table schemas.
Our goal is to make sure schema-changing methods are working correctly. For raw SQL execution, we should emphasize by documentation that manually schema refresh is required. As you can see, refreshing schema in
It doesn't help in this case (e.g., you modify a table schema followed by row insertions in the same migration). |
Right. Then I like the plan and PR. I see nothing wrong in having |
Disagreed. The problem, which is exposed at the issue #8627, is about unreliable DB migrations applying. Currently there are many migration written already, which may cause this problem appear at certain circumstances. We must ensure migration code as it mentioned in the docs is reliable. Enforcing developers to recheck and rewrite thier old migrations does not seem right.
This is true for the DDL code written for specific business logic support inside regular application code. But for migration this will produce bad user experience: why should I manually clear table schema cache inside migration, which exists to change table schemas?
Yes, it does not. Still it is unlikely |
@klimov-paul @samdark When you need to refresh multiple tables, |
Yes, it will be inconsistencies in complex cases. Sure we need to warn developers about it. But we need to handle the common migration flow automatically. $this->execute('CREATE TABLE status ...');
$this->insert('status', ['name' => 'some']);
// another migration:
$this->execute('ALTER TABLE status ...');
$this->insert('status', ['title' => 'some']); To make it work developer have to write it in following way: $this->execute('CREATE TABLE status ...');
$this->insert('status', ['name' => 'some']);
// another migration:
$this->execute('ALTER TABLE status ...');
$this->db->refreshTableSchema('status'); // does not look right
$this->insert('status', ['title' => 'some']); Do not forget, we are talking about migrations, which are already written! Which were promised to be working. We are fixing a bug - not creating new feature. |
My point is that when you are working with multiple DBs, for one DB you don't need to refresh schema while for the other, you will have to. And even for working with a single DB, if you use |
Simply because it enforces to add extra redundant code inside the migration. Migration I can agree on removal of |
That's only when you are using raw SQLs. So it really depends on how common it is that you would use raw SQLs to adjust table schemas (and if it is indeed common, then is it common to use raw SQLs to insert rows). Also when the migrations are working with a different DB or using
This doesn't solve the problem if you are altering a table and inserting rows into it in the same migration. |
I saw usage of SQL in migrations many times so it's indeed a common practice.
OK. Fine with me to leave it there. |
Technically schema refresh could be solved by parsing low level requests for |
What if the SQLs are executed via
What if it's done via a stored procedure? |
Well, then we can't detect it. I understand what you're talking about: partial solution is worse than no solution in this case but I really don't like doing |
For common migration - it will! |
Thanks for the explanation. Are we currently disabling schema caching in migrations? I don't see the code there yet. |
Disabling |
I thought we already did this. Okay, I would prefer we refresh schema at the beginning of each migration. |
|
A changelog entry? Codewise it looks good to me. |
A changelog entry already present. |
Right. Then it's complete. |
…a-cache Fix #8627: invalidate table schema cache on DDL
👍 |
1 similar comment
👍 |
Great work 👍 |
Fix #8627: invalidate table schema cache on DDL
Any DDL statement composed using
yii\db\Command
methods automatically refreshing related table schema.Migration::execute()
refreshes entire database schema, because raw SQL may contain DDL, which changes several tables.