mirror of
https://github.com/moodle/moodle.git
synced 2025-08-09 10:56:56 +02:00
MDL-19711 dml: Enable use of readonly slave database handles
Implemented with moodle_read_slave_trait Functionality is triggered by supplying config dboption['readonly']. See config-dist.php for more info on supported dboptions. pgsql and mysqli drivers are using this feature. Also added support for connection timeout for these two drivers.
This commit is contained in:
parent
d85118369d
commit
46cfde3d95
19 changed files with 2422 additions and 28 deletions
|
@ -25,6 +25,7 @@
|
|||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once(__DIR__.'/moodle_database.php');
|
||||
require_once(__DIR__.'/moodle_read_slave_trait.php');
|
||||
require_once(__DIR__.'/mysqli_native_moodle_recordset.php');
|
||||
require_once(__DIR__.'/mysqli_native_moodle_temptables.php');
|
||||
|
||||
|
@ -36,6 +37,9 @@ require_once(__DIR__.'/mysqli_native_moodle_temptables.php');
|
|||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mysqli_native_moodle_database extends moodle_database {
|
||||
use moodle_read_slave_trait {
|
||||
can_use_readonly as read_slave_can_use_readonly;
|
||||
}
|
||||
|
||||
/** @var mysqli $mysqli */
|
||||
protected $mysqli = null;
|
||||
|
@ -235,6 +239,14 @@ class mysqli_native_moodle_database extends moodle_database {
|
|||
if (isset($this->dboptions['dbcollation'])) {
|
||||
return $this->dboptions['dbcollation'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 'dbcollation' option
|
||||
*
|
||||
* @return string $dbcollation
|
||||
*/
|
||||
private function detect_collation(): string {
|
||||
if ($this->external) {
|
||||
return null;
|
||||
}
|
||||
|
@ -246,9 +258,7 @@ class mysqli_native_moodle_database extends moodle_database {
|
|||
$sql = "SELECT collation_name
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE table_schema = DATABASE() AND table_name = '{$this->prefix}config' AND column_name = 'value'";
|
||||
$this->query_start($sql, NULL, SQL_QUERY_AUX);
|
||||
$result = $this->mysqli->query($sql);
|
||||
$this->query_end($result);
|
||||
if ($rec = $result->fetch_assoc()) {
|
||||
// MySQL 8 BC: information_schema.* returns the fields in upper case.
|
||||
$rec = array_change_key_case($rec, CASE_LOWER);
|
||||
|
@ -260,9 +270,7 @@ class mysqli_native_moodle_database extends moodle_database {
|
|||
if (!$collation) {
|
||||
// Get the default database collation, but only if using UTF-8.
|
||||
$sql = "SELECT @@collation_database";
|
||||
$this->query_start($sql, NULL, SQL_QUERY_AUX);
|
||||
$result = $this->mysqli->query($sql);
|
||||
$this->query_end($result);
|
||||
if ($rec = $result->fetch_assoc()) {
|
||||
if (strpos($rec['@@collation_database'], 'utf8_') === 0 || strpos($rec['@@collation_database'], 'utf8mb4_') === 0) {
|
||||
$collation = $rec['@@collation_database'];
|
||||
|
@ -275,9 +283,7 @@ class mysqli_native_moodle_database extends moodle_database {
|
|||
// We want only utf8 compatible collations.
|
||||
$collation = null;
|
||||
$sql = "SHOW COLLATION WHERE Collation LIKE 'utf8mb4\_%' AND Charset = 'utf8mb4'";
|
||||
$this->query_start($sql, NULL, SQL_QUERY_AUX);
|
||||
$result = $this->mysqli->query($sql);
|
||||
$this->query_end($result);
|
||||
while ($res = $result->fetch_assoc()) {
|
||||
$collation = $res['Collation'];
|
||||
if (strtoupper($res['Default']) === 'YES') {
|
||||
|
@ -518,7 +524,6 @@ class mysqli_native_moodle_database extends moodle_database {
|
|||
|
||||
/**
|
||||
* Connect to db
|
||||
* Must be called before other methods.
|
||||
* @param string $dbhost The database host.
|
||||
* @param string $dbuser The database username.
|
||||
* @param string $dbpass The database username's password.
|
||||
|
@ -527,7 +532,7 @@ class mysqli_native_moodle_database extends moodle_database {
|
|||
* @param array $dboptions driver specific options
|
||||
* @return bool success
|
||||
*/
|
||||
public function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null) {
|
||||
public function raw_connect(string $dbhost, string $dbuser, string $dbpass, string $dbname, $prefix, array $dboptions=null): bool {
|
||||
$driverstatus = $this->driver_installed();
|
||||
|
||||
if ($driverstatus !== true) {
|
||||
|
@ -556,10 +561,19 @@ class mysqli_native_moodle_database extends moodle_database {
|
|||
if ($dbhost and !empty($this->dboptions['dbpersist'])) {
|
||||
$dbhost = "p:$dbhost";
|
||||
}
|
||||
$this->mysqli = @new mysqli($dbhost, $dbuser, $dbpass, $dbname, $dbport, $dbsocket);
|
||||
$this->mysqli = mysqli_init();
|
||||
if (!empty($this->dboptions['connecttimeout'])) {
|
||||
$this->mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, $this->dboptions['connecttimeout']);
|
||||
}
|
||||
|
||||
if ($this->mysqli->connect_errno !== 0) {
|
||||
$dberr = $this->mysqli->connect_error;
|
||||
$conn = null;
|
||||
try {
|
||||
$conn = $this->mysqli->real_connect($dbhost, $dbuser, $dbpass, $dbname, $dbport, $dbsocket);
|
||||
} catch (\Exception $e) {
|
||||
$dberr = "$e";
|
||||
}
|
||||
if (!$conn) {
|
||||
$dberr = $dberr ?: $this->mysqli->connect_error;
|
||||
$this->mysqli = null;
|
||||
throw new dml_connection_exception($dberr);
|
||||
}
|
||||
|
@ -568,16 +582,14 @@ class mysqli_native_moodle_database extends moodle_database {
|
|||
$this->query_log_prevent();
|
||||
|
||||
if (isset($dboptions['dbcollation'])) {
|
||||
$collationinfo = explode('_', $dboptions['dbcollation']);
|
||||
$this->dboptions['dbcollation'] = $dboptions['dbcollation'];
|
||||
$collation = $this->dboptions['dbcollation'] = $dboptions['dbcollation'];
|
||||
} else {
|
||||
$collationinfo = explode('_', $this->get_dbcollation());
|
||||
$collation = $this->detect_collation();
|
||||
}
|
||||
$collationinfo = explode('_', $collation);
|
||||
$charset = reset($collationinfo);
|
||||
|
||||
$this->query_start("--set_charset()", null, SQL_QUERY_AUX);
|
||||
$this->mysqli->set_charset($charset);
|
||||
$this->query_end(true);
|
||||
|
||||
// If available, enforce strict mode for the session. That guaranties
|
||||
// standard behaviour under some situations, avoiding some MySQL nasty
|
||||
|
@ -587,9 +599,7 @@ class mysqli_native_moodle_database extends moodle_database {
|
|||
$si = $this->get_server_info();
|
||||
if (version_compare($si['version'], '5.0.2', '>=')) {
|
||||
$sql = "SET SESSION sql_mode = 'STRICT_ALL_TABLES'";
|
||||
$this->query_start($sql, null, SQL_QUERY_AUX);
|
||||
$result = $this->mysqli->query($sql);
|
||||
$this->query_end($result);
|
||||
}
|
||||
|
||||
// We can enable logging now.
|
||||
|
@ -614,6 +624,40 @@ class mysqli_native_moodle_database extends moodle_database {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets db handle currently used with queries
|
||||
* @return resource
|
||||
*/
|
||||
protected function get_db_handle() {
|
||||
return $this->mysqli;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets db handle to be used with subsequent queries
|
||||
* @param resource $dbh
|
||||
* @return void
|
||||
*/
|
||||
protected function set_db_handle($dbh): void {
|
||||
$this->mysqli = $dbh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if The query qualifies for readonly connection execution
|
||||
* Logging queries are exempt, those are write operations that circumvent
|
||||
* standard query_start/query_end paths.
|
||||
* @param int $type type of query
|
||||
* @param string $sql
|
||||
* @return bool
|
||||
*/
|
||||
protected function can_use_readonly(int $type, string $sql): bool {
|
||||
// ... *_LOCK queries always go to master.
|
||||
if (preg_match('/\b(GET|RELEASE)_LOCK/i', $sql)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->read_slave_can_use_readonly($type, $sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns database server info array
|
||||
* @return array Array containing 'description' and 'version' info
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue