Skip to content

Commit a72c1be

Browse files
Update to add removal of incompatible sql_mode settings when used with MySQL 5.7.0 and newer, per issue processwire/processwire-issues#28
1 parent e84cb81 commit a72c1be

File tree

3 files changed

+160
-9
lines changed

3 files changed

+160
-9
lines changed

Diff for: config.php

+31
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,37 @@
820820
*/
821821
$config->dbPort = 3306;
822822

823+
/**
824+
* Database init command (PDO::MYSQL_ATTR_INIT_COMMAND)
825+
*
826+
* Note: Placeholder "{charset}" gets automatically replaced with $config->dbCharset.
827+
*
828+
* @var string
829+
*
830+
*/
831+
$config->dbInitCommand = "SET NAMES '{charset}'";
832+
833+
/**
834+
* Set or adjust SQL mode per MySQL version
835+
*
836+
* Array indexes are minimum MySQL version mode applies to. Array values are
837+
* the names of the mode(s) to apply. If value is preceded with "remove:" the mode will
838+
* be removed, or if preceded with "add:" the mode will be added. If neither is present
839+
* then the mode will be set exactly as given. To specify more than one SQL mode for the
840+
* value, separate them by commas (CSV). To specify multiple statements for the same
841+
* version, separate them with a slash "/".
842+
*
843+
* ~~~~~
844+
* array("5.7.0" => "remove:STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY/add:NO_ZERO_DATE")
845+
* ~~~~~
846+
*
847+
* @var array
848+
*
849+
*/
850+
$config->dbSqlModes = array(
851+
"5.7.0" => "remove:STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY"
852+
);
853+
823854
/**
824855
* Optional DB socket config for sites that need it (for most you should exclude this)
825856
*

Diff for: core/Config.php

+2
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@
9999
* @property string $dbEngine Database engine (MyISAM or InnoDB) #pw-group-database
100100
* @property string $dbPath MySQL database exec path (Path to mysqldump) #pw-group-database
101101
* @property int $dbQueryLogMax Maximum number of queries WireDatabasePDO will log in memory, when debug mode is enabled (default=1000). #pw-group-database
102+
* @property string $dbInitCommand Database init command, for PDO::MYSQL_ATTR_INIT_COMMAND. Note placeholder {charset} gets replaced with $config->dbCharset. #pw-group-database
103+
* $property array $dbSqlModes Set, add or remove SQL mode based on MySQL version. See default in /wire/config.php for details. #pw-group-database
102104
*
103105
* @property array $pageList Settings specific to Page lists. #pw-group-modules
104106
* @property array $pageEdit Settings specific to Page editors. #pw-group-modules

Diff for: core/WireDatabasePDO.php

+127-9
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ class WireDatabasePDO extends Wire implements WireDatabase {
5555
*/
5656
protected $pdo = null;
5757

58+
/**
59+
* Whether or not our _init() has been called for the current $pdo connection
60+
*
61+
* @var bool
62+
*
63+
*/
64+
protected $init = false;
65+
5866
/**
5967
* PDO connection settings
6068
*
@@ -99,6 +107,8 @@ public static function getInstance(Config $config) {
99107
$name = $config->dbName;
100108
$socket = $config->dbSocket;
101109
$charset = $config->dbCharset;
110+
$initCommand = str_replace('{charset}', $charset, $config->dbInitCommand);
111+
102112
if($socket) {
103113
// if socket is provided ignore $host and $port and use $socket instead:
104114
$dsn = "mysql:unix_socket=$socket;dbname=$name;";
@@ -107,13 +117,18 @@ public static function getInstance(Config $config) {
107117
$port = $config->dbPort;
108118
if($port) $dsn .= ";port=$port";
109119
}
120+
110121
$driver_options = array(
111-
\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES '$charset'",
112122
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
113-
);
123+
);
124+
125+
if($initCommand) $driver_options[\PDO::MYSQL_ATTR_INIT_COMMAND] = $initCommand;
126+
114127
$database = new WireDatabasePDO($dsn, $username, $password, $driver_options);
115128
$database->setDebugMode($config->debug);
116129
$config->wire($database);
130+
$database->_init();
131+
117132
return $database;
118133
}
119134

@@ -134,7 +149,37 @@ public function __construct($dsn, $username = null, $password = null, array $dri
134149
$this->pdoConfig['pass'] = $password;
135150
$this->pdoConfig['options'] = $driver_options;
136151
$this->pdo();
137-
$this->queryLogMax = (int) $this->wire('config')->dbQueryLogMax;
152+
}
153+
154+
/**
155+
* Additional initialization after DB connection established and Wire instance populated
156+
*
157+
* #pw-internal
158+
*
159+
*/
160+
public function _init() {
161+
if($this->init || !$this->isWired()) return;
162+
$this->init = true;
163+
$config = $this->wire('config');
164+
$this->queryLogMax = (int) $config->dbQueryLogMax;
165+
$sqlModes = $config->dbSqlModes;
166+
if(is_array($sqlModes)) {
167+
// ["5.7.0" => "remove:mode1,mode2/add:mode3"]
168+
foreach($sqlModes as $minVersion => $commands) {
169+
if(strpos($commands, '/') !== false) {
170+
$commands = explode('/', $commands);
171+
} else {
172+
$commands = array($commands);
173+
}
174+
foreach($commands as $modes) {
175+
$modes = trim($modes);
176+
if(empty($modes)) continue;
177+
$action = 'set';
178+
if(strpos($modes, ':')) list($action, $modes) = explode(':', $modes);
179+
$this->sqlMode(trim($action), trim($modes), $minVersion);
180+
}
181+
}
182+
}
138183
}
139184

