From ec647ed00729fad8e5cb97a0905e304e9c81f9c1 Mon Sep 17 00:00:00 2001 From: Oliver Shaw Date: Sat, 5 Sep 2015 12:50:26 +1200 Subject: [PATCH] UPDATE: Add support for user defined charset and collation --- _config/config.yml | 5 +++++ model/connect/MySQLDatabase.php | 15 ++++++++++++--- model/connect/MySQLSchemaManager.php | 20 +++++++++++++++----- model/connect/MySQLiConnector.php | 15 +++++++++++++-- model/connect/PDOConnector.php | 11 +++++++++-- model/fieldtypes/Enum.php | 7 +++++-- model/fieldtypes/MultiEnum.php | 7 ++++--- model/fieldtypes/Text.php | 7 +++++-- model/fieldtypes/Varchar.php | 7 +++++-- 9 files changed, 73 insertions(+), 21 deletions(-) diff --git a/_config/config.yml b/_config/config.yml index d6d9357ad..42dc3103c 100644 --- a/_config/config.yml +++ b/_config/config.yml @@ -5,7 +5,12 @@ Upload: # Replace an existing file rather than renaming the new one. replaceFile: false MySQLDatabase: + # You are advised to backup your tables if changing settings on an existing database + # `connection_charset` and `charset` should be equal, similarly so should `connection_collation` and `collation` connection_charset: utf8 + connection_collation: utf8_general_ci + charset: utf8 + collation: utf8_general_ci HTTP: cache_control: max-age: 0 diff --git a/model/connect/MySQLDatabase.php b/model/connect/MySQLDatabase.php index 03969dd86..d743a942d 100644 --- a/model/connect/MySQLDatabase.php +++ b/model/connect/MySQLDatabase.php @@ -31,6 +31,13 @@ class MySQLDatabase extends SS_Database { $parameters['charset'] = $charset; } + // Set collation + if( empty($parameters['collation']) + && ($collation = Config::inst()->get('MySQLDatabase', 'connection_collation')) + ) { + $parameters['collation'] = $collation; + } + // Notify connector of parameters $this->connector->connect($parameters); @@ -159,18 +166,20 @@ class MySQLDatabase extends SS_Database { $baseClasses[$class] = '"' . $class . '"'; } + $charset = Config::inst()->get('MySQLDatabase', 'charset'); + // Make column selection lists $select = array( 'SiteTree' => array( "ClassName", "$baseClasses[SiteTree].\"ID\"", "ParentID", "Title", "MenuTitle", "URLSegment", "Content", "LastEdited", "Created", - "Filename" => "_utf8''", "Name" => "_utf8''", + "Filename" => "_{$charset}''", "Name" => "_{$charset}''", "Relevance" => $relevance['SiteTree'], "CanViewType" ), 'File' => array( - "ClassName", "$baseClasses[File].\"ID\"", "ParentID" => "_utf8''", - "Title", "MenuTitle" => "_utf8''", "URLSegment" => "_utf8''", "Content", + "ClassName", "$baseClasses[File].\"ID\"", "ParentID" => "_{$charset}''", + "Title", "MenuTitle" => "_{$charset}''", "URLSegment" => "_{$charset}''", "Content", "LastEdited", "Created", "Filename", "Name", "Relevance" => $relevance['File'], "CanViewType" => "NULL" diff --git a/model/connect/MySQLSchemaManager.php b/model/connect/MySQLSchemaManager.php index 3fc9d59db..7487f4db7 100644 --- a/model/connect/MySQLSchemaManager.php +++ b/model/connect/MySQLSchemaManager.php @@ -194,7 +194,9 @@ class MySQLSchemaManager extends DBSchemaManager { } public function createDatabase($name) { - $this->query("CREATE DATABASE \"$name\" DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci"); + $charset = Config::inst()->get('MySQLDatabase', 'charset'); + $collation = Config::inst()->get('MySQLDatabase', 'collation'); + $this->query("CREATE DATABASE \"$name\" DEFAULT CHARACTER SET {$charset} DEFAULT COLLATE {$collation}"); } public function dropDatabase($name) { @@ -433,7 +435,9 @@ class MySQLSchemaManager extends DBSchemaManager { //DB::requireField($this->tableName, $this->name, "enum('" . implode("','", $this->enum) . "') character set // utf8 collate utf8_general_ci default '{$this->default}'"); $valuesString = implode(",", Convert::raw2sql($values['enums'], true)); - return "enum($valuesString) character set utf8 collate utf8_general_ci" . $this->defaultClause($values); + $charset = Config::inst()->get('MySQLDatabase', 'charset'); + $collation = Config::inst()->get('MySQLDatabase', 'collation'); + return "enum($valuesString) character set {$charset} collate {$collation}" . $this->defaultClause($values); } /** @@ -449,7 +453,9 @@ class MySQLSchemaManager extends DBSchemaManager { //DB::requireField($this->tableName, $this->name, "enum('" . implode("','", $this->enum) . "') character set //utf8 collate utf8_general_ci default '{$this->default}'"); $valuesString = implode(",", Convert::raw2sql($values['enums'], true)); - return "set($valuesString) character set utf8 collate utf8_general_ci" . $this->defaultClause($values); + $charset = Config::inst()->get('MySQLDatabase', 'charset'); + $collation = Config::inst()->get('MySQLDatabase', 'collation'); + return "set($valuesString) character set {$charset} collate {$collation}" . $this->defaultClause($values); } /** @@ -503,7 +509,9 @@ class MySQLSchemaManager extends DBSchemaManager { //For reference, this is what typically gets passed to this function: //$parts=Array('datatype'=>'mediumtext', 'character set'=>'utf8', 'collate'=>'utf8_general_ci'); //DB::requireField($this->tableName, $this->name, "mediumtext character set utf8 collate utf8_general_ci"); - return 'mediumtext character set utf8 collate utf8_general_ci' . $this->defaultClause($values); + $charset = Config::inst()->get('MySQLDatabase', 'charset'); + $collation = Config::inst()->get('MySQLDatabase', 'collation'); + return 'mediumtext character set ' . $charset . ' collate ' . $collation . $this->defaultClause($values); } /** @@ -533,7 +541,9 @@ class MySQLSchemaManager extends DBSchemaManager { //DB::requireField($this->tableName, $this->name, "varchar($this->size) character set utf8 collate // utf8_general_ci"); $default = $this->defaultClause($values); - return "varchar({$values['precision']}) character set utf8 collate utf8_general_ci$default"; + $charset = Config::inst()->get('MySQLDatabase', 'charset'); + $collation = Config::inst()->get('MySQLDatabase', 'collation'); + return "varchar({$values['precision']}) character set {$charset} collate {$collation}{$default}"; } /* diff --git a/model/connect/MySQLiConnector.php b/model/connect/MySQLiConnector.php index 8a07f37d0..6ffa401c6 100644 --- a/model/connect/MySQLiConnector.php +++ b/model/connect/MySQLiConnector.php @@ -56,6 +56,10 @@ class MySQLiConnector extends DBConnector { // Normally $selectDB is set to false by the MySQLDatabase controller, as per convention $selectedDB = ($selectDB && !empty($parameters['database'])) ? $parameters['database'] : null; + // Connection charset and collation + $connCharset = Config::inst()->get('MySQLDatabase', 'connection_charset'); + $connCollation = Config::inst()->get('MySQLDatabase', 'connection_collation'); + if(!empty($parameters['port'])) { $this->dbConn = new MySQLi( $parameters['server'], @@ -77,11 +81,18 @@ class MySQLiConnector extends DBConnector { $this->databaseError("Couldn't connect to MySQL database | " . $this->dbConn->connect_error); } - // Set charset if given and not null. Can explicitly set to empty string to omit + // Set charset and collation if given and not null. Can explicitly set to empty string to omit $charset = isset($parameters['charset']) ? $parameters['charset'] - : 'utf8'; + : $connCharset; + if (!empty($charset)) $this->dbConn->set_charset($charset); + + $collation = isset($parameters['collation']) + ? $parameters['collation'] + : $connCollation; + + if (!empty($collation)) $this->dbConn->query("SET collation_connection = {$collation}"); } public function __destruct() { diff --git a/model/connect/PDOConnector.php b/model/connect/PDOConnector.php index 97b3506bc..6b423754e 100644 --- a/model/connect/PDOConnector.php +++ b/model/connect/PDOConnector.php @@ -134,17 +134,24 @@ class PDOConnector extends DBConnector { } } + // Connection charset and collation + $connCharset = Config::inst()->get('MySQLDatabase', 'connection_charset'); + $connCollation = Config::inst()->get('MySQLDatabase', 'connection_collation'); + // Set charset if given and not null. Can explicitly set to empty string to omit if($parameters['driver'] !== 'sqlsrv') { $charset = isset($parameters['charset']) ? $parameters['charset'] - : 'utf8'; + : $connCharset; if (!empty($charset)) $dsn[] = "charset=$charset"; } // Connection commands to be run on every re-connection + if(!isset($charset)) { + $charset = $connCharset; + } $options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . $charset . ' COLLATE ' . $connCollation ); if(self::is_emulate_prepare()) { $options[PDO::ATTR_EMULATE_PREPARES] = true; diff --git a/model/fieldtypes/Enum.php b/model/fieldtypes/Enum.php index c09ef3c2d..cca542aaa 100644 --- a/model/fieldtypes/Enum.php +++ b/model/fieldtypes/Enum.php @@ -65,11 +65,14 @@ class Enum extends StringField { * @return void */ public function requireField() { + $charset = Config::inst()->get('MySQLDatabase', 'charset'); + $collation = Config::inst()->get('MySQLDatabase', 'collation'); + $parts = array( 'datatype' => 'enum', 'enums' => $this->enum, - 'character set' => 'utf8', - 'collate' => 'utf8_general_ci', + 'character set' => $charset, + 'collate' => $collation, 'default' => $this->default, 'table' => $this->tableName, 'arrayValue' => $this->arrayValue diff --git a/model/fieldtypes/MultiEnum.php b/model/fieldtypes/MultiEnum.php index d1b8089f4..72eb93382 100644 --- a/model/fieldtypes/MultiEnum.php +++ b/model/fieldtypes/MultiEnum.php @@ -31,13 +31,14 @@ class MultiEnum extends Enum { } public function requireField(){ - + $charset = Config::inst()->get('MySQLDatabase', 'charset'); + $collation = Config::inst()->get('MySQLDatabase', 'collation'); $values=array( 'type'=>'set', 'parts'=>array( 'enums'=>$this->enum, - 'character set'=>'utf8', - 'collate'=> 'utf8_general_ci', + 'character set'=> $charset, + 'collate'=> $collation, 'default'=> $this->default, 'table'=>$this->tableName, 'arrayValue'=>$this->arrayValue diff --git a/model/fieldtypes/Text.php b/model/fieldtypes/Text.php index 97ad02b81..32d3db68f 100644 --- a/model/fieldtypes/Text.php +++ b/model/fieldtypes/Text.php @@ -37,10 +37,13 @@ class Text extends StringField { * @see DBField::requireField() */ public function requireField() { + $charset = Config::inst()->get('MySQLDatabase', 'charset'); + $collation = Config::inst()->get('MySQLDatabase', 'collation'); + $parts = array( 'datatype' => 'mediumtext', - 'character set' => 'utf8', - 'collate' => 'utf8_general_ci', + 'character set' => $charset, + 'collate' => $collation, 'arrayValue' => $this->arrayValue ); diff --git a/model/fieldtypes/Varchar.php b/model/fieldtypes/Varchar.php index 43eb83d9c..f1924123a 100644 --- a/model/fieldtypes/Varchar.php +++ b/model/fieldtypes/Varchar.php @@ -50,11 +50,14 @@ class Varchar extends StringField { * @see DBField::requireField() */ public function requireField() { + $charset = Config::inst()->get('MySQLDatabase', 'charset'); + $collation = Config::inst()->get('MySQLDatabase', 'collation'); + $parts = array( 'datatype'=>'varchar', 'precision'=>$this->size, - 'character set'=>'utf8', - 'collate'=>'utf8_general_ci', + 'character set'=> $charset, + 'collate'=> $collation, 'arrayValue'=>$this->arrayValue );