Merge #17860 - Fix #17793 - insert record - fix null not selected if nullable UUID & insert error

Pull-request: #17860
Fixes: #17793

Signed-off-by: William Desportes <williamdes@wdes.fr>
This commit is contained in:
William Desportes 2022-11-03 20:23:21 +01:00
commit 5e1ff822f3
No known key found for this signature in database
GPG Key ID: 90A0EF1B8251A889
3 changed files with 302 additions and 78 deletions

View File

@ -84,7 +84,7 @@ final class ColumnsDefinition
]
);
if (isset($_POST['field_where'])) {
$form_params['after_field'] = $_POST['after_field'];
$form_params['after_field'] = (string) $_POST['after_field'];
}
}
@ -246,6 +246,8 @@ final class ColumnsDefinition
case 'NULL':
case 'CURRENT_TIMESTAMP':
case 'current_timestamp()':
case 'UUID':
case 'uuid()':
$columnMeta['Default'] = $columnMeta['DefaultType'];
break;
}
@ -282,38 +284,8 @@ final class ColumnsDefinition
$columnMeta['Expression'] = is_array($expressions) ? $expressions[$columnMeta['Field']] : null;
}
switch ($columnMeta['Default']) {
case null:
if ($columnMeta['Default'] === null) {
if ($columnMeta['Null'] === 'YES') {
$columnMeta['DefaultType'] = 'NULL';
$columnMeta['DefaultValue'] = '';
} else {
$columnMeta['DefaultType'] = 'NONE';
$columnMeta['DefaultValue'] = '';
}
} else { // empty
$columnMeta['DefaultType'] = 'USER_DEFINED';
$columnMeta['DefaultValue'] = $columnMeta['Default'];
}
break;
case 'CURRENT_TIMESTAMP':
case 'current_timestamp()':
$columnMeta['DefaultType'] = 'CURRENT_TIMESTAMP';
$columnMeta['DefaultValue'] = '';
break;
default:
$columnMeta['DefaultType'] = 'USER_DEFINED';
$columnMeta['DefaultValue'] = $columnMeta['Default'];
if (substr($columnMeta['Type'], -4) === 'text') {
$textDefault = substr($columnMeta['Default'], 1, -1);
$columnMeta['Default'] = stripcslashes($textDefault);
}
break;
}
$columnMetaDefault = self::decorateColumnMetaDefault($columnMeta);
$columnMeta = array_merge($columnMeta, $columnMetaDefault);
}
if (isset($columnMeta['Type'])) {
@ -366,14 +338,10 @@ final class ColumnsDefinition
}
// old column type
if (isset($columnMeta['Type'])) {
// keep in uppercase because the new type will be in uppercase
$form_params['field_type_orig[' . $columnNumber . ']'] = mb_strtoupper($type);
if (isset($columnMeta['column_status']) && ! $columnMeta['column_status']['isEditable']) {
$form_params['field_type[' . $columnNumber . ']'] = mb_strtoupper($type);
}
} else {
$form_params['field_type_orig[' . $columnNumber . ']'] = '';
// keep in uppercase because the new type will be in uppercase
$form_params['field_type_orig[' . $columnNumber . ']'] = mb_strtoupper($type);
if (isset($columnMeta['column_status']) && ! $columnMeta['column_status']['isEditable']) {
$form_params['field_type[' . $columnNumber . ']'] = mb_strtoupper($type);
}
// old column length
@ -439,9 +407,11 @@ final class ColumnsDefinition
}
if ($type_upper === 'BIT') {
$default_value = Util::convertBitDefaultValue($columnMeta['DefaultValue']);
$default_value = ! empty($columnMeta['DefaultValue'])
? Util::convertBitDefaultValue($columnMeta['DefaultValue'])
: '';
} elseif ($type_upper === 'BINARY' || $type_upper === 'VARBINARY') {
$default_value = bin2hex($columnMeta['DefaultValue']);
$default_value = bin2hex((string) $columnMeta['DefaultValue']);
}
$content_cells[$columnNumber] = [
@ -522,4 +492,52 @@ final class ColumnsDefinition
'disable_is' => $cfg['Server']['DisableIS'],
];
}
/**
* Set default type and default value according to the column metadata
*
* @param array $columnMeta Column Metadata
* @phpstan-param array<string, string|null> $columnMeta
*
* @return non-empty-array<array-key, mixed>
*/
public static function decorateColumnMetaDefault(array $columnMeta): array
{
$metaDefault = [
'DefaultType' => 'USER_DEFINED',
'DefaultValue' => '',
];
switch ($columnMeta['Default']) {
case null:
if ($columnMeta['Null'] === 'YES') {
$metaDefault['DefaultType'] = 'NULL';
} else {
$metaDefault['DefaultType'] = 'NONE';
}
break;
case 'CURRENT_TIMESTAMP':
case 'current_timestamp()':
$metaDefault['DefaultType'] = 'CURRENT_TIMESTAMP';
break;
case 'UUID':
case 'uuid()':
$metaDefault['DefaultType'] = 'UUID';
break;
default:
$metaDefault['DefaultValue'] = $columnMeta['Default'];
if (substr((string) $columnMeta['Type'], -4) === 'text') {
$textDefault = substr($columnMeta['Default'], 1, -1);
$metaDefault['Default'] = stripcslashes($textDefault);
}
break;
}
return $metaDefault;
}
}

