Yii2: db/Schema::getTableMetadata() broken

Created on 7 Nov 2017  路  7Comments  路  Source: yiisoft/yii2

What steps will reproduce the problem?

Run migrate/fresh more than once.

What is the expected result?

Command should run on each call.

What do you get instead?

On second call an exception is thrown because the migration table gets not created after truncate.

Additional info

| Q | A
| ---------------- | ---
| Yii version | 2.0.13
| PHP version | 7.0.18
| Operating system | Debian

The error was introduced in Yii 2.0.13.

The reason is the implementation of db/Schema::getTableMetadata(). The migration table does not exist but it's schema information are still present in file cache.

This is the current implementation:

if ($refresh || !isset($this->_tableMetadata[$name])) {
    ...
}

if (!array_key_exists($type, $this->_tableMetadata[$name])) {
    ...
}

And it should look like this:

if (!isset($this->_tableMetadata[$name])) {
    ...
}

if ($refresh || !array_key_exists($type, $this->_tableMetadata[$name])) {
    ...
}
db bug

Most helpful comment

@U-D13 thanks for details. It's definitely not okay.
But caching/purging logic is still the same from the old Schema, so it this issue probably existed for some time before.
Anyway I will dig into this.

All 7 comments

Em. yii\db\Schema::refreshTableSchema will be used in yii\console\controllers\MigrateController::truncateDatabase() and should take care of it.

The current implementation should work fine because it discards a complete table metadata if $refresh is true.

What's the database used? MySQL?

Yes, MySQL.

The current implementation should work fine because it discards a complete table metadata if $refresh is true.
@sergeymakinen

No, it does not work "fine".

use yii\console\controllers\MigrateController,
    yii\db\Connection,
    yii\db\Schema,
    yii\db\TableSchema;

MigrateController::truncateDatabase() gets table names from Schema::getTableSchemas(), where the TableSchema::$name for the migrations table is plain migration (or {prefix}migration if current Connection::$tablePrefix is {prefix}). The metadata for this table name is discarded.

But MigrateController::getMigrationHistory(), which is called when migrating up, gets the metadata for MigrateController::$migrationTable, defined as {{%migration}} by default. If the metadata for this table name is cached, Schema::getTableMetadata() will return that regardless of the value of $refresh (and the actual schema state). That's what this issue is all about.

I suppose a solution would involve tuning table prefix resolution for schema caching and/or migrations.

@U-D13 thanks for details. It's definitely not okay.
But caching/purging logic is still the same from the old Schema, so it this issue probably existed for some time before.
Anyway I will dig into this.

I confirm this.
An issue resides in yii\db\Schema::getCacheKey it was there for a long time:
a cache key doesn't resolve table names to raw names, so {{%table}} and table entries are stored/deleted as is.
I will add tests and fix it tonight.

This error is still present in the latest version. Running migrate/up once, then dropping/recreating db and running migrate/up again results in error because its still cached. The solution proposed in the original post fixes it and seems logical to me.

Was this page helpful?
0 / 5 - 0 ratings