MDL-37734 use prefetching workaround for MARS transaction problems

This commit is contained in:
Petr Škoda 2013-01-25 22:46:12 +01:00
parent 37c68301f9
commit 0363b7826b
2 changed files with 72 additions and 4 deletions

View File

@ -41,6 +41,8 @@ class sqlsrv_native_moodle_database extends moodle_database {
protected $last_error_reporting; // To handle SQL*Server-Native driver default verbosity
protected $temptables; // Control existing temptables (sqlsrv_moodle_temptables object)
protected $collation; // current DB collation cache
/** @var array list of open recordsets */
protected $recordsets = array();
/**
* Constructor - instantiates the database, specifying if it's external (connect to other systems) or no (Moodle DB)
@ -789,7 +791,20 @@ class sqlsrv_native_moodle_database extends moodle_database {
* @return sqlsrv_native_moodle_recordset
*/
protected function create_recordset($result) {
return new sqlsrv_native_moodle_recordset($result);
$rs = new sqlsrv_native_moodle_recordset($result, $this);
$this->recordsets[] = $rs;
return $rs;
}
/**
* Do not use outside of recordset class.
* @internal
* @param sqlsrv_native_moodle_recordset $rs
*/
public function recordset_closed(sqlsrv_native_moodle_recordset $rs) {
if ($key = array_search($rs, $this->recordsets, true)) {
unset($this->recordsets[$key]);
}
}
/**
@ -1367,6 +1382,12 @@ class sqlsrv_native_moodle_database extends moodle_database {
* @return void
*/
protected function begin_transaction() {
// Recordsets do not work well with transactions in SQL Server,
// let's prefetch the recordsets to memory to work around these problems.
foreach ($this->recordsets as $rs) {
$rs->transaction_starts();
}
$this->query_start('native sqlsrv_begin_transaction', NULL, SQL_QUERY_AUX);
$result = sqlsrv_begin_transaction($this->sqlsrv);
$this->query_end($result);

View File

@ -31,9 +31,49 @@ class sqlsrv_native_moodle_recordset extends moodle_recordset {
protected $rsrc;
protected $current;
public function __construct($rsrc) {
$this->rsrc = $rsrc;
/** @var array recordset buffer */
protected $buffer = null;
/** @var sqlsrv_native_moodle_database */
protected $db;
public function __construct($rsrc, sqlsrv_native_moodle_database $db) {
$this->rsrc = $rsrc;
$this->current = $this->fetch_next();
$this->db = $db;
}
/**
* Inform existing open recordsets that transaction
* is starting, this works around MARS problem described
* in MDL-37734.
*/
public function transaction_starts() {
if ($this->buffer !== null) {
$this->unregister();
return;
}
if (!$this->rsrc) {
$this->unregister();
return;
}
// This might eat memory pretty quickly...
raise_memory_limit('2G');
$this->buffer = array();
while($next = $this->fetch_next()) {
$this->buffer[] = $next;
}
}
/**
* Unregister recordset from the global list of open recordsets.
*/
private function unregister() {
if ($this->db) {
$this->db->recordset_closed($this);
$this->db = null;
}
}
public function __destruct() {
@ -47,6 +87,7 @@ class sqlsrv_native_moodle_recordset extends moodle_recordset {
if (!$row = sqlsrv_fetch_array($this->rsrc, SQLSRV_FETCH_ASSOC)) {
sqlsrv_free_stmt($this->rsrc);
$this->rsrc = null;
$this->unregister();
return false;
}
@ -69,7 +110,11 @@ class sqlsrv_native_moodle_recordset extends moodle_recordset {
}
public function next() {
$this->current = $this->fetch_next();
if ($this->buffer === null) {
$this->current = $this->fetch_next();
} else {
$this->current = array_shift($this->buffer);
}
}
public function valid() {
@ -82,5 +127,7 @@ class sqlsrv_native_moodle_recordset extends moodle_recordset {
$this->rsrc = null;
}
$this->current = null;
$this->buffer = null;
$this->unregister();
}
}