View File

@ -1590,38 +1590,21 @@ class InsertEditTest extends AbstractTestCase
/**
* Test for getSpecialCharsAndBackupFieldForInsertingMode
*
* @param array $column Column parameters
* @param array $expected Expected result
* @psalm-param array<string, string|bool|null> $column
* @psalm-param array<bool|string> $expected
*
* @dataProvider providerForTestGetSpecialCharsAndBackupFieldForInsertingMode
*/
public function testGetSpecialCharsAndBackupFieldForInsertingMode(): void
{
$column = [];
$column['True_Type'] = 'bit';
$column['Default'] = 'b\'101\'';
$column['is_binary'] = true;
public function testGetSpecialCharsAndBackupFieldForInsertingMode(
array $column,
array $expected
): void {
$GLOBALS['cfg']['ProtectBinary'] = false;
$GLOBALS['cfg']['ShowFunctionFields'] = true;
$result = $this->callFunction(
$this->insertEdit,
InsertEdit::class,
'getSpecialCharsAndBackupFieldForInsertingMode',
[$column]
);
$this->assertEquals(
[
false,
'b\'101\'',
'101',
'',
'101',
],
$result
);
// case 2
unset($column['Default']);
$column['True_Type'] = 'char';
$result = (array) $this->callFunction(
$this->insertEdit,
InsertEdit::class,
@ -1630,17 +1613,128 @@ class InsertEditTest extends AbstractTestCase
);
$this->assertEquals(
[
true,
'',
'',
'',
'',
],
$expected,
$result
);
}
/**
* Data provider for test getSpecialCharsAndBackupFieldForInsertingMode()
*
* @return array
* @psalm-return array<string, array{array<string, string|bool|null>, array<bool|string>}>
*/
public function providerForTestGetSpecialCharsAndBackupFieldForInsertingMode(): array
{
return [
'bit' => [
[
'True_Type' => 'bit',
'Default' => 'b\'101\'',
'is_binary' => true,
],
[
false,
'b\'101\'',
'101',
'',
'101',
],
],
'char' => [
[
'True_Type' => 'char',
'is_binary' => true,
],
[
true,
'',
'',
'',
'',
],
],
'time with CURRENT_TIMESTAMP value' => [
[
'True_Type' => 'time',
'Default' => 'CURRENT_TIMESTAMP',
],
[
false,
'CURRENT_TIMESTAMP',
'CURRENT_TIMESTAMP',
'',
'CURRENT_TIMESTAMP',
],
],
'time with current_timestamp() value' => [
[
'True_Type' => 'time',
'Default' => 'current_timestamp()',
],
[
false,
'current_timestamp()',
'current_timestamp()',
'',
'current_timestamp()',
],
],
'time with no dot value' => [
[
'True_Type' => 'time',
'Default' => '10',
],
[
false,
'10',
'10.000000',
'',
'10.000000',
],
],
'time with dot value' => [
[
'True_Type' => 'time',
'Default' => '10.08',
],
[
false,
'10.08',
'10.080000',
'',
'10.080000',
],
],
'any text with escape text default' => [
[
'True_Type' => 'text',
'Default' => '"lorem\"ipsem"',
],
[
false,
'"lorem\"ipsem"',
'lorem"ipsem',
'',
'lorem"ipsem',
],
],
'varchar with html special chars' => [
[
'True_Type' => 'varchar',
'Default' => 'hello world<br><b>lorem</b> ipsem',
],
[
false,
'hello world<br><b>lorem</b> ipsem',
'hello world&lt;br&gt;&lt;b&gt;lorem&lt;/b&gt; ipsem',
'',
'hello world&lt;br&gt;&lt;b&gt;lorem&lt;/b&gt; ipsem',
],
],
];
}
/**
* Test for getParamsForUpdateOrInsert
*/

View File

@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Table;
use PhpMyAdmin\Table\ColumnsDefinition;
use PhpMyAdmin\Tests\AbstractTestCase;
/**
* @covers \PhpMyAdmin\Table\ColumnsDefinition
*/
class ColumnsDefinitionTest extends AbstractTestCase
{
/**
* test for ColumnsDefinition::decorateColumnMetaDefault
*
* @param array $columnMeta column metadata
* @param array $expected expected result
* @phpstan-param array<string, string|null> $columnMeta
* @phpstan-param array<string, string> $expected
*
* @dataProvider providerColumnMetaDefault
*/
public function testDecorateColumnMetaDefault(array $columnMeta, array $expected): void
{
$result = ColumnsDefinition::decorateColumnMetaDefault($columnMeta);
$this->assertEquals($expected, $result);
}
/**
* Data provider for testDecorateColumnMetaDefault
*
* @return array
* @psalm-return array<string, array{array<string, string|null>, array<string, string>}>
*/
public function providerColumnMetaDefault(): array
{
return [
'when Default is null and Null is YES' => [
[
'Default' => null,
'Null' => 'YES',
],
[
'DefaultType' => 'NULL',
'DefaultValue' => '',
],
],
'when Default is null and Null is NO' => [
[
'Default' => null,
'Null' => 'NO',
],
[
'DefaultType' => 'NONE',
'DefaultValue' => '',
],
],
'when Default is CURRENT_TIMESTAMP' => [
['Default' => 'CURRENT_TIMESTAMP'],
[
'DefaultType' => 'CURRENT_TIMESTAMP',
'DefaultValue' => '',
],
],
'when Default is current_timestamp' => [
['Default' => 'current_timestamp()'],
[
'DefaultType' => 'CURRENT_TIMESTAMP',
'DefaultValue' => '',
],
],
'when Default is UUID' => [
['Default' => 'UUID'],
[
'DefaultType' => 'UUID',
'DefaultValue' => '',
],
],
'when Default is uuid()' => [
['Default' => 'uuid()'],
[
'DefaultType' => 'UUID',
'DefaultValue' => '',
],
],
'when Default is anything else and Type is text' => [
[
'Default' => '"some\/thing"',
'Type' => 'text',
],
[
'Default' => 'some/thing',
'DefaultType' => 'USER_DEFINED',
'DefaultValue' => '"some\/thing"',
],
],
'when Default is anything else and Type is not text' => [
[
'Default' => '"some\/thing"',
'Type' => 'something',
],
[
'DefaultType' => 'USER_DEFINED',
'DefaultValue' => '"some\/thing"',
],
],
];
}
}