You've already forked phpmyadmin
mirror of
https://github.com/phpmyadmin/phpmyadmin.git
synced 2026-10-23 18:29:15 +03:00
738 lines
27 KiB
PHP
738 lines
27 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace PhpMyAdmin\Tests\Controllers\Database;
|
|
|
|
use PhpMyAdmin\Config;
|
|
use PhpMyAdmin\Config\PageSettings;
|
|
use PhpMyAdmin\Config\Settings;
|
|
use PhpMyAdmin\ConfigStorage\Relation;
|
|
use PhpMyAdmin\Controllers\Database\StructureController;
|
|
use PhpMyAdmin\Current;
|
|
use PhpMyAdmin\Dbal\DatabaseInterface;
|
|
use PhpMyAdmin\DbTableExists;
|
|
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
|
|
use PhpMyAdmin\Http\ServerRequest;
|
|
use PhpMyAdmin\Replication\Replication;
|
|
use PhpMyAdmin\Table\Table;
|
|
use PhpMyAdmin\Template;
|
|
use PhpMyAdmin\Tests\AbstractTestCase;
|
|
use PhpMyAdmin\Tests\Stubs\ResponseRenderer as ResponseStub;
|
|
use PhpMyAdmin\Tracking\TrackingChecker;
|
|
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
|
|
use PHPUnit\Framework\Attributes\CoversClass;
|
|
use ReflectionClass;
|
|
use ReflectionException;
|
|
use ReflectionMethod;
|
|
|
|
use function array_keys;
|
|
|
|
#[CoversClass(StructureController::class)]
|
|
#[AllowMockObjectsWithoutExpectations]
|
|
class StructureControllerTest extends AbstractTestCase
|
|
{
|
|
private ResponseStub $response;
|
|
|
|
private Relation $relation;
|
|
|
|
private Replication $replication;
|
|
|
|
private Template $template;
|
|
|
|
private Config $config;
|
|
|
|
/**
|
|
* Prepares environment for the test.
|
|
*/
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->config = new Config();
|
|
Config::$instance = $this->config;
|
|
$this->config->selectedServer['DisableIS'] = false;
|
|
Current::$table = 'table';
|
|
Current::$database = 'db';
|
|
|
|
$table = $this->createMock(Table::class);
|
|
// Expect the table will have 6 rows
|
|
$table->method('getRealRowCountTable')->willReturn(6);
|
|
$table->method('countRecords')->willReturn(6);
|
|
|
|
$dbi = $this->createMock(DatabaseInterface::class);
|
|
$dbi->method('getTable')->willReturn($table);
|
|
|
|
DatabaseInterface::$instance = $dbi;
|
|
|
|
$this->template = new Template($this->config);
|
|
$this->response = new ResponseStub();
|
|
$this->relation = new Relation($dbi);
|
|
$this->replication = new Replication($dbi);
|
|
}
|
|
|
|
/**
|
|
* Tests for getValuesForInnodbTable()
|
|
*/
|
|
public function testGetValuesForInnodbTable(): void
|
|
{
|
|
$class = new ReflectionClass(StructureController::class);
|
|
$method = $class->getMethod('getValuesForInnodbTable');
|
|
$dbi = DatabaseInterface::getInstance();
|
|
$config = Config::getInstance();
|
|
$controller = new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
$config,
|
|
);
|
|
// Showing statistics
|
|
$property = $class->getProperty('isShowStats');
|
|
$property->setValue($controller, true);
|
|
|
|
$currentTable = [
|
|
'ENGINE' => 'InnoDB',
|
|
'TABLE_ROWS' => 5,
|
|
'Data_length' => 16384,
|
|
'Index_length' => 0,
|
|
'TABLE_NAME' => 'table',
|
|
];
|
|
[$currentTable, , , $sumSize] = $method->invokeArgs(
|
|
$controller,
|
|
[$currentTable, 10],
|
|
);
|
|
|
|
self::assertTrue($currentTable['COUNTED']);
|
|
self::assertSame(6, $currentTable['TABLE_ROWS']);
|
|
self::assertSame(16394, $sumSize);
|
|
|
|
$currentTable['ENGINE'] = 'MYISAM';
|
|
[$currentTable, , , $sumSize] = $method->invokeArgs(
|
|
$controller,
|
|
[$currentTable, 10],
|
|
);
|
|
|
|
self::assertFalse($currentTable['COUNTED']);
|
|
self::assertSame(16394, $sumSize);
|
|
|
|
$controller = new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
$config,
|
|
);
|
|
|
|
$currentTable['ENGINE'] = 'InnoDB';
|
|
[$currentTable, , , $sumSize] = $method->invokeArgs($controller, [$currentTable, 10]);
|
|
self::assertTrue($currentTable['COUNTED']);
|
|
self::assertSame(10, $sumSize);
|
|
|
|
$currentTable['ENGINE'] = 'MYISAM';
|
|
[$currentTable, , , $sumSize] = $method->invokeArgs($controller, [$currentTable, 10]);
|
|
self::assertFalse($currentTable['COUNTED']);
|
|
self::assertSame(10, $sumSize);
|
|
}
|
|
|
|
/**
|
|
* Tests for the getValuesForAriaTable()
|
|
*/
|
|
public function testGetValuesForAriaTable(): void
|
|
{
|
|
$class = new ReflectionClass(StructureController::class);
|
|
$method = $class->getMethod('getValuesForAriaTable');
|
|
|
|
$dbi = DatabaseInterface::getInstance();
|
|
$config = Config::getInstance();
|
|
$controller = new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
$config,
|
|
);
|
|
// Showing statistics
|
|
$property = $class->getProperty('isShowStats');
|
|
$property->setValue($controller, true);
|
|
$property = $class->getProperty('dbIsSystemSchema');
|
|
$property->setValue($controller, true);
|
|
|
|
$currentTable = ['Data_length' => 16384, 'Index_length' => 0, 'Name' => 'table', 'Data_free' => 300];
|
|
[$currentTable, , , , , $overheadSize, $sumSize] = $method->invokeArgs(
|
|
$controller,
|
|
[$currentTable, 0, 0, 0, 0, 0, 0],
|
|
);
|
|
self::assertSame(6, $currentTable['Rows']);
|
|
self::assertSame(16384, $sumSize);
|
|
self::assertSame(300, $overheadSize);
|
|
|
|
unset($currentTable['Data_free']);
|
|
[$currentTable, , , , , $overheadSize] = $method->invokeArgs(
|
|
$controller,
|
|
[$currentTable, 0, 0, 0, 0, 0, 0],
|
|
);
|
|
self::assertSame(0, $overheadSize);
|
|
|
|
$controller = new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
$config,
|
|
);
|
|
[$currentTable, , , , , , $sumSize] = $method->invokeArgs(
|
|
$controller,
|
|
[$currentTable, 0, 0, 0, 0, 0, 0],
|
|
);
|
|
self::assertSame(0, $sumSize);
|
|
|
|
$controller = new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
$config,
|
|
);
|
|
[$currentTable] = $method->invokeArgs(
|
|
$controller,
|
|
[$currentTable, 0, 0, 0, 0, 0, 0],
|
|
);
|
|
self::assertArrayNotHasKey('Row', $currentTable);
|
|
}
|
|
|
|
/**
|
|
* Tests for hasTable()
|
|
*/
|
|
public function testHasTable(): void
|
|
{
|
|
$class = new ReflectionClass(StructureController::class);
|
|
$method = $class->getMethod('hasTable');
|
|
|
|
$dbi = DatabaseInterface::getInstance();
|
|
$controller = new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
Config::getInstance(),
|
|
);
|
|
|
|
// When parameter $db is empty
|
|
self::assertFalse(
|
|
$method->invokeArgs($controller, [[], 'table']),
|
|
);
|
|
|
|
// Correct parameter
|
|
$tables = ['db.table'];
|
|
self::assertTrue(
|
|
$method->invokeArgs($controller, [$tables, 'table']),
|
|
);
|
|
|
|
// Table not in database
|
|
$tables = ['db.tab1e'];
|
|
self::assertFalse(
|
|
$method->invokeArgs($controller, [$tables, 'table']),
|
|
);
|
|
}
|
|
|
|
/** @throws ReflectionException */
|
|
public function testDisplayTableList(): void
|
|
{
|
|
$this->config->set('ShowDbStructureCreation', true);
|
|
$this->config->set('ShowDbStructureLastUpdate', true);
|
|
$this->config->set('ShowDbStructureLastCheck', true);
|
|
|
|
$class = new ReflectionClass(StructureController::class);
|
|
$method = $class->getMethod('displayTableList');
|
|
|
|
$dbi = DatabaseInterface::getInstance();
|
|
$controller = new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
$this->config,
|
|
);
|
|
// Showing statistics
|
|
$class = new ReflectionClass(StructureController::class);
|
|
$showStatsProperty = $class->getProperty('isShowStats');
|
|
$showStatsProperty->setValue($controller, true);
|
|
|
|
$tablesProperty = $class->getProperty('tables');
|
|
|
|
$numTables = $class->getProperty('numTables');
|
|
|
|
$_REQUEST['db'] = 'my_unique_test_db';
|
|
|
|
$numTables->setValue($controller, 0);
|
|
|
|
//no tables
|
|
$_REQUEST['db'] = 'my_unique_test_db';
|
|
$tablesProperty->setValue($controller, []);
|
|
$result = $method->invoke($controller, ['status' => false]);
|
|
self::assertIsString($result);
|
|
self::assertStringContainsString($_REQUEST['db'], $result);
|
|
self::assertStringNotContainsString('id="overhead"', $result);
|
|
|
|
//with table
|
|
$tableInfo = [
|
|
'ENGINE' => 'Maria',
|
|
'TABLE_TYPE' => 'BASE TABLE',
|
|
'TABLE_ROWS' => '0',
|
|
'TABLE_COMMENT' => 'test',
|
|
'Data_length' => '5000',
|
|
'Index_length' => '100',
|
|
'Data_free' => '10000',
|
|
];
|
|
$tablesProperty->setValue($controller, [
|
|
'my_unique_test_table_1' => [
|
|
'TABLE_NAME' => 'my_unique_test_table_1',
|
|
'Create_time' => '2026-09-04 09:11:00',
|
|
'Update_time' => '2026-09-04 10:11:00',
|
|
'Check_time' => '2026-09-04 11:11:00',
|
|
] + $tableInfo,
|
|
'my_unique_test_table_2' => [
|
|
'TABLE_NAME' => 'my_unique_test_table_2',
|
|
'Create_time' => '2026-09-04 09:22:00',
|
|
'Update_time' => '2026-09-04 10:22:00',
|
|
'Check_time' => '2026-09-04 11:22:00',
|
|
] + $tableInfo,
|
|
]);
|
|
$numTables->setValue($controller, 2);
|
|
$result = $method->invoke($controller, ['status' => false]);
|
|
self::assertIsString($result);
|
|
|
|
self::assertStringContainsString($_REQUEST['db'], $result);
|
|
self::assertStringContainsString('id="overhead"', $result);
|
|
self::assertStringContainsString('9.8', $result);
|
|
self::assertStringContainsString(
|
|
'<th class="value tbl_creation font-monospace text-end">Sep 04, 2026 at 09:11 AM</th>',
|
|
$result,
|
|
);
|
|
self::assertStringContainsString(
|
|
'<th class="value tbl_last_update font-monospace text-end">Sep 04, 2026 at 10:22 AM</th>',
|
|
$result,
|
|
);
|
|
self::assertStringContainsString(
|
|
'<th class="value tbl_last_check font-monospace text-end">Sep 04, 2026 at 11:22 AM</th>',
|
|
$result,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Tests for getValuesForMroongaTable()
|
|
*/
|
|
public function testGetValuesForMroongaTable(): void
|
|
{
|
|
Current::$database = 'testdb';
|
|
Current::$table = 'mytable';
|
|
|
|
$dbi = DatabaseInterface::getInstance();
|
|
$config = Config::getInstance();
|
|
$structureController = new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
$config,
|
|
);
|
|
|
|
self::assertSame(
|
|
[[], '', '', 0],
|
|
(new ReflectionMethod(StructureController::class, 'getValuesForMroongaTable'))
|
|
->invokeArgs($structureController, [[], 0]),
|
|
);
|
|
|
|
(new ReflectionMethod(StructureController::class, 'getDatabaseInfo'))
|
|
->invokeArgs($structureController, [self::createStub(ServerRequest::class), 0]);
|
|
|
|
self::assertSame(
|
|
[['Data_length' => 45, 'Index_length' => 60], '105', 'B', 105],
|
|
(new ReflectionMethod(StructureController::class, 'getValuesForMroongaTable'))
|
|
->invokeArgs($structureController, [['Data_length' => 45, 'Index_length' => 60], 0]),
|
|
);
|
|
|
|
self::assertSame(
|
|
[
|
|
['Data_length' => 45, 'Index_length' => 60],
|
|
'105',
|
|
'B',
|
|
180, //105 + 75
|
|
],
|
|
(new ReflectionMethod(StructureController::class, 'getValuesForMroongaTable'))
|
|
->invokeArgs($structureController, [['Data_length' => 45, 'Index_length' => 60], 75]),
|
|
);
|
|
}
|
|
|
|
public function testGetDbInfo(): void
|
|
{
|
|
Config::getInstance()->selectedServer['DisableIS'] = true;
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbiDummy->addResult('SHOW TABLES FROM `test_db`;', [['test_table']], ['Tables_in_test_db']);
|
|
DatabaseInterface::$instance = $this->createDatabaseInterface($dbiDummy);
|
|
|
|
$dbi = DatabaseInterface::getInstance();
|
|
$structureController = new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
Config::getInstance(),
|
|
);
|
|
|
|
$tableInfo = [
|
|
'Name' => 'test_table',
|
|
'Engine' => 'InnoDB',
|
|
'Version' => '10',
|
|
'Row_format' => 'Dynamic',
|
|
'Rows' => '3',
|
|
'Avg_row_length' => '5461',
|
|
'Data_length' => '16384',
|
|
'Max_data_length' => '0',
|
|
'Index_length' => '0',
|
|
'Data_free' => '0',
|
|
'Auto_increment' => '4',
|
|
'Create_time' => '2011-12-13 14:15:16',
|
|
'Update_time' => null,
|
|
'Check_time' => null,
|
|
'Collation' => 'utf8mb4_general_ci',
|
|
'Checksum' => null,
|
|
'Create_options' => '',
|
|
'Comment' => '',
|
|
'Max_index_length' => '0',
|
|
'Temporary' => 'N',
|
|
'Type' => 'InnoDB',
|
|
'TABLE_SCHEMA' => 'test_db',
|
|
'TABLE_NAME' => 'test_table',
|
|
'ENGINE' => 'InnoDB',
|
|
'VERSION' => '10',
|
|
'ROW_FORMAT' => 'Dynamic',
|
|
'TABLE_ROWS' => '3',
|
|
'AVG_ROW_LENGTH' => '5461',
|
|
'DATA_LENGTH' => '16384',
|
|
'MAX_DATA_LENGTH' => '0',
|
|
'INDEX_LENGTH' => '0',
|
|
'DATA_FREE' => '0',
|
|
'AUTO_INCREMENT' => '4',
|
|
'CREATE_TIME' => '2011-12-13 14:15:16',
|
|
'UPDATE_TIME' => null,
|
|
'CHECK_TIME' => null,
|
|
'TABLE_COLLATION' => 'utf8mb4_general_ci',
|
|
'CHECKSUM' => null,
|
|
'CREATE_OPTIONS' => '',
|
|
'TABLE_COMMENT' => '',
|
|
'TABLE_TYPE' => 'BASE TABLE',
|
|
];
|
|
$expected = [['test_table' => $tableInfo], 1];
|
|
$actual = $structureController->getDbInfo('test_db', null, null, null, null, 0);
|
|
self::assertSame($expected, $actual);
|
|
}
|
|
|
|
public function testGetDbInfoWithSortedColumnAndDescendingOrder(): void
|
|
{
|
|
$this->config->selectedServer['DisableIS'] = true;
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbiDummy->addResult('SHOW TABLES FROM `test_db`;', [['a_table'], ['b_table']], ['Tables_in_test_db']);
|
|
$dbiDummy->addResult(
|
|
'SHOW TABLE STATUS FROM `test_db` WHERE `Name` IN (\'a_table\', \'b_table\')',
|
|
[['a_table', 'InnoDB', '3'], ['b_table', 'InnoDB', '10']],
|
|
['Name', 'Engine', 'Rows'],
|
|
);
|
|
$dbi = $this->createDatabaseInterface($dbiDummy, $this->config);
|
|
|
|
[$tables, $totalNumTables] = $this->createController($dbi)->getDbInfo(
|
|
'test_db',
|
|
'records',
|
|
'DESC',
|
|
null,
|
|
null,
|
|
0,
|
|
);
|
|
|
|
self::assertSame(['b_table', 'a_table'], array_keys($tables));
|
|
self::assertSame(2, $totalNumTables);
|
|
$dbiDummy->assertAllQueriesConsumed();
|
|
}
|
|
|
|
public function testGetDbInfoWithUnknownSortColumnFallsBackToNameAscending(): void
|
|
{
|
|
$this->config->selectedServer['DisableIS'] = true;
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbiDummy->addResult('SHOW TABLES FROM `test_db`;', [['a_table'], ['b_table']], ['Tables_in_test_db']);
|
|
// The 'DESC' sort order must be ignored for an unrecognized sort column
|
|
$dbiDummy->addResult(
|
|
'SHOW TABLE STATUS FROM `test_db` WHERE `Name` IN (\'a_table\', \'b_table\')',
|
|
[['a_table', 'InnoDB', '3'], ['b_table', 'InnoDB', '10']],
|
|
['Name', 'Engine', 'Rows'],
|
|
);
|
|
$dbi = $this->createDatabaseInterface($dbiDummy, $this->config);
|
|
|
|
[$tables, $totalNumTables] = $this->createController($dbi)->getDbInfo(
|
|
'test_db',
|
|
'invalid',
|
|
'DESC',
|
|
null,
|
|
null,
|
|
0,
|
|
);
|
|
|
|
self::assertSame(['a_table', 'b_table'], array_keys($tables));
|
|
self::assertSame(2, $totalNumTables);
|
|
$dbiDummy->assertAllQueriesConsumed();
|
|
}
|
|
|
|
public function testGetDbInfoWithTableGroup(): void
|
|
{
|
|
$this->config->selectedServer['DisableIS'] = true;
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbiDummy->addResult(
|
|
'SHOW TABLE STATUS FROM `test_db` WHERE (`Name` LIKE \'2024\\\\_\\\\_%\' OR `Name` = \'2024\')',
|
|
[['2024', 'InnoDB', '3'], ['2024__child', 'InnoDB', '5']],
|
|
['Name', 'Engine', 'Rows'],
|
|
);
|
|
$dbi = $this->createDatabaseInterface($dbiDummy, $this->config);
|
|
|
|
[$tables, $totalNumTables] = $this->createController($dbi)->getDbInfo('test_db', null, null, '2024', null, 0);
|
|
|
|
// Numerical keys must be preserved when both results are merged
|
|
self::assertSame([2024, '2024__child'], array_keys($tables));
|
|
self::assertSame(2, $totalNumTables);
|
|
$dbiDummy->assertAllQueriesConsumed();
|
|
}
|
|
|
|
public function testGetDbInfoWithTableGroupAndTableType(): void
|
|
{
|
|
$this->config->selectedServer['DisableIS'] = true;
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbiDummy->addResult(
|
|
'SHOW TABLE STATUS FROM `test_db` WHERE '
|
|
. '(`Name` LIKE \'2024\\\\_\\\\_%\' OR `Name` = \'2024\') AND `Comment` != \'VIEW\'',
|
|
[['2024', 'InnoDB', '3'], ['2024__child', 'InnoDB', '5']],
|
|
['Name', 'Engine', 'Rows'],
|
|
);
|
|
$dbi = $this->createDatabaseInterface($dbiDummy, $this->config);
|
|
|
|
[$tables, $totalNumTables] = $this->createController($dbi)->getDbInfo(
|
|
'test_db',
|
|
null,
|
|
null,
|
|
'2024',
|
|
'table',
|
|
0,
|
|
);
|
|
|
|
self::assertSame([2024, '2024__child'], array_keys($tables));
|
|
self::assertSame(2, $totalNumTables);
|
|
$dbiDummy->assertAllQueriesConsumed();
|
|
}
|
|
|
|
public function testGetDbInfoWithTableGroupAndInformationSchema(): void
|
|
{
|
|
$this->config->selectedServer['DisableIS'] = false;
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbiDummy->addResult(
|
|
'SELECT *, `TABLE_SCHEMA` AS `Db`, `TABLE_NAME` AS `Name`,'
|
|
. ' `TABLE_TYPE` AS `TABLE_TYPE`, `ENGINE` AS `Engine`, `ENGINE` AS `Type`,'
|
|
. ' `VERSION` AS `Version`, `ROW_FORMAT` AS `Row_format`, `TABLE_ROWS` AS `Rows`,'
|
|
. ' `AVG_ROW_LENGTH` AS `Avg_row_length`, `DATA_LENGTH` AS `Data_length`,'
|
|
. ' `MAX_DATA_LENGTH` AS `Max_data_length`, `INDEX_LENGTH` AS `Index_length`,'
|
|
. ' `DATA_FREE` AS `Data_free`, `AUTO_INCREMENT` AS `Auto_increment`,'
|
|
. ' `CREATE_TIME` AS `Create_time`, `UPDATE_TIME` AS `Update_time`,'
|
|
. ' `CHECK_TIME` AS `Check_time`, `TABLE_COLLATION` AS `Collation`,'
|
|
. ' `CHECKSUM` AS `Checksum`, `CREATE_OPTIONS` AS `Create_options`,'
|
|
. ' `TABLE_COMMENT` AS `Comment` FROM `information_schema`.`TABLES` t'
|
|
. ' WHERE `TABLE_SCHEMA` COLLATE utf8_bin = \'test_db\''
|
|
. ' AND (t.`TABLE_NAME` LIKE \'2024\\\\_\\\\_%\' OR t.`TABLE_NAME` COLLATE utf8_bin = \'2024\')'
|
|
. ' ORDER BY Name ASC',
|
|
[['2024', '2024', 'InnoDB', 'InnoDB', '3'], ['2024__child', '2024__child', 'InnoDB', 'InnoDB', '5']],
|
|
['TABLE_NAME', 'Name', 'ENGINE', 'Engine', 'TABLE_ROWS'],
|
|
);
|
|
$dbi = $this->createDatabaseInterface($dbiDummy, $this->config);
|
|
|
|
[$tables, $totalNumTables] = $this->createController($dbi)->getDbInfo('test_db', null, null, '2024', null, 0);
|
|
|
|
self::assertSame([2024, '2024__child'], array_keys($tables));
|
|
self::assertSame(2, $totalNumTables);
|
|
$dbiDummy->assertAllQueriesConsumed();
|
|
}
|
|
|
|
public function testGetDbInfoWithTableType(): void
|
|
{
|
|
$this->config->selectedServer['DisableIS'] = true;
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbiDummy->addResult(
|
|
'SHOW TABLE STATUS FROM `test_db` WHERE `Comment` = \'VIEW\'',
|
|
[['test_view', null, '0', 'VIEW']],
|
|
['Name', 'Engine', 'Rows', 'Comment'],
|
|
);
|
|
$dbi = $this->createDatabaseInterface($dbiDummy, $this->config);
|
|
|
|
[$tables, $totalNumTables] = $this->createController($dbi)->getDbInfo('test_db', null, null, null, 'view', 0);
|
|
|
|
self::assertSame(['test_view'], array_keys($tables));
|
|
self::assertSame('VIEW', $tables['test_view']['TABLE_TYPE']);
|
|
self::assertSame(1, $totalNumTables);
|
|
$dbiDummy->assertAllQueriesConsumed();
|
|
}
|
|
|
|
public function testGetDbInfoWithUnknownTableTypeFallsBackToFullList(): void
|
|
{
|
|
$this->config->selectedServer['DisableIS'] = true;
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbiDummy->addResult('SHOW TABLES FROM `test_db`;', [['a_table']], ['Tables_in_test_db']);
|
|
$dbiDummy->addResult(
|
|
'SHOW TABLE STATUS FROM `test_db` WHERE `Name` IN (\'a_table\')',
|
|
[['a_table', 'InnoDB', '3']],
|
|
['Name', 'Engine', 'Rows'],
|
|
);
|
|
$dbi = $this->createDatabaseInterface($dbiDummy, $this->config);
|
|
|
|
[$tables, $totalNumTables] = $this->createController($dbi)->getDbInfo('test_db', null, null, null, 'bogus', 0);
|
|
|
|
self::assertSame(['a_table'], array_keys($tables));
|
|
self::assertSame(1, $totalNumTables);
|
|
$dbiDummy->assertAllQueriesConsumed();
|
|
}
|
|
|
|
public function testGetDbInfoWithLimitedTableList(): void
|
|
{
|
|
$this->config->selectedServer['DisableIS'] = true;
|
|
$this->config->config = new Settings(['MaxTableList' => 1]);
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbiDummy->addResult('SHOW TABLES FROM `test_db`;', [['a_table'], ['b_table']], ['Tables_in_test_db']);
|
|
// Only the page starting at the current position is fetched in detail
|
|
$dbiDummy->addResult(
|
|
'SHOW TABLE STATUS FROM `test_db` WHERE `Name` IN (\'b_table\')',
|
|
[['b_table', 'InnoDB', '10']],
|
|
['Name', 'Engine', 'Rows'],
|
|
);
|
|
$dbi = $this->createDatabaseInterface($dbiDummy, $this->config);
|
|
|
|
[$tables, $totalNumTables] = $this->createController($dbi)->getDbInfo('test_db', null, null, null, null, 1);
|
|
|
|
self::assertSame(['b_table'], array_keys($tables));
|
|
self::assertSame(2, $totalNumTables);
|
|
$dbiDummy->assertAllQueriesConsumed();
|
|
}
|
|
|
|
private function createController(DatabaseInterface $dbi): StructureController
|
|
{
|
|
return new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
$this->config,
|
|
);
|
|
}
|
|
|
|
public function testGetTableListPosition(): void
|
|
{
|
|
$dbi = DatabaseInterface::getInstance();
|
|
$structureController = new StructureController(
|
|
$this->response,
|
|
$this->template,
|
|
$this->relation,
|
|
$this->replication,
|
|
$dbi,
|
|
self::createStub(TrackingChecker::class),
|
|
self::createStub(PageSettings::class),
|
|
new DbTableExists($dbi),
|
|
Config::getInstance(),
|
|
);
|
|
|
|
// Default 0
|
|
$actual = $structureController->getTableListPosition(null, 'test_db');
|
|
self::assertSame(0, $actual);
|
|
|
|
// From POST
|
|
$actual = $structureController->getTableListPosition('250', 'test_db');
|
|
self::assertSame(250, $actual);
|
|
|
|
// From SESSION
|
|
$actual = $structureController->getTableListPosition(null, 'test_db');
|
|
self::assertSame(250, $actual);
|
|
|
|
// Negative positions are clamped to 0
|
|
$actual = $structureController->getTableListPosition('-250', 'test_db');
|
|
self::assertSame(0, $actual);
|
|
}
|
|
|
|
public function testRedirectsToLastPageWhenPositionIsBeyondTheTableList(): void
|
|
{
|
|
Current::$database = 'test_db';
|
|
$this->config->selectedServer['DisableIS'] = true;
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbiDummy->addSelectDb('test_db');
|
|
$dbiDummy->addResult('SHOW TABLES FROM `test_db`;', [['test_table']], ['Tables_in_test_db']);
|
|
$dbiDummy->addResult(
|
|
'SHOW TABLE STATUS FROM `test_db`',
|
|
[['test_table', 'InnoDB', '3']],
|
|
['Name', 'Engine', 'Rows'],
|
|
);
|
|
$dbi = $this->createDatabaseInterface($dbiDummy, $this->config);
|
|
|
|
// The first (and only) page holds the whole table list, so position 1 is past the end
|
|
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/')
|
|
->withQueryParams(['db' => 'test_db', 'pos' => '1']);
|
|
|
|
$response = $this->createController($dbi)($request);
|
|
|
|
self::assertSame(302, $response->getStatusCode());
|
|
self::assertStringContainsString('pos=0', $response->getHeaderLine('Location'));
|
|
self::assertStringContainsString('route=/database/structure', $response->getHeaderLine('Location'));
|
|
$dbiDummy->assertAllQueriesConsumed();
|
|
}
|
|
}
|