/**
  * Call this method from test if you want to make sure that
  * the resetting of database is done the slow way without transaction
  * rollback.
  *
  * This is useful especially when testing stuff that is not compatible with transactions.
  *
  * @return void
  */
 public function preventResetByRollback()
 {
     if ($this->testdbtransaction and !$this->testdbtransaction->is_disposed()) {
         $this->testdbtransaction->allow_commit();
         $this->testdbtransaction = null;
     }
 }
예제 #2
0
 /**
  * Call when delegated transaction failed, this rolls back
  * all delegated transactions up to the top most level.
  *
  * In many cases you do not need to call this method manually,
  * because all open delegated transactions are rolled back
  * automatically if exceptions not caught.
  *
  * @param moodle_transaction $transaction An instance of a moodle_transaction.
  * @param Exception|Throwable $e The related exception/throwable to this transaction rollback.
  * @return void This does not return, instead the exception passed in will be rethrown.
  */
 public function rollback_delegated_transaction(moodle_transaction $transaction, $e)
 {
     if (!$e instanceof Exception && !$e instanceof Throwable) {
         // PHP7 - we catch Throwables in phpunit but can't use that as the type hint in PHP5.
         $e = new \coding_exception("Must be given an Exception or Throwable object!");
     }
     if ($transaction->is_disposed()) {
         throw new dml_transaction_exception('Transactions already disposed', $transaction);
     }
     // mark as disposed so that it can not be used again
     $transaction->dispose();
     // one rollback at any level rollbacks everything
     $this->force_rollback = true;
     if (empty($this->transactions) or $transaction !== $this->transactions[count($this->transactions) - 1]) {
         // this may or may not be a coding problem, better just rethrow the exception,
         // because we do not want to loose the original $e
         throw $e;
     }
     if (count($this->transactions) == 1) {
         // only rollback the top most level
         $this->rollback_transaction();
     }
     array_pop($this->transactions);
     if (empty($this->transactions)) {
         // finally top most level rolled back
         $this->force_rollback = false;
         \core\event\manager::database_transaction_rolledback();
         \core\message\manager::database_transaction_rolledback();
     }
     throw $e;
 }
예제 #3
0
 function test_wrong_transactions()
 {
     $DB = $this->tdb;
     $dbman = $DB->get_manager();
     $table = $this->get_test_table();
     $tablename = $table->getName();
     $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
     $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
     $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
     $dbman->create_table($table);
     // wrong order of nested commits
     $transaction1 = $DB->start_delegated_transaction();
     $data = (object) array('course' => 3);
     $DB->insert_record($tablename, $data);
     $transaction2 = $DB->start_delegated_transaction();
     $data = (object) array('course' => 4);
     $DB->insert_record($tablename, $data);
     try {
         $transaction1->allow_commit();
         $this->fail('wrong order of commits must throw exception');
     } catch (Exception $e) {
         $this->assertEqual(get_class($e), 'dml_transaction_exception');
     }
     try {
         $transaction2->allow_commit();
         $this->fail('first wrong commit forces rollback');
     } catch (Exception $e) {
         $this->assertEqual(get_class($e), 'dml_transaction_exception');
     }
     // this is done in default exception handler usually
     $this->assertTrue($DB->is_transaction_started());
     $this->assertEqual(2, $DB->count_records($tablename));
     // not rolled back yet
     $DB->force_transaction_rollback();
     $this->assertEqual(0, $DB->count_records($tablename));
     $DB->delete_records($tablename);
     // wrong order of nested rollbacks
     $transaction1 = $DB->start_delegated_transaction();
     $data = (object) array('course' => 3);
     $DB->insert_record($tablename, $data);
     $transaction2 = $DB->start_delegated_transaction();
     $data = (object) array('course' => 4);
     $DB->insert_record($tablename, $data);
     try {
         // this first rollback should prevent all other rollbacks
         $transaction1->rollback(new Exception('test'));
     } catch (Exception $e) {
         $this->assertEqual(get_class($e), 'Exception');
     }
     try {
         $transaction2->rollback(new Exception('test'));
     } catch (Exception $e) {
         $this->assertEqual(get_class($e), 'Exception');
     }
     try {
         $transaction1->rollback(new Exception('test'));
     } catch (Exception $e) {
         // the rollback was used already once, no way to use it again
         $this->assertEqual(get_class($e), 'dml_transaction_exception');
     }
     // this is done in default exception handler usually
     $this->assertTrue($DB->is_transaction_started());
     $DB->force_transaction_rollback();
     $DB->delete_records($tablename);
     // unknown transaction object
     $transaction1 = $DB->start_delegated_transaction();
     $data = (object) array('course' => 3);
     $DB->insert_record($tablename, $data);
     $transaction2 = new moodle_transaction($DB);
     try {
         $transaction2->allow_commit();
         $this->fail('foreign transaction must fail');
     } catch (Exception $e) {
         $this->assertEqual(get_class($e), 'dml_transaction_exception');
     }
     try {
         $transaction1->allow_commit();
         $this->fail('first wrong commit forces rollback');
     } catch (Exception $e) {
         $this->assertEqual(get_class($e), 'dml_transaction_exception');
     }
     $DB->force_transaction_rollback();
     $DB->delete_records($tablename);
 }
