From a980aa9f81557c3fe993db49252fcc140f5df139 Mon Sep 17 00:00:00 2001 From: aB0t Date: Sat, 28 Sep 2024 01:27:34 +0200 Subject: [PATCH] Update 2024-09-28 01:27:31 --- .../code.php | 89 ++++++++++++++----- .../code.power | 89 ++++++++++++++----- 2 files changed, 130 insertions(+), 48 deletions(-) diff --git a/src/f3c04c28-bce4-422e-be93-7d163e4e342b/code.php b/src/f3c04c28-bce4-422e-be93-7d163e4e342b/code.php index 0ad261c..18e7e02 100644 --- a/src/f3c04c28-bce4-422e-be93-7d163e4e342b/code.php +++ b/src/f3c04c28-bce4-422e-be93-7d163e4e342b/code.php @@ -579,41 +579,57 @@ abstract class Schema implements SchemaInterface */ protected function isDataTypeChangeSignificant(string $currentType, string $expectedType): bool { - // Normalize both input types to lowercase for case-insensitive comparison - $currentType = strtolower($currentType); - $expectedType = strtolower($expectedType); + // Normalize both input types to lowercase and remove extra spaces for comparison + $currentType = strtolower(trim($currentType)); + $expectedType = strtolower(trim($expectedType)); - // Regex to extract the base data type and numeric parameters with named groups - $typePattern = '/^(?\w+)(\((?\d+(,\d+)?)\))?/'; + // Regex to extract the base data type and numeric parameters (size and precision) with named groups + $typePattern = '/^(?\w+)(\((?\s*\d+\s*(,\s*\d+\s*)?)\))?/'; // Match types and parameters preg_match($typePattern, $currentType, $currentMatches); preg_match($typePattern, $expectedType, $expectedMatches); - // Compare base types + // Compare base types (datatype without size/precision) if ($currentMatches['datatype'] !== $expectedMatches['datatype']) { return true; // Base types differ } - // Define types where size and other modifiers are irrelevant + // Define numeric types where display width is irrelevant but precision (for DECIMAL) matters $sizeIrrelevantTypes = [ - 'int', 'tinyint', 'smallint', 'mediumint', 'bigint', - 'float', 'double', 'decimal', 'numeric' // Numeric types where display width is irrelevant + 'int', 'tinyint', 'smallint', 'mediumint', 'bigint', + 'float', 'double' // Numeric types where display width is irrelevant ]; - // If the type is not in the size irrelevant list, compare full definitions - if (!in_array($currentMatches['datatype'], $sizeIrrelevantTypes)) + // Handle DECIMAL and NUMERIC types explicitly (precision and scale are relevant) + if (in_array($currentMatches['datatype'], ['decimal', 'numeric'])) { - return $currentType !== $expectedType; // Use full definition for types where size matters + // Extract precision and scale (if present) + if ($currentMatches['params'] !== $expectedMatches['params']) + { + return true; // Precision or scale has changed + } } - // For size irrelevant types, only compare base type, ignoring size and unsigned - $currentBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $currentType); - $expectedBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $expectedType); + // Check if the type is in the list of size-irrelevant types + if (in_array($currentMatches['datatype'], $sizeIrrelevantTypes)) + { + // Remove irrelevant parts like display width and "unsigned" for size-irrelevant types, including extra spaces + $currentBaseType = preg_replace('/\s*\(\s*\d+(\s*,\s*\d+)?\s*\)\s*|\s*unsigned\s*/', '', $currentType); + $expectedBaseType = preg_replace('/\s*\(\s*\d+(\s*,\s*\d+)?\s*\)\s*|\s*unsigned\s*/', '', $expectedType); - // Perform a final comparison for numeric types ignoring size - return $currentBaseType !== $expectedBaseType; + // Compare base types after normalization + return $currentBaseType !== $expectedBaseType; + } + + // For types where size is relevant (e.g., VARCHAR, CHAR, etc.), compare the full definitions + // Normalize size parameters by removing extra spaces around commas, e.g., "decimal(5 , 2)" -> "decimal(5,2)" + $normalizedCurrentType = preg_replace('/\s*,\s*/', ',', $currentType); + $normalizedExpectedType = preg_replace('/\s*,\s*/', ',', $expectedType); + + // Perform a full comparison for types where size matters + return $normalizedCurrentType !== $normalizedExpectedType; } /** @@ -624,7 +640,7 @@ abstract class Schema implements SchemaInterface * @param mixed $currentDefault Current default value. * @param mixed $newDefault The new default value to be set. * - * @return void + * @return bool True if update was successful, false if no update was needed. * @since 3.2.1 * @throws \Exception If there is an error updating column defaults. */ @@ -640,13 +656,38 @@ abstract class Schema implements SchemaInterface $updateTable = 'UPDATE ' . $this->db->quoteName($this->getTable($table)); $dbField = $this->db->quoteName($column); - // Update SQL to set new default on existing rows where the default is currently the old default - $sql = $updateTable . " SET $dbField = $sqlDefault WHERE $dbField IS NULL OR $dbField = ''"; + if (isset($this->columns[$column])) + { + $fieldType = strtoupper($this->columns[$column]->Type); - // Execute the update - $this->db->setQuery($sql); - return $this->db->execute(); - } catch (\Exception $e) { + // If the field is numeric, avoid comparing with empty string + if (strpos($fieldType, 'INT') !== false || + strpos($fieldType, 'FLOAT') !== false || + strpos($fieldType, 'DOUBLE') !== false || + strpos($fieldType, 'DECIMAL') !== false) + { + $whereCondition = "$dbField IS NULL OR $dbField = 0"; + } + else + { + // Default condition for non-numeric fields + $whereCondition = "$dbField IS NULL OR $dbField = ''"; + } + + // Update SQL to set new default on existing rows where the default is currently the old default + $sql = $updateTable . " SET $dbField = $sqlDefault WHERE $whereCondition"; + + // Execute the update + $this->db->setQuery($sql); + return $this->db->execute(); + } + else + { + throw new \Exception("Error: Column $column does not exist in table $table."); + } + } + catch (\Exception $e) + { throw new \Exception("Error: failed to update ($column) column defaults in $table table. " . $e->getMessage()); } } diff --git a/src/f3c04c28-bce4-422e-be93-7d163e4e342b/code.power b/src/f3c04c28-bce4-422e-be93-7d163e4e342b/code.power index 255917a..ac7ae5d 100644 --- a/src/f3c04c28-bce4-422e-be93-7d163e4e342b/code.power +++ b/src/f3c04c28-bce4-422e-be93-7d163e4e342b/code.power @@ -552,41 +552,57 @@ */ protected function isDataTypeChangeSignificant(string $currentType, string $expectedType): bool { - // Normalize both input types to lowercase for case-insensitive comparison - $currentType = strtolower($currentType); - $expectedType = strtolower($expectedType); + // Normalize both input types to lowercase and remove extra spaces for comparison + $currentType = strtolower(trim($currentType)); + $expectedType = strtolower(trim($expectedType)); - // Regex to extract the base data type and numeric parameters with named groups - $typePattern = '/^(?\w+)(\((?\d+(,\d+)?)\))?/'; + // Regex to extract the base data type and numeric parameters (size and precision) with named groups + $typePattern = '/^(?\w+)(\((?\s*\d+\s*(,\s*\d+\s*)?)\))?/'; // Match types and parameters preg_match($typePattern, $currentType, $currentMatches); preg_match($typePattern, $expectedType, $expectedMatches); - // Compare base types + // Compare base types (datatype without size/precision) if ($currentMatches['datatype'] !== $expectedMatches['datatype']) { return true; // Base types differ } - // Define types where size and other modifiers are irrelevant + // Define numeric types where display width is irrelevant but precision (for DECIMAL) matters $sizeIrrelevantTypes = [ - 'int', 'tinyint', 'smallint', 'mediumint', 'bigint', - 'float', 'double', 'decimal', 'numeric' // Numeric types where display width is irrelevant + 'int', 'tinyint', 'smallint', 'mediumint', 'bigint', + 'float', 'double' // Numeric types where display width is irrelevant ]; - // If the type is not in the size irrelevant list, compare full definitions - if (!in_array($currentMatches['datatype'], $sizeIrrelevantTypes)) + // Handle DECIMAL and NUMERIC types explicitly (precision and scale are relevant) + if (in_array($currentMatches['datatype'], ['decimal', 'numeric'])) { - return $currentType !== $expectedType; // Use full definition for types where size matters + // Extract precision and scale (if present) + if ($currentMatches['params'] !== $expectedMatches['params']) + { + return true; // Precision or scale has changed + } } - // For size irrelevant types, only compare base type, ignoring size and unsigned - $currentBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $currentType); - $expectedBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $expectedType); + // Check if the type is in the list of size-irrelevant types + if (in_array($currentMatches['datatype'], $sizeIrrelevantTypes)) + { + // Remove irrelevant parts like display width and "unsigned" for size-irrelevant types, including extra spaces + $currentBaseType = preg_replace('/\s*\(\s*\d+(\s*,\s*\d+)?\s*\)\s*|\s*unsigned\s*/', '', $currentType); + $expectedBaseType = preg_replace('/\s*\(\s*\d+(\s*,\s*\d+)?\s*\)\s*|\s*unsigned\s*/', '', $expectedType); - // Perform a final comparison for numeric types ignoring size - return $currentBaseType !== $expectedBaseType; + // Compare base types after normalization + return $currentBaseType !== $expectedBaseType; + } + + // For types where size is relevant (e.g., VARCHAR, CHAR, etc.), compare the full definitions + // Normalize size parameters by removing extra spaces around commas, e.g., "decimal(5 , 2)" -> "decimal(5,2)" + $normalizedCurrentType = preg_replace('/\s*,\s*/', ',', $currentType); + $normalizedExpectedType = preg_replace('/\s*,\s*/', ',', $expectedType); + + // Perform a full comparison for types where size matters + return $normalizedCurrentType !== $normalizedExpectedType; } /** @@ -597,7 +613,7 @@ * @param mixed $currentDefault Current default value. * @param mixed $newDefault The new default value to be set. * - * @return void + * @return bool True if update was successful, false if no update was needed. * @since 3.2.1 * @throws \Exception If there is an error updating column defaults. */ @@ -613,13 +629,38 @@ $updateTable = 'UPDATE ' . $this->db->quoteName($this->getTable($table)); $dbField = $this->db->quoteName($column); - // Update SQL to set new default on existing rows where the default is currently the old default - $sql = $updateTable . " SET $dbField = $sqlDefault WHERE $dbField IS NULL OR $dbField = ''"; + if (isset($this->columns[$column])) + { + $fieldType = strtoupper($this->columns[$column]->Type); - // Execute the update - $this->db->setQuery($sql); - return $this->db->execute(); - } catch (\Exception $e) { + // If the field is numeric, avoid comparing with empty string + if (strpos($fieldType, 'INT') !== false || + strpos($fieldType, 'FLOAT') !== false || + strpos($fieldType, 'DOUBLE') !== false || + strpos($fieldType, 'DECIMAL') !== false) + { + $whereCondition = "$dbField IS NULL OR $dbField = 0"; + } + else + { + // Default condition for non-numeric fields + $whereCondition = "$dbField IS NULL OR $dbField = ''"; + } + + // Update SQL to set new default on existing rows where the default is currently the old default + $sql = $updateTable . " SET $dbField = $sqlDefault WHERE $whereCondition"; + + // Execute the update + $this->db->setQuery($sql); + return $this->db->execute(); + } + else + { + throw new \Exception("Error: Column $column does not exist in table $table."); + } + } + catch (\Exception $e) + { throw new \Exception("Error: failed to update ($column) column defaults in $table table. " . $e->getMessage()); } }