Merge pull request #8880 from tractorcow/pulls/4.2/enum-schema-shifting

BUG Resolve issue where schema changes between enum / non-enum types
This commit is contained in:
Robbie Averill 2019-04-05 11:09:02 +13:00 committed by GitHub
commit 67d88c824b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 46 deletions

View File

@ -86,9 +86,9 @@ class GridFieldLevelup implements GridField_HTMLProvider
)); ));
$template = SSViewer::get_templates_by_class($this, '', __CLASS__); $template = SSViewer::get_templates_by_class($this, '', __CLASS__);
return array( return [
'before' => $forTemplate->renderWith($template), 'before' => $forTemplate->renderWith($template),
); ];
} }
public function setAttributes($attrs) public function setAttributes($attrs)

View File

@ -2,12 +2,13 @@
namespace SilverStripe\ORM\Connect; namespace SilverStripe\ORM\Connect;
use Exception;
use SilverStripe\Control\Director; use SilverStripe\Control\Director;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\Injector;
use SilverStripe\ORM\FieldType\DBPrimaryKey; use SilverStripe\ORM\DB;
use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\FieldType\DBField;
use Exception; use SilverStripe\ORM\FieldType\DBPrimaryKey;
/** /**
* Represents and handles all schema management for a database * Represents and handles all schema management for a database
@ -710,33 +711,33 @@ MESSAGE
} elseif ($fieldValue != $specValue) { } elseif ($fieldValue != $specValue) {
// If enums/sets are being modified, then we need to fix existing data in the table. // If enums/sets are being modified, then we need to fix existing data in the table.
// Update any records where the enum is set to a legacy value to be set to the default. // Update any records where the enum is set to a legacy value to be set to the default.
foreach (array('enum', 'set') as $enumtype) { $enumValuesExpr = "/^(enum|set)\\s*\\(['\"](?<values>[^'\"]+)['\"]\\).*/i";
if (preg_match("/^$enumtype/i", $specValue)) { if (preg_match($enumValuesExpr, $specValue, $specMatches)
$newStr = preg_replace("/(^$enumtype\\s*\\(')|('\\).*)/i", "", $spec_orig); && preg_match($enumValuesExpr, $spec_orig, $oldMatches)
$new = preg_split("/'\\s*,\\s*'/", $newStr); ) {
$new = preg_split("/'\\s*,\\s*'/", $specMatches['values']);
$old = preg_split("/'\\s*,\\s*'/", $oldMatches['values']);
$oldStr = preg_replace("/(^$enumtype\\s*\\(')|('\\).*)/i", "", $fieldValue); $holder = array();
$old = preg_split("/'\\s*,\\s*'/", $oldStr); foreach ($old as $check) {
if (!in_array($check, $new)) {
$holder = array(); $holder[] = $check;
foreach ($old as $check) {
if (!in_array($check, $new)) {
$holder[] = $check;
}
} }
if (count($holder)) { }
if (count($holder)) {
// Get default pre-escaped for SQL. We just use this directly, as we don't have a real way to
// de-encode SQL values
$default = explode('default ', $spec_orig); $default = explode('default ', $spec_orig);
$default = $default[1]; $defaultSQL = isset($default[1]) ? $default[1] : 'NULL';
$query = "UPDATE \"$table\" SET $field=$default WHERE $field IN ("; // Reset to default any value in that is in the old enum, but not the new one
for ($i = 0; $i + 1 < count($holder); $i++) { $placeholders = DB::placeholders($holder);
$query .= "'{$holder[$i]}', "; $query = "UPDATE \"{$table}\" SET \"{$field}\" = {$defaultSQL} WHERE \"{$field}\" IN ({$placeholders})";
} $this->preparedQuery($query, $holder);
$query .= "'{$holder[$i]}')";
$this->query($query);
$amount = $this->database->affectedRows(); $amount = $this->database->affectedRows();
$this->alterationMessage("Changed $amount rows to default value of field $field" $this->alterationMessage(
. " (Value: $default)"); "Changed $amount rows to default value of field $field (Value: $defaultSQL)"
} );
} }
} }
$this->transAlterField($table, $field, $spec_orig); $this->transAlterField($table, $field, $spec_orig);

View File

@ -41,11 +41,12 @@ if (!defined('BASE_PATH')) {
define('BASE_PATH', call_user_func(function () { define('BASE_PATH', call_user_func(function () {
// Determine BASE_PATH based on the composer autoloader // Determine BASE_PATH based on the composer autoloader
foreach (debug_backtrace() as $backtraceItem) { foreach (debug_backtrace() as $backtraceItem) {
if (isset($backtraceItem['file']) && preg_match( if (isset($backtraceItem['file'])
'#^(?<base>.*)(/|\\\\)vendor(/|\\\\)composer(/|\\\\)autoload_real.php#', && preg_match(
$backtraceItem['file'], '#^(?<base>.*)(/|\\\\)vendor(/|\\\\)composer(/|\\\\)autoload_real.php#',
$matches $backtraceItem['file'],
)) { $matches
)) {
return realpath($matches['base']) ?: DIRECTORY_SEPARATOR; return realpath($matches['base']) ?: DIRECTORY_SEPARATOR;
} }
} }
@ -132,23 +133,24 @@ if (!defined('BASE_URL')) {
// When htaccess redirects from /base to /base/public folder, we need to only include /public // When htaccess redirects from /base to /base/public folder, we need to only include /public
// in the BASE_URL if it's also present in the request // in the BASE_URL if it's also present in the request
if ($baseURL if (!$baseURL
&& PUBLIC_DIR || !PUBLIC_DIR
&& isset($_SERVER['REQUEST_URI']) || !isset($_SERVER['REQUEST_URI'])
&& substr($baseURL, -strlen(PUBLIC_DIR)) === PUBLIC_DIR || substr($baseURL, -strlen(PUBLIC_DIR)) !== PUBLIC_DIR
) { ) {
$requestURI = $_SERVER['REQUEST_URI']; return $baseURL;
// Check if /base/public or /base are in the request
foreach ([$baseURL, dirname($baseURL)] as $candidate) {
if (stripos($requestURI, $candidate) === 0) {
return $candidate;
}
}
// Ambiguous
return '';
} }
return $baseURL; $requestURI = $_SERVER['REQUEST_URI'];
// Check if /base/public or /base are in the request
foreach ([$baseURL, dirname($baseURL)] as $candidate) {
if (stripos($requestURI, $candidate) === 0) {
return $candidate;
}
}
// Ambiguous
return '';
})); }));
} }