diff --git a/lib/datalib.php b/lib/datalib.php index c7ef03ca556..175bce45520 100644 --- a/lib/datalib.php +++ b/lib/datalib.php @@ -1232,6 +1232,36 @@ function update_timezone_records($timezones) { } } +/** + * Increment standard revision field. + * + * The revision are based on current time and are incrementing. + * There is a protection for runaway revisions, it may not go further than + * one hour into future. + * + * The field has to be XMLDB_TYPE_INTEGER with size 10. + * + * @param string $table + * @param string $field name of the field containing revision + * @param string $select use empty string when updating all records + * @param array $params optional select parameters + */ +function increment_revision_number($table, $field, $select, array $params = null) { + global $DB; + + $now = time(); + $sql = "UPDATE {{$table}} + SET $field = (CASE + WHEN $field IS NULL THEN $now + WHEN $field < $now THEN $now + WHEN $field > $now + 3600 THEN $now + ELSE $field + 1 END)"; + if ($select) { + $sql = $sql . " WHERE $select"; + } + $DB->execute($sql, $params); +} + /// MODULE FUNCTIONS ///////////////////////////////////////////////// diff --git a/lib/tests/datalib_test.php b/lib/tests/datalib_test.php index 465bd4a50e0..0357707a337 100644 --- a/lib/tests/datalib_test.php +++ b/lib/tests/datalib_test.php @@ -289,4 +289,58 @@ class core_datalib_testcase extends advanced_testcase { $this->assertSame('ZOMBIES', $result->shortname); $this->assertEquals($before + 1, $DB->perf_get_queries()); } + + public function test_increment_revision_number() { + global $DB; + $this->resetAfterTest(); + + // Let's abuse course.timemodified column for now. + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $DB->set_field('course', 'timemodified', 1, array()); + + $record1 = $DB->get_record('course', array('id'=>$course1->id)); + $record2 = $DB->get_record('course', array('id'=>$course2->id)); + $this->assertEquals(1, $record1->timemodified); + $this->assertEquals(1, $record2->timemodified); + + // Incrementing some lower value. + $now = time(); + increment_revision_number('course', 'timemodified', 'id = :id', array('id'=>$course1->id)); + $record1 = $DB->get_record('course', array('id'=>$course1->id)); + $record2 = $DB->get_record('course', array('id'=>$course2->id)); + $this->assertGreaterThanOrEqual($now, $record1->timemodified); + $this->assertLessThanOrEqual($now+1, $record1->timemodified); + $this->assertEquals(1, $record2->timemodified); + + // Incrementing in the same second. + $rev1 = $DB->get_field('course', 'timemodified', array('id'=>$course1->id)); + $now = time(); + increment_revision_number('course', 'timemodified', 'id = :id', array('id'=>$course1->id)); + $this->assertGreaterThan($rev1, $rev1 = $DB->get_field('course', 'timemodified', array('id'=>$course1->id))); + increment_revision_number('course', 'timemodified', 'id = :id', array('id'=>$course1->id)); + $this->assertGreaterThan($rev1, $rev1 = $DB->get_field('course', 'timemodified', array('id'=>$course1->id))); + increment_revision_number('course', 'timemodified', 'id = :id', array('id'=>$course1->id)); + $this->assertGreaterThan($rev1, $rev1 = $DB->get_field('course', 'timemodified', array('id'=>$course1->id))); + $this->assertGreaterThan($now+2, $rev1); + + // Recovering from runaway revision. + $DB->set_field('course', 'timemodified', time()+60*60*60, array('id'=>$course2->id)); + $record2 = $DB->get_record('course', array('id'=>$course2->id)); + $this->assertGreaterThan(time(), $record2->timemodified); + increment_revision_number('course', 'timemodified', 'id = :id', array('id'=>$course2->id)); + $record2b = $DB->get_record('course', array('id'=>$course2->id)); + $this->assertLessThan($record2->timemodified, $record2b->timemodified); + $this->assertGreaterThanOrEqual(time(), $record2b->timemodified); + + // Update all revisions. + $now = time(); + $DB->set_field('course', 'timemodified', 1, array()); + increment_revision_number('course', 'timemodified', ''); + $record1 = $DB->get_record('course', array('id'=>$course1->id)); + $record2 = $DB->get_record('course', array('id'=>$course2->id)); + $this->assertGreaterThanOrEqual($now, $record1->timemodified); + $this->assertLessThanOrEqual($now+1, $record1->timemodified); + $this->assertEquals($record1->timemodified, $record2->timemodified); + } }