You've already forked phpmyadmin
mirror of
https://github.com/phpmyadmin/phpmyadmin.git
synced 2026-10-23 18:29:15 +03:00
QA_5_2 still lives in libraries/classes/ and test/classes/; master moved to src/ and tests/unit/. Seven files therefore arrived as "deleted by us" and had to be ported by hand rather than merged. Upstream commit roll-call (060391b9a7..3d2594b2d3, 15 non-merge commits). The 5 merge commits in that range were checked with `git show --cc` and are all empty, so they contribute no content of their own:366d165982ported Strip port from host in processes privileges link544839869dported Test coverage for the port-stripping80638e8371ported Add LIMIT 0 when fetching metadata (4 of 7 hunks; see below for the 3 that do not apply)3b46e5b869ported Fix Uncaught ReferenceError: CodeMirror is not defined8525d24e09already in Fix search of INET4 with = operator1730b7c0bfno-op Remove useless parentheses6c1568b15bskipped Move getColumnMapFromSql into the methodafaeacecc9,cae0175274,64c6890d6c,669f8d17bc,eee3cd458a,9c8246a127,5f1ae11fc4,111993769enot taken Translated using Weblate (lv, vi) Conflict resolutions, file by file: * src/Server/Status/Processes.php Ported366d165982(#20315 / #19841): expose a 'user_host' row key holding the host with any `:port` suffix stripped. Adapted to master's lower-cased array keys from array_change_key_case(). * resources/templates/server/status/processes/list.twig The privileges link now uses row.user_host instead of row.host, so the hostname matches mysql.user.Host, which never contains a port. * src/Plugins/Transformations/{Input/Text_Plain_{Json,Sql,Xml}Editor, Output/Text_Plain_{Json,Sql,Xml}}.php Ported3b46e5b869. Kept master's Config::getInstance()->config->CodemirrorEnable accessor and expressed the HTTP_USER_AGENT check with Core::getEnv() rather than $_SERVER, to match the same #20159 guard already present in src/Header.php:225. * src/SystemDatabase.php Ported the memory part of #20426 only: getColumnMapFromSql() now wraps the query as `SELECT * FROM (...) AS tmp LIMIT 0` (80638e8371) so a large VIEW cannot exhaust memory while only its column metadata is wanted. This is where libraries/classes/DatabaseInterface.php's hunk landed: the method moved from the DBAL to SystemDatabase on master. Deliberately did NOT port6c1568b15b"Move getColumnMapFromSql into the method": that commit exists to stop 5.2's CreateController from running the view query when there is no transformation data, but master already only calls it inside `if ($pmaTransformationData !== false)` (src/Controllers/View/CreateController.php:205), and getExistingTransformationData() returns false precisely when browserTransformationFeature is null. Porting the signature churn would have degraded master's SystemColumn[] API for no behaviour gain, so src/Controllers/View/CreateController.php is left untouched and that commit's phpstan/psalm baseline hunks are moot. * src/Table/Table.php Ported80638e8371: getColumnsMeta() uses `LIMIT 0` instead of `LIMIT 1`. * src/Table/Search.php Resolved as ours. QA_5_2's8525d24e09is the same fix master already carries via725a767cc5(#20398 / #20395), only expressed against the refactored $needsQuoting code. No change needed. * tests/unit/Controllers/Server/Status/Processes/RefreshControllerTest.php Ported544839869d: covers both that the Host column still shows the port and that the privileges link does not. Added the trailing comma PHPCS (SlevomatCodingStandard.Functions.RequireTrailingCommaInCall) requires. * tests/unit/Stubs/DbiDummy.php Two fixtures updated, both kept in master's compact single-line style: the PROCESSLIST row now returns 'Host1:12345' to exercise port stripping (544839869d), and the `testdb`.`mytable` select moved from LIMIT 1 to LIMIT 0 (80638e8371). * tests/unit/Table/TableTest.php and tests/unit/SystemDatabaseTest.php Expected queries updated for the two changes above (`LIMIT 0`, and `SELECT * FROM (PMA_sql_query) AS tmp LIMIT 0`). The latter is where test/classes/DatabaseInterfaceTest.php's hunk landed, following the method from the DBAL to SystemDatabase. * tests/unit/Table/ColumnsDefinitionTest.php Not a conflicted file, but a master-only fixture that had to follow Table::getColumnsMeta(): the query is now `LIMIT 0` and returns no rows. Incoming changes with no master counterpart: * libraries/classes/Dbal/DbalInterface.php Dropped. The interface does not exist on master, so 80638e8371's rename of its getColumnMapFromSql() parameter has nothing to apply to. * test/classes/Http/Factory/ServerRequestFactoryTest.php1730b7c0bfis a no-op here: master's tests/unit/Http/Factory/ServerRequestFactoryTest.php contains no `(new ReflectionMethod(ServerRequestFactory...))` construct at all, the test having been rewritten. Note that rename detection wrongly paired this file with tests/unit/Http/Factory/UriFactoryTest.php, which was therefore resolved as ours and is unchanged. * phpstan-baseline.neon, psalm-baseline.xml Resolved as ours; the baselines are branch-specific. * resources/po/lv.po, resources/po/vi.po Resolved as ours, dropping the 8 Weblate commits listed above. This matches how all eight previous "Merge branch 'QA_5_2'" commits were resolved (6f427d8785,ccf4064698,fe4be26eeb,055f071473,28cd3776d2,3b4defeb35,ff06deaca2all have a zero-file po diff against their first parent); master runs its own Weblate component. Also removed the empty libraries/ and test/ directories the merge left behind. Verification: `phpunit --testsuite unit` 5668 tests / 18476 assertions OK, `phpcs` clean, `phpstan analyse` reports no errors. `psalm` reports one UnusedBaselineEntry for src/Utils/HttpRequest.php, which is pre-existing on master (neither that file nor psalm-baseline.xml is touched by this merge). Signed-off-by: William Desportes <williamdes@wdes.fr> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
318 lines
13 KiB
PHP
318 lines
13 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace PhpMyAdmin\Tests\Table;
|
|
|
|
use PhpMyAdmin\Config;
|
|
use PhpMyAdmin\ConfigStorage\Relation;
|
|
use PhpMyAdmin\Current;
|
|
use PhpMyAdmin\Dbal\DatabaseInterface;
|
|
use PhpMyAdmin\Table\ColumnsDefinition;
|
|
use PhpMyAdmin\Tests\AbstractTestCase;
|
|
use PhpMyAdmin\Tests\FieldHelper;
|
|
use PhpMyAdmin\Transformations;
|
|
use PhpMyAdmin\UserPrivileges;
|
|
use PHPUnit\Framework\Attributes\CoversClass;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
|
|
use function array_merge;
|
|
|
|
use const MYSQLI_TYPE_STRING;
|
|
|
|
#[CoversClass(ColumnsDefinition::class)]
|
|
class ColumnsDefinitionTest extends AbstractTestCase
|
|
{
|
|
public function testDisplayForm(): void
|
|
{
|
|
Config::getInstance()->selectedServer['DisableIS'] = false;
|
|
$dummyDbi = $this->createDbiDummy();
|
|
$dbi = $this->createDatabaseInterface($dummyDbi);
|
|
DatabaseInterface::$instance = $dbi;
|
|
|
|
// phpcs:disable Generic.Files.LineLength.TooLong
|
|
$columnMeta = ['Field' => 'actor_id', 'Type' => 'smallint(5) unsigned', 'Collation' => null, 'Null' => 'NO', 'Key' => 'PRI', 'Default' => null, 'Extra' => 'auto_increment', 'Privileges' => 'select,insert,update,references', 'Comment' => ''];
|
|
$dummyDbi->addResult(
|
|
'SELECT `COLUMN_NAME` AS `Field`, `COLUMN_TYPE` AS `Type`, `COLLATION_NAME` AS `Collation`,'
|
|
. ' `IS_NULLABLE` AS `Null`, `COLUMN_KEY` AS `Key`,'
|
|
. ' `COLUMN_DEFAULT` AS `Default`, `EXTRA` AS `Extra`, `PRIVILEGES` AS `Privileges`,'
|
|
. ' `COLUMN_COMMENT` AS `Comment`'
|
|
. ' FROM `information_schema`.`COLUMNS`'
|
|
. ' WHERE `TABLE_SCHEMA` COLLATE utf8_bin = \'sakila\' AND'
|
|
. ' `TABLE_NAME` COLLATE utf8_bin = \'actor\''
|
|
. ' ORDER BY `ORDINAL_POSITION`',
|
|
[
|
|
['actor_id', 'smallint(5) unsigned', null, 'NO', 'PRI', null, 'auto_increment', 'select,insert,update,references', ''],
|
|
['first_name', 'varchar(45)', 'utf8mb4_general_ci', 'NO', '', null, '', 'select,insert,update,references', ''],
|
|
['last_name', 'varchar(45)', 'utf8mb4_general_ci', 'NO', 'MUL', null, '', 'select,insert,update,references', ''],
|
|
['last_update', 'timestamp', null, 'NO', '', 'current_timestamp()', 'on update current_timestamp()', 'select,insert,update,references', ''],
|
|
],
|
|
['Field', 'Type', 'Collation', 'Null', 'Key', 'Default', 'Extra', 'Privileges', 'Comment'],
|
|
);
|
|
$dummyDbi->addResult(
|
|
'SHOW INDEXES FROM `sakila`.`actor`',
|
|
[
|
|
['actor', '0', 'PRIMARY', '1', 'actor_id', 'A', '2', null, null, '', 'BTREE', '', '', 'NO'],
|
|
['actor', '1', 'idx_actor_last_name', '1', 'last_name', 'A', '2', null, null, '', 'BTREE', '', '', 'NO'],
|
|
],
|
|
['Table', 'Non_unique', 'Key_name', 'Seq_in_index', 'Column_name', 'Collation', 'Cardinality', 'Sub_part', 'Packed', 'Null', 'Index_type', 'Comment', 'Index_comment', 'Ignored'],
|
|
);
|
|
// phpcs:enable
|
|
$dummyDbi->addResult(
|
|
'SELECT * FROM `sakila`.`actor` LIMIT 0',
|
|
[],
|
|
['actor_id', 'first_name', 'last_name', 'last_update'],
|
|
);
|
|
$createTable = <<<'SQL'
|
|
CREATE TABLE `actor` (
|
|
`actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
|
|
`first_name` varchar(45) NOT NULL,
|
|
`last_name` varchar(45) NOT NULL,
|
|
`last_update` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
|
PRIMARY KEY (`actor_id`),
|
|
KEY `idx_actor_last_name` (`last_name`)
|
|
) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8mb4
|
|
SQL;
|
|
$dummyDbi->addResult(
|
|
'SHOW CREATE TABLE `sakila`.`actor`',
|
|
[['actor', $createTable]],
|
|
['actor_id', 'first_name', 'last_name', 'last_update'],
|
|
);
|
|
|
|
$relation = new Relation($dbi);
|
|
$columnsDefinition = new ColumnsDefinition($dbi, $relation, new Transformations($dbi, $relation));
|
|
|
|
Current::$database = 'sakila';
|
|
Current::$table = 'actor';
|
|
$userPrivileges = new UserPrivileges(column: true, isReload: true);
|
|
|
|
$actual = $columnsDefinition->displayForm(
|
|
$userPrivileges,
|
|
'/table/structure/save',
|
|
1,
|
|
['actor_id'],
|
|
[$columnMeta],
|
|
);
|
|
|
|
$contentCell = [
|
|
'column_number' => 0,
|
|
'column_meta' => array_merge($columnMeta, ['DefaultType' => 'NONE', 'DefaultValue' => '']),
|
|
'type_upper' => 'SMALLINT',
|
|
'default_value' => '',
|
|
'length_values_input_size' => 8,
|
|
'length' => '5',
|
|
'extracted_columnspec' => [
|
|
'type' => 'smallint',
|
|
'spec_in_brackets' => '5',
|
|
'enum_set_values' => [],
|
|
'print_type' => 'smallint(5)',
|
|
'binary' => false,
|
|
'unsigned' => true,
|
|
'zerofill' => false,
|
|
'attribute' => 'UNSIGNED',
|
|
'can_contain_collation' => false,
|
|
'displayed_type' => 'smallint(5)',
|
|
],
|
|
'submit_attribute' => null,
|
|
'comments_map' => [],
|
|
'fields_meta' => [$columnMeta],
|
|
'is_backup' => true,
|
|
'move_columns' => [
|
|
FieldHelper::fromArray(['type' => MYSQLI_TYPE_STRING, 'name' => 'actor_id']),
|
|
FieldHelper::fromArray(['type' => MYSQLI_TYPE_STRING, 'name' => 'first_name']),
|
|
FieldHelper::fromArray(['type' => MYSQLI_TYPE_STRING, 'name' => 'last_name']),
|
|
FieldHelper::fromArray(['type' => MYSQLI_TYPE_STRING, 'name' => 'last_update']),
|
|
],
|
|
'available_mime' => [],
|
|
'mime_map' => [],
|
|
];
|
|
$expected = [
|
|
'is_backup' => true,
|
|
'fields_meta' => [$columnMeta],
|
|
'relation_parameters' => $relation->getRelationParameters(),
|
|
'action' => '/table/structure/save',
|
|
'form_params' => [
|
|
'db' => 'sakila',
|
|
'table' => 'actor',
|
|
'orig_num_fields' => 1,
|
|
'orig_field_where' => null,
|
|
'orig_after_field' => null,
|
|
'selected[0]' => 'actor_id',
|
|
'field_orig[0]' => 'actor_id',
|
|
'field_type_orig[0]' => 'SMALLINT',
|
|
'field_length_orig[0]' => '5',
|
|
'field_default_value_orig[0]' => '',
|
|
'field_default_type_orig[0]' => 'NONE',
|
|
'field_collation_orig[0]' => '',
|
|
'field_attribute_orig[0]' => 'UNSIGNED',
|
|
'field_null_orig[0]' => 'NO',
|
|
'field_extra_orig[0]' => 'auto_increment',
|
|
'field_comments_orig[0]' => '',
|
|
'field_virtuality_orig[0]' => '',
|
|
'field_expression_orig[0]' => '',
|
|
],
|
|
'content_cells' => [$contentCell],
|
|
'partition_details' => [
|
|
'partition_by' => null,
|
|
'partition_expr' => null,
|
|
'subpartition_by' => null,
|
|
'subpartition_expr' => null,
|
|
'partition_count' => 0,
|
|
'subpartition_count' => 0,
|
|
'can_have_subpartitions' => false,
|
|
'value_enabled' => false,
|
|
],
|
|
'primary_indexes' => null,
|
|
'unique_indexes' => null,
|
|
'indexes' => null,
|
|
'fulltext_indexes' => null,
|
|
'spatial_indexes' => null,
|
|
'table' => null,
|
|
'comment' => null,
|
|
'tbl_collation' => null,
|
|
'charsets' => [
|
|
[
|
|
'name' => 'armscii8',
|
|
'description' => 'armscii8_general_ci',
|
|
'collations' => [['name' => 'armscii8_general_ci', 'description' => 'Armenian, case-insensitive']],
|
|
],
|
|
[
|
|
'name' => 'latin1',
|
|
'description' => 'cp1252 West European',
|
|
'collations' => [['name' => 'latin1_swedish_ci', 'description' => 'Swedish, case-insensitive']],
|
|
],
|
|
[
|
|
'name' => 'utf8',
|
|
'description' => 'UTF-8 Unicode',
|
|
'collations' => [
|
|
['name' => 'utf8_bin', 'description' => 'Unicode, binary'],
|
|
['name' => 'utf8_general_ci', 'description' => 'Unicode, case-insensitive'],
|
|
],
|
|
],
|
|
[
|
|
'name' => 'utf8mb4',
|
|
'description' => 'utf8mb4_0900_ai_ci',
|
|
'collations' => [
|
|
['name' => 'utf8mb4_general_ci', 'description' => 'Unicode (UCA 4.0.0), case-insensitive'],
|
|
],
|
|
],
|
|
],
|
|
'tbl_storage_engine' => null,
|
|
'storage_engines' => ['dummy' => ['name' => 'dummy', 'comment' => 'dummy comment', 'is_default' => false]],
|
|
'connection' => null,
|
|
'change_column' => null,
|
|
'is_virtual_columns_supported' => true,
|
|
'is_integers_length_restricted' => false,
|
|
'browse_mime' => true,
|
|
'supports_stored_keyword' => true,
|
|
'server_version' => $dbi->getVersion(),
|
|
'max_rows' => 25,
|
|
'char_editing' => 'input',
|
|
'attribute_types' => ['', 'BINARY', 'UNSIGNED', 'UNSIGNED ZEROFILL', 'on update CURRENT_TIMESTAMP'],
|
|
'privs_available' => true,
|
|
'max_length' => 1024,
|
|
'have_partitioning' => true,
|
|
'disable_is' => false,
|
|
];
|
|
|
|
self::assertEquals($expected, $actual);
|
|
}
|
|
|
|
/**
|
|
* test for ColumnsDefinition::decorateColumnMetaDefault
|
|
*
|
|
* @param array<string, string> $expected
|
|
*/
|
|
#[DataProvider('providerColumnMetaDefault')]
|
|
public function testDecorateColumnMetaDefault(
|
|
string|null $default,
|
|
bool $isNull,
|
|
array $expected,
|
|
): void {
|
|
$result = ColumnsDefinition::decorateColumnMetaDefault($default, $isNull);
|
|
|
|
self::assertEquals($expected, $result);
|
|
}
|
|
|
|
/**
|
|
* Data provider for testDecorateColumnMetaDefault
|
|
*
|
|
* @return array<string, array{string|null, bool, array<string, string>}>
|
|
*/
|
|
public static function providerColumnMetaDefault(): array
|
|
{
|
|
return [
|
|
'when Default is null and Null is YES' => [
|
|
null,
|
|
true,
|
|
['DefaultType' => 'NULL', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is null and Null is NO' => [
|
|
null,
|
|
false,
|
|
['DefaultType' => 'NONE', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is empty string' => [
|
|
'',
|
|
false,
|
|
['DefaultType' => 'USER_DEFINED', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is CURRENT_TIMESTAMP' => [
|
|
'CURRENT_TIMESTAMP',
|
|
false,
|
|
['DefaultType' => 'CURRENT_TIMESTAMP', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is current_timestamp' => [
|
|
'current_timestamp()',
|
|
false,
|
|
['DefaultType' => 'CURRENT_TIMESTAMP', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is UUID' => [
|
|
'UUID',
|
|
false,
|
|
['DefaultType' => 'UUID', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is uuid()' => [
|
|
'uuid()',
|
|
false,
|
|
['DefaultType' => 'UUID', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is UUID_v4' => [
|
|
'UUID_v4',
|
|
false,
|
|
['DefaultType' => 'UUID_v4', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is uuid_v4()' => [
|
|
'uuid_v4()',
|
|
false,
|
|
['DefaultType' => 'UUID_v4', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is UUID_v7' => [
|
|
'UUID_v7',
|
|
false,
|
|
['DefaultType' => 'UUID_v7', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is uuid_v7()' => [
|
|
'uuid_v7()',
|
|
false,
|
|
['DefaultType' => 'UUID_v7', 'DefaultValue' => ''],
|
|
],
|
|
'when Default is anything else and Type is text' => [
|
|
'"some/thing"',
|
|
false,
|
|
['DefaultType' => 'USER_DEFINED', 'DefaultValue' => '"some/thing"'],
|
|
],
|
|
'when Default is anything else and Type is not text' => [
|
|
'"some\/thing"',
|
|
false,
|
|
['DefaultType' => 'USER_DEFINED', 'DefaultValue' => '"some\/thing"'],
|
|
],
|
|
'when Default is an empty string' => [
|
|
'',
|
|
false,
|
|
['DefaultType' => 'USER_DEFINED', 'DefaultValue' => ''],
|
|
],
|
|
];
|
|
}
|
|
}
|