MDL-71000 lib/dml: New query type SQL_QUERY_AUX_READONLY

For SQL_QUERY_AUX queries that can go to readonly, conditions permitting.
This commit is contained in:
Srdjan
2022-08-23 10:39:43 +10:00
parent 17ee072693
commit 5248067884
6 changed files with 261 additions and 72 deletions
+9 -1
View File
@@ -52,6 +52,10 @@ define('SQL_QUERY_STRUCTURE', 4);
/** SQL_QUERY_AUX - Auxiliary query done by driver, setting connection config, getting table info, etc. */
define('SQL_QUERY_AUX', 5);
/** SQL_QUERY_AUX_READONLY - Auxiliary query that can be done using the readonly connection:
* database parameters, table/index/column lists, if not within transaction/ddl. */
define('SQL_QUERY_AUX_READONLY', 6);
/**
* Abstract class representing moodle database interface.
* @link http://docs.moodle.org/dev/DML_functions
@@ -414,9 +418,11 @@ abstract class moodle_database {
/**
* This should be called before each db query.
*
* @param string $sql The query string.
* @param array $params An array of parameters.
* @param int $type The type of query. ( SQL_QUERY_SELECT | SQL_QUERY_AUX | SQL_QUERY_INSERT | SQL_QUERY_UPDATE | SQL_QUERY_STRUCTURE )
* @param int $type The type of query ( SQL_QUERY_SELECT | SQL_QUERY_AUX_READONLY | SQL_QUERY_AUX |
* SQL_QUERY_INSERT | SQL_QUERY_UPDATE | SQL_QUERY_STRUCTURE ).
* @param mixed $extrainfo This is here for any driver specific extra information.
* @return void
*/
@@ -433,6 +439,7 @@ abstract class moodle_database {
switch ($type) {
case SQL_QUERY_SELECT:
case SQL_QUERY_AUX:
case SQL_QUERY_AUX_READONLY:
$this->reads++;
break;
case SQL_QUERY_INSERT:
@@ -483,6 +490,7 @@ abstract class moodle_database {
switch ($type) {
case SQL_QUERY_SELECT:
case SQL_QUERY_AUX:
case SQL_QUERY_AUX_READONLY:
throw new dml_read_exception($error, $sql, $params);
case SQL_QUERY_INSERT:
case SQL_QUERY_UPDATE:
+8 -1
View File
@@ -57,8 +57,9 @@ defined('MOODLE_INTERNAL') || die();
* in the $written array and microtime() the event. For those queries master
* write handle is used.
* - SQL_QUERY_AUX queries will always use the master write handle because they
* are used for transactionstart/end, locking etc. In that respect, query_start() and
* are used for transaction start/end, locking etc. In that respect, query_start() and
* query_end() *must not* be used during the connection phase.
* - SQL_QUERY_AUX_READONLY queries will use the master write handle if in a transaction.
* - SELECT queries will use the master write handle if:
* -- any of the tables involved is a temp table
* -- any of the tables involved is listed in the 'exclude_tables' option
@@ -91,6 +92,7 @@ trait moodle_read_slave_trait {
private $wantreadslave = false;
private $readsslave = 0;
private $slavelatency = 1;
private $structurechange = false;
private $written = []; // Track tables being written to.
private $readexclude = []; // Tables to exclude from using dbhreadonly.
@@ -325,6 +327,10 @@ trait moodle_read_slave_trait {
// Transactions are done as AUX, we cannot play with that.
switch ($type) {
case SQL_QUERY_AUX_READONLY:
// SQL_QUERY_AUX_READONLY may read the structure data.
// We don't have a way to reliably determine whether it is safe to go to readonly if the structure has changed.
return !$this->structurechange;
case SQL_QUERY_SELECT:
if ($this->transactions) {
return false;
@@ -358,6 +364,7 @@ trait moodle_read_slave_trait {
}
return false;
case SQL_QUERY_STRUCTURE:
$this->structurechange = true;
foreach ($this->table_names($sql) as $tablename) {
if (!in_array($tablename, $this->readexclude)) {
$this->readexclude[] = $tablename;
+44 -36
View File
@@ -176,7 +176,7 @@ class mysqli_native_moodle_database extends moodle_database {
$sql = "SELECT engine
FROM INFORMATION_SCHEMA.TABLES
WHERE table_schema = DATABASE() AND table_name = '{$this->prefix}config'";
$this->query_start($sql, NULL, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end($result);
if ($rec = $result->fetch_assoc()) {
@@ -194,7 +194,7 @@ class mysqli_native_moodle_database extends moodle_database {
// Get the default database engine.
$sql = "SELECT @@default_storage_engine engine";
$this->query_start($sql, NULL, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end($result);
if ($rec = $result->fetch_assoc()) {
@@ -205,7 +205,7 @@ class mysqli_native_moodle_database extends moodle_database {
if ($engine === 'MyISAM') {
// we really do not want MyISAM for Moodle, InnoDB or XtraDB is a reasonable defaults if supported
$sql = "SHOW STORAGE ENGINES";
$this->query_start($sql, NULL, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end($result);
$engines = array();
@@ -349,7 +349,7 @@ class mysqli_native_moodle_database extends moodle_database {
$sql = "SHOW VARIABLES LIKE 'innodb_file_format'";
}
$this->query_start($sql, NULL, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end($result);
if ($rec = $result->fetch_assoc()) {
@@ -541,8 +541,8 @@ class mysqli_native_moodle_database extends moodle_database {
$this->store_settings($dbhost, $dbuser, $dbpass, $dbname, $prefix, $dboptions);
// dbsocket is used ONLY if host is NULL or 'localhost',
// you can not disable it because it is always tried if dbhost is 'localhost'
// The dbsocket option is used ONLY if host is null or 'localhost'.
// You can not disable it because it is always tried if dbhost is 'localhost'.
if (!empty($this->dboptions['dbsocket'])
and (strpos($this->dboptions['dbsocket'], '/') !== false or strpos($this->dboptions['dbsocket'], '\\') !== false)) {
$dbsocket = $this->dboptions['dbsocket'];
@@ -696,7 +696,7 @@ class mysqli_native_moodle_database extends moodle_database {
$this->tables = array();
$prefix = str_replace('_', '\\_', $this->prefix);
$sql = "SHOW TABLES LIKE '$prefix%'";
$this->query_start($sql, null, SQL_QUERY_AUX);
$this->query_start($sql, null, $usecache ? SQL_QUERY_AUX_READONLY : SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end($result);
$len = strlen($this->prefix);
@@ -723,7 +723,7 @@ class mysqli_native_moodle_database extends moodle_database {
$indexes = array();
$fixedtable = $this->fix_table_name($table);
$sql = "SHOW INDEXES FROM $fixedtable";
$this->query_start($sql, null, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX_READONLY);
$result = $this->mysqli->query($sql);
try {
$this->query_end($result);
@@ -760,7 +760,7 @@ class mysqli_native_moodle_database extends moodle_database {
WHERE table_name = '" . $this->prefix.$table . "'
AND table_schema = '" . $this->dbname . "'
ORDER BY ordinal_position";
$this->query_start($sql, null, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX_READONLY);
$result = $this->mysqli->query($sql);
$this->query_end(true); // Don't want to throw anything here ever. MDL-30147
@@ -783,7 +783,7 @@ class mysqli_native_moodle_database extends moodle_database {
$result->close();
$fixedtable = $this->fix_table_name($table);
$sql = "SHOW COLUMNS FROM $fixedtable";
$this->query_start($sql, null, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX_READONLY);
$result = $this->mysqli->query($sql);
$this->query_end(true);
if ($result === false) {
@@ -1065,7 +1065,7 @@ class mysqli_native_moodle_database extends moodle_database {
$charset = reset($collationinfo);
$sql = "SHOW COLLATION WHERE Collation ='$collation' AND Charset = '$charset'";
$this->query_start($sql, NULL, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX_READONLY);
$result = $this->mysqli->query($sql);
$this->query_end($result);
if ($result->fetch_assoc()) {
@@ -1404,28 +1404,10 @@ class mysqli_native_moodle_database extends moodle_database {
}
/**
* Insert multiple records into database as fast as possible.
*
* Order of inserts is maintained, but the operation is not atomic,
* use transactions if necessary.
*
* This method is intended for inserting of large number of small objects,
* do not use for huge objects with text or binary fields.
*
* @since Moodle 2.7
*
* @param string $table The database table to be inserted into
* @param array|Traversable $dataobjects list of objects to be inserted, must be compatible with foreach
* @return void does not return new record ids
*
* @throws coding_exception if data objects have different structure
* @throws dml_exception A DML specific exception is thrown for any errors.
* Get chunk size for multiple records insert
* @return int
*/
public function insert_records($table, $dataobjects) {
if (!is_array($dataobjects) and !$dataobjects instanceof Traversable) {
throw new coding_exception('insert_records() passed non-traversable object');
}
private function insert_chunk_size(): int {
// MySQL has a relatively small query length limit by default,
// make sure 'max_allowed_packet' in my.cnf is high enough
// if you change the following default...
@@ -1456,7 +1438,33 @@ class mysqli_native_moodle_database extends moodle_database {
}
}
}
return $chunksize;
}
/**
* Insert multiple records into database as fast as possible.
*
* Order of inserts is maintained, but the operation is not atomic,
* use transactions if necessary.
*
* This method is intended for inserting of large number of small objects,
* do not use for huge objects with text or binary fields.
*
* @since Moodle 2.7
*
* @param string $table The database table to be inserted into
* @param array|Traversable $dataobjects list of objects to be inserted, must be compatible with foreach
* @return void does not return new record ids
*
* @throws coding_exception if data objects have different structure
* @throws dml_exception A DML specific exception is thrown for any errors.
*/
public function insert_records($table, $dataobjects) {
if (!is_array($dataobjects) && !$dataobjects instanceof Traversable) {
throw new coding_exception('insert_records() passed non-traversable object');
}
$chunksize = $this->insert_chunk_size();
$columns = $this->get_columns($table, true);
$fields = null;
$count = 0;
@@ -2023,12 +2031,12 @@ class mysqli_native_moodle_database extends moodle_database {
}
$sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED";
$this->query_start($sql, NULL, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end($result);
$sql = "START TRANSACTION";
$this->query_start($sql, NULL, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end($result);
}
@@ -2044,7 +2052,7 @@ class mysqli_native_moodle_database extends moodle_database {
}
$sql = "COMMIT";
$this->query_start($sql, NULL, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end($result);
}
@@ -2060,7 +2068,7 @@ class mysqli_native_moodle_database extends moodle_database {
}
$sql = "ROLLBACK";
$this->query_start($sql, NULL, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end($result);
+22 -12
View File
@@ -44,6 +44,9 @@ class pgsql_native_moodle_database extends moodle_database {
query_end as read_slave_query_end;
}
/** @var array $serverinfo cache */
private $serverinfo = [];
/** @var array $dbhcursor keep track of open cursors */
private $dbhcursor = [];
@@ -336,7 +339,12 @@ class pgsql_native_moodle_database extends moodle_database {
error_reporting($this->last_error_reporting);
try {
$this->read_slave_query_end($result);
if ($this->savepointpresent and $this->last_type != SQL_QUERY_AUX and $this->last_type != SQL_QUERY_SELECT) {
if ($this->savepointpresent &&
!in_array(
$this->last_type,
[SQL_QUERY_AUX, SQL_QUERY_AUX_READONLY, SQL_QUERY_SELECT],
true
)) {
$res = @pg_query($this->pgsql, "RELEASE SAVEPOINT moodle_pg_savepoint; SAVEPOINT moodle_pg_savepoint");
if ($res) {
pg_free_result($res);
@@ -357,14 +365,16 @@ class pgsql_native_moodle_database extends moodle_database {
* Returns database server info array
* @return array Array containing 'description' and 'version' info
*/
public function get_server_info() {
static $info;
if (!$info) {
$this->query_start("--pg_version()", null, SQL_QUERY_AUX);
$info = pg_version($this->pgsql);
public function get_server_info(): array {
if (empty($this->serverinfo)) {
$this->query_start('--pg_version()', null, SQL_QUERY_AUX);
$this->serverinfo = pg_version($this->pgsql);
$this->query_end(true);
}
return array('description'=>$info['server'], 'version'=>$info['server']);
return [
'description' => $this->serverinfo['server'],
'version' => $this->serverinfo['server'],
];
}
/**
@@ -400,7 +410,7 @@ class pgsql_native_moodle_database extends moodle_database {
WHERE c.relname LIKE '$prefix%' ESCAPE '|'
AND c.relkind = 'r'
AND (ns.nspname = current_schema() OR ns.oid = pg_my_temp_schema())";
$this->query_start($sql, null, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX_READONLY);
$result = pg_query($this->pgsql, $sql);
$this->query_end($result);
@@ -492,7 +502,7 @@ class pgsql_native_moodle_database extends moodle_database {
WHERE i.tablename = '$tablename'
AND (i.schemaname = current_schema() OR ns.oid = pg_my_temp_schema())";
$this->query_start($sql, null, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX_READONLY);
$result = pg_query($this->pgsql, $sql);
$this->query_end($result);
@@ -545,7 +555,7 @@ class pgsql_native_moodle_database extends moodle_database {
AND (ns.nspname = current_schema() OR ns.oid = pg_my_temp_schema())
ORDER BY a.attnum";
$this->query_start($sql, null, SQL_QUERY_AUX);
$this->query_start($sql, null, SQL_QUERY_AUX_READONLY);
$result = pg_query($this->pgsql, $sql);
$this->query_end($result);
@@ -766,8 +776,8 @@ class pgsql_native_moodle_database extends moodle_database {
*/
public function setup_is_unicodedb() {
// Get PostgreSQL server_encoding value
$sql = "SHOW server_encoding";
$this->query_start($sql, null, SQL_QUERY_AUX);
$sql = 'SHOW server_encoding';
$this->query_start($sql, null, SQL_QUERY_AUX_READONLY);
$result = pg_query($this->pgsql, $sql);
$this->query_end($result);
+80 -5
View File
@@ -25,6 +25,8 @@
namespace core;
use moodle_database;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/fixtures/read_slave_moodle_database_mock_mysqli.php');
@@ -45,7 +47,7 @@ class dml_mysqli_read_slave_test extends \base_testcase {
*
* @return void
*/
public function test_lock() : void {
public function test_lock(): void {
$DB = new read_slave_moodle_database_mock_mysqli();
$this->assertEquals(0, $DB->perf_get_reads_slave());
@@ -62,11 +64,11 @@ class dml_mysqli_read_slave_test extends \base_testcase {
}
/**
* Test readonly connection failure with real mysqli connection
* Test readonly handle is used for SQL_QUERY_AUX_READONLY queries.
*
* @return void
*/
public function test_real_readslave_connect_fail() : void {
public function test_aux_readonly(): void {
global $DB;
if ($DB->get_dbfamily() != 'mysql') {
@@ -76,14 +78,87 @@ class dml_mysqli_read_slave_test extends \base_testcase {
// Open second connection.
$cfg = $DB->export_dbconfig();
if (!isset($cfg->dboptions)) {
$cfg->dboptions = array();
$cfg->dboptions = [];
}
$cfg->dboptions['readonly'] = [
'instance' => [$cfg->dbhost]
];
$cfg->dboptions['dbengine'] = null;
$cfg->dboptions['bulkinsertsize'] = null;
// Get a separate disposable db connection handle with guaranteed 'readonly' config.
$db2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
$db2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
$reads = $db2->perf_get_reads();
$readsprimary = $reads - $db2->perf_get_reads_slave();
// Readonly handle queries.
$db2->setup_is_unicodedb();
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertEquals($readsprimary, $reads - $db2->perf_get_reads_slave());
$db2->get_tables();
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertEquals($readsprimary, $reads - $db2->perf_get_reads_slave());
$db2->get_indexes('course');
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertEquals($readsprimary, $reads - $db2->perf_get_reads_slave());
$db2->get_columns('course');
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertEquals($readsprimary, $reads - $db2->perf_get_reads_slave());
// Readwrite handle queries.
if (PHP_INT_SIZE !== 4) {
$rc = new \ReflectionClass(\mysqli_native_moodle_database::class);
$rcm = $rc->getMethod('insert_chunk_size');
$rcm->setAccessible(true);
$rcm->invoke($db2);
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertGreaterThan($readsprimary, $readsprimary = $reads - $db2->perf_get_reads_slave());
}
$db2->get_dbengine();
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertGreaterThan($readsprimary, $readsprimary = $reads - $db2->perf_get_reads_slave());
$db2->diagnose();
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertGreaterThan($readsprimary, $readsprimary = $reads - $db2->perf_get_reads_slave());
$db2->get_row_format('course');
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertGreaterThan($readsprimary, $readsprimary = $reads - $db2->perf_get_reads_slave());
}
/**
* Test readonly connection failure with real mysqli connection
*
* @return void
*/
public function test_real_readslave_connect_fail(): void {
global $DB;
if ($DB->get_dbfamily() != 'mysql') {
$this->markTestSkipped('Not mysql');
}
// Open second connection.
$cfg = $DB->export_dbconfig();
if (!isset($cfg->dboptions)) {
$cfg->dboptions = [];
}
$cfg->dboptions['readonly'] = [
'instance' => ['host.that.is.not'],
'connecttimeout' => 1
];
$db2 = \moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
$db2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
$db2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
$this->assertTrue(count($db2->get_records('user')) > 0);
}
+98 -17
View File
@@ -25,6 +25,9 @@
namespace core;
use moodle_database;
use xmldb_table;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/fixtures/read_slave_moodle_database_mock_pgsql.php');
@@ -38,13 +41,13 @@ require_once(__DIR__.'/fixtures/read_slave_moodle_database_mock_pgsql.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \pgsql_native_moodle_database
*/
class dml_pgsql_read_slave_test extends \base_testcase {
class dml_pgsql_read_slave_test extends \advanced_testcase {
/**
* Test correct database handles are used for cursors
*
* @return void
*/
public function test_cursors() : void {
public function test_cursors(): void {
$DB = new read_slave_moodle_database_mock_pgsql();
// Declare a cursor on a table that has not been written to.
@@ -73,6 +76,18 @@ class dml_pgsql_read_slave_test extends \base_testcase {
$DB->query_start($sql, null, SQL_QUERY_AUX);
$this->assertTrue($DB->db_handle_is_rw());
$DB->query_end(null);
// Close the non-written to table cursor.
$sql = 'CLOSE crs1';
$DB->query_start($sql, [], SQL_QUERY_AUX);
$this->assertTrue($DB->db_handle_is_ro());
$DB->query_end(null);
// Close the written to table cursor.
$sql = 'CLOSE crs2';
$DB->query_start($sql, [], SQL_QUERY_AUX);
$this->assertTrue($DB->db_handle_is_rw());
$DB->query_end(null);
}
/**
@@ -80,7 +95,7 @@ class dml_pgsql_read_slave_test extends \base_testcase {
*
* @return void
*/
public function test_read_pg_table() : void {
public function test_read_pg_table(): void {
$DB = new read_slave_moodle_database_mock_pgsql();
$this->assertEquals(0, $DB->perf_get_reads_slave());
@@ -97,7 +112,7 @@ class dml_pgsql_read_slave_test extends \base_testcase {
*
* @return void
*/
public function test_read_pg_lock_table() : void {
public function test_read_pg_lock_table(): void {
$DB = new read_slave_moodle_database_mock_pgsql();
$this->assertEquals(0, $DB->perf_get_reads_slave());
@@ -111,19 +126,16 @@ class dml_pgsql_read_slave_test extends \base_testcase {
}
/**
* Test readonly handle is not used for reading from temptables
* and getting temptables metadata.
* This test is only possible because of no pg_query error reporting.
* It may need to be removed in the future if we decide to handle null
* results in pgsql_native_moodle_database differently.
* Test readonly handle is used for SQL_QUERY_AUX_READONLY queries.
*
* @return void
*/
public function test_temp_table() : void {
public function test_aux_readonly(): void {
global $DB;
$this->resetAfterTest();
if ($DB->get_dbfamily() != 'postgres') {
$this->markTestSkipped("Not postgres");
$this->markTestSkipped('Not postgres');
}
// Open second connection.
@@ -138,12 +150,81 @@ class dml_pgsql_read_slave_test extends \base_testcase {
}
// Get a separate disposable db connection handle with guaranteed 'readonly' config.
$db2 = \moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
$db2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
$db2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
$reads = $db2->perf_get_reads();
$readsprimary = $reads - $db2->perf_get_reads_slave();
// Readonly handle queries.
$db2->get_server_info();
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertGreaterThan($readsprimary, $readsprimary = $reads - $db2->perf_get_reads_slave());
$db2->setup_is_unicodedb();
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertEquals($readsprimary, $reads - $db2->perf_get_reads_slave());
$db2->get_tables();
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertEquals($readsprimary, $reads - $db2->perf_get_reads_slave());
$db2->get_indexes('course');
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertEquals($readsprimary, $reads - $db2->perf_get_reads_slave());
$db2->get_columns('course');
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertEquals($readsprimary, $reads - $db2->perf_get_reads_slave());
// Readwrite handle queries.
$tablename = 'test_table';
$table = new xmldb_table($tablename);
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$dbman = $db2->get_manager();
$dbman->create_table($table);
$db2->get_columns($tablename);
$this->assertGreaterThan($reads, $reads = $db2->perf_get_reads());
$this->assertGreaterThan($readsprimary, $reads - $db2->perf_get_reads_slave());
}
/**
* Test readonly handle is not used for reading from temptables
* and getting temptables metadata.
* This test is only possible because of no pg_query error reporting.
* It may need to be removed in the future if we decide to handle null
* results in pgsql_native_moodle_database differently.
*
* @return void
*/
public function test_temp_table(): void {
global $DB;
if ($DB->get_dbfamily() != 'postgres') {
$this->markTestSkipped('Not postgres');
}
// Open second connection.
$cfg = $DB->export_dbconfig();
if (!isset($cfg->dboptions)) {
$cfg->dboptions = [];
}
if (!isset($cfg->dboptions['readonly'])) {
$cfg->dboptions['readonly'] = [
'instance' => [$cfg->dbhost]
];
}
// Get a separate disposable db connection handle with guaranteed 'readonly' config.
$db2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
$db2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
$dbman = $db2->get_manager();
$table = new \xmldb_table('silly_test_table');
$table = new xmldb_table('silly_test_table');
$table->add_field('id', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, XMLDB_SEQUENCE);
$table->add_field('msg', XMLDB_TYPE_CHAR, 255);
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
@@ -158,7 +239,7 @@ class dml_pgsql_read_slave_test extends \base_testcase {
$db2->get_records('silly_test_table');
$this->assertEquals($reads, $db2->perf_get_reads_slave());
$table2 = new \xmldb_table('silly_test_table2');
$table2 = new xmldb_table('silly_test_table2');
$table2->add_field('id', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, XMLDB_SEQUENCE);
$table2->add_field('msg', XMLDB_TYPE_CHAR, 255);
$table2->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
@@ -179,11 +260,11 @@ class dml_pgsql_read_slave_test extends \base_testcase {
*
* @return void
*/
public function test_real_readslave_connect_fail() : void {
public function test_real_readslave_connect_fail(): void {
global $DB;
if ($DB->get_dbfamily() != 'postgres') {
$this->markTestSkipped("Not postgres");
$this->markTestSkipped('Not postgres');
}
// Open second connection.
@@ -196,7 +277,7 @@ class dml_pgsql_read_slave_test extends \base_testcase {
'connecttimeout' => 1
];
$db2 = \moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
$db2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
$db2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
$this->assertTrue(count($db2->get_records('user')) > 0);
}