예제 #4
0
 /**
  * Call when delegated transaction failed, this rolls back
  * all delegated transactions up to the top most level.
  *
  * In many cases you do not need to call this method manually,
  * because all open delegated transactions are rolled back
  * automatically if exceptions not caught.
  *
  * @param moodle_transaction $transaction An instance of a moodle_transaction.
  * @param Exception $e The related exception to this transaction rollback.
  * @return void This does not return, instead the exception passed in will be rethrown.
  */
 public function rollback_delegated_transaction(moodle_transaction $transaction, Exception $e)
 {
     if ($transaction->is_disposed()) {
         throw new dml_transaction_exception('Transactions already disposed', $transaction);
     }
     // mark as disposed so that it can not be used again
     $transaction->dispose();
     // one rollback at any level rollbacks everything
     $this->force_rollback = true;
     if (empty($this->transactions) or $transaction !== $this->transactions[count($this->transactions) - 1]) {
         // this may or may not be a coding problem, better just rethrow the exception,
         // because we do not want to loose the original $e
         throw $e;
     }
     if (count($this->transactions) == 1) {
         // only rollback the top most level
         $this->rollback_transaction();
     }
     array_pop($this->transactions);
     if (empty($this->transactions)) {
         // finally top most level rolled back
         $this->force_rollback = false;
     }
     throw $e;
 }
예제 #5
0
 /**
  * Persist the passed in grades (keyed by userid) to the database
  *
  * @param array               $grades
  * @param \moodle_transaction $transaction
  *
  * @return bool
  */
 protected function persist_grades($grades, \moodle_transaction $transaction)
 {
     global $DB;
     foreach ($grades as $userid => $grade) {
         if ($usergrade = $DB->get_record('activequiz_grades', array('userid' => $userid, 'activequizid' => $this->rtq->getRTQ()->id))) {
             // we're updating
             $usergrade->grade = $grade;
             $usergrade->timemodified = time();
             if (!$DB->update_record('activequiz_grades', $usergrade)) {
                 $transaction->rollback(new \Exception('Can\'t update user grades'));
             }
         } else {
             // we're adding
             $usergrade = new \stdClass();
             $usergrade->activequizid = $this->rtq->getRTQ()->id;
             $usergrade->userid = $userid;
             $usergrade->grade = $grade;
             $usergrade->timemodified = time();
             if (!$DB->insert_record('activequiz_grades', $usergrade)) {
                 $transaction->rollback(new \Exception('Can\'t insert user grades'));
             }
         }
     }
     return true;
 }