140185
/**
@@ -148,12 +193,16 @@ public function __construct($dsn, $username = null, $password = null, array $dri
148193
*
149194
*/
150195
public function pdo() {
151-
if(!$this->pdo) $this->pdo = new \PDO(
152-
$this->pdoConfig['dsn'],
153-
$this->pdoConfig['user'],
154-
$this->pdoConfig['pass'],
155-
$this->pdoConfig['options']
156-
);
196+
if(!$this->pdo) {
197+
$this->init = false;
198+
$this->pdo = new \PDO(
199+
$this->pdoConfig['dsn'],
200+
$this->pdoConfig['user'],
201+
$this->pdoConfig['pass'],
202+
$this->pdoConfig['options']
203+
);
204+
}
205+
if(!$this->init) $this->_init();
157206
return $this->pdo;
158207
}
159208

@@ -731,4 +780,73 @@ public function getMaxIndexLength() {
731780
return $max;
732781
}
733782

783+
/**
784+
* Get SQL mode, set SQL mode, add to existing SQL mode, or remove from existing SQL mode
785+
*
786+
* #pw-group-custom
787+
*
788+
* ~~~~~
789+
* // Get SQL mode
790+
* $mode = $database->sqlMode();
791+
*
792+
* // Add an SQL mode
793+
* $database->sqlMode('add', 'STRICT_TRANS_TABLES');
794+
*
795+
* // Remove SQL mode if version at least 5.7.0
796+
* $database->sqlMode('remove', 'ONLY_FULL_GROUP_BY', '5.7.0');
797+
* ~~~~~
798+
*
799+
* @param string $action Specify "get", "set", "add" or "remove". (default="get")
800+
* @param string $mode Mode string or CSV string with SQL mode(s), i.e. "STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY".
801+
* This argument should be omitted when using the "get" action.
802+
* @param string $minVersion Make the given action only apply if MySQL version is at least $minVersion, i.e. "5.7.0".
803+
* @return string|bool Returns string in "get" action, boolean false if required version not present, or true otherwise.
804+
* @throws WireException If given an invalid $action
805+
*
806+
*/
807+
public function sqlMode($action = 'get', $mode = '', $minVersion = '') {
808+
809+
$result = true;
810+
$modes = array();
811+
812+
if(empty($action)) $action = 'get';
813+
814+
if($action !== 'get' && $minVersion) {
815+
$serverVersion = $this->getAttribute(\PDO::ATTR_SERVER_VERSION);
816+
if(version_compare($serverVersion, $minVersion, '<')) return false;
817+
}
818+
819+
if($mode) {
820+
foreach(explode(',', $mode) as $m) {
821+
$modes[] = $this->escapeStr(strtoupper($this->wire('sanitizer')->fieldName($m)));
822+
}
823+
}
824+
825+
switch($action) {
826+
case 'get':
827+
$query = $this->pdo()->query("SELECT @@sql_mode");
828+
$result = $query->fetchColumn();
829+
$query->closeCursor();
830+
break;
831+
case 'set':
832+
$modes = implode(',', $modes);
833+
$result = $modes;
834+
$this->pdo()->exec("SET sql_mode='$modes'");
835+
break;
836+
case 'add':
837+
foreach($modes as $m) {
838+
$this->pdo()->exec("SET sql_mode=(SELECT CONCAT(@@sql_mode,',$m'))");
839+
}
840+
break;
841+
case 'remove':
842+
foreach($modes as $m) {
843+
$this->pdo()->exec("SET sql_mode=(SELECT REPLACE(@@sql_mode,'$m',''))");
844+
}
845+
break;
846+
default:
847+
throw new WireException("Unknown action '$action'");
848+
}
849+
850+
return $result;
851+
}
734852
}

0 commit comments

Comments
 (0)