diff --git a/libraries/classes/Table/ColumnsDefinition.php b/libraries/classes/Table/ColumnsDefinition.php index 37b1dba35f..1fb5117ed2 100644 --- a/libraries/classes/Table/ColumnsDefinition.php +++ b/libraries/classes/Table/ColumnsDefinition.php @@ -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 $columnMeta + * + * @return non-empty-array + */ + 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; + } } diff --git a/test/classes/InsertEditTest.php b/test/classes/InsertEditTest.php index 36186fd19e..6bbe885c12 100644 --- a/test/classes/InsertEditTest.php +++ b/test/classes/InsertEditTest.php @@ -1590,38 +1590,21 @@ class InsertEditTest extends AbstractTestCase /** * Test for getSpecialCharsAndBackupFieldForInsertingMode + * + * @param array $column Column parameters + * @param array $expected Expected result + * @psalm-param array $column + * @psalm-param array $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, array}> + */ + 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
lorem ipsem', + ], + [ + false, + 'hello world
lorem ipsem', + 'hello world<br><b>lorem</b> ipsem', + '', + 'hello world<br><b>lorem</b> ipsem', + ], + ], + ]; + } + /** * Test for getParamsForUpdateOrInsert */ diff --git a/test/classes/Table/ColumnsDefinitionTest.php b/test/classes/Table/ColumnsDefinitionTest.php new file mode 100644 index 0000000000..e972a22b8a --- /dev/null +++ b/test/classes/Table/ColumnsDefinitionTest.php @@ -0,0 +1,112 @@ + $columnMeta + * @phpstan-param array $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, array}> + */ + 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"', + ], + ], + ]; + } +}