2009-10-01 23:04:18 +02:00
|
|
|
<?php
|
2016-06-15 06:03:16 +02:00
|
|
|
|
2016-10-14 03:30:05 +02:00
|
|
|
namespace SilverStripe\ORM\Tests;
|
|
|
|
|
2016-06-15 06:03:16 +02:00
|
|
|
use SilverStripe\ORM\DB;
|
|
|
|
use SilverStripe\ORM\DataObject;
|
2016-08-19 00:51:35 +02:00
|
|
|
use SilverStripe\Dev\SapphireTest;
|
2018-02-08 22:28:32 +01:00
|
|
|
use SilverStripe\ORM\Tests\TransactionTest\TestObject;
|
2016-08-19 00:51:35 +02:00
|
|
|
|
2016-12-16 05:34:21 +01:00
|
|
|
class TransactionTest extends SapphireTest
|
|
|
|
{
|
2018-02-08 22:28:32 +01:00
|
|
|
protected $usesDatabase = true;
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-10-04 09:25:53 +02:00
|
|
|
protected $usesTransactions = false;
|
|
|
|
|
2018-02-08 22:28:32 +01:00
|
|
|
protected static $extra_dataobjects = [
|
|
|
|
TransactionTest\TestObject::class,
|
|
|
|
];
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-10-18 09:19:39 +02:00
|
|
|
private static $originalVersionInfo;
|
|
|
|
|
2021-10-27 04:39:47 +02:00
|
|
|
public static function setUpBeforeClass(): void
|
2016-12-16 05:34:21 +01:00
|
|
|
{
|
2018-02-08 22:28:32 +01:00
|
|
|
parent::setUpBeforeClass();
|
|
|
|
if (!DB::get_conn()->supportsTransactions()) {
|
|
|
|
static::markTestSkipped('Current database does not support transactions');
|
|
|
|
}
|
|
|
|
}
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-10-18 09:19:39 +02:00
|
|
|
public function testTransactions()
|
|
|
|
{
|
|
|
|
$conn = DB::get_conn();
|
|
|
|
if (!$conn->supportsTransactions()) {
|
|
|
|
$this->markTestSkipped("DB Doesn't support transactions");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-13 09:05:33 +01:00
|
|
|
// Test that successful transactions are committed
|
2018-10-18 09:19:39 +02:00
|
|
|
$obj = new TestObject();
|
|
|
|
$failed = false;
|
|
|
|
$conn->withTransaction(
|
|
|
|
function () use (&$obj) {
|
|
|
|
$obj->Title = 'Save 1';
|
|
|
|
$obj->write();
|
|
|
|
},
|
|
|
|
function () use (&$failed) {
|
|
|
|
$failed = true;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
$this->assertEquals('Save 1', TestObject::get()->first()->Title);
|
|
|
|
$this->assertFalse($failed);
|
|
|
|
|
|
|
|
// Test failed transactions are rolled back
|
|
|
|
$ex = null;
|
|
|
|
$failed = false;
|
|
|
|
try {
|
|
|
|
$conn->withTransaction(
|
|
|
|
function () use (&$obj) {
|
|
|
|
$obj->Title = 'Save 2';
|
|
|
|
$obj->write();
|
|
|
|
throw new \Exception("error");
|
|
|
|
},
|
|
|
|
function () use (&$failed) {
|
|
|
|
$failed = true;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
} catch (\Exception $ex) {
|
|
|
|
}
|
|
|
|
$this->assertTrue($failed);
|
|
|
|
$this->assertEquals('Save 1', TestObject::get()->first()->Title);
|
|
|
|
$this->assertInstanceOf('Exception', $ex);
|
|
|
|
$this->assertEquals('error', $ex->getMessage());
|
|
|
|
}
|
|
|
|
|
2018-02-08 22:28:32 +01:00
|
|
|
public function testNestedTransaction()
|
|
|
|
{
|
2018-10-18 09:19:39 +02:00
|
|
|
if (!DB::get_conn()->supportsSavepoints()) {
|
|
|
|
static::markTestSkipped('Current database does not support savepoints');
|
|
|
|
}
|
|
|
|
|
2018-02-08 22:28:32 +01:00
|
|
|
$this->assertCount(0, TestObject::get());
|
|
|
|
try {
|
|
|
|
DB::get_conn()->withTransaction(function () {
|
|
|
|
$obj = TransactionTest\TestObject::create();
|
|
|
|
$obj->Title = 'Test';
|
|
|
|
$obj->write();
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-02-08 22:28:32 +01:00
|
|
|
$this->assertCount(1, TestObject::get());
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-02-08 22:28:32 +01:00
|
|
|
DB::get_conn()->withTransaction(function () {
|
|
|
|
$obj = TransactionTest\TestObject::create();
|
|
|
|
$obj->Title = 'Test2';
|
|
|
|
$obj->write();
|
|
|
|
$this->assertCount(2, TestObject::get());
|
|
|
|
});
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-02-08 22:28:32 +01:00
|
|
|
throw new \Exception('roll back transaction');
|
|
|
|
});
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
$this->assertEquals('roll back transaction', $e->getMessage());
|
|
|
|
}
|
|
|
|
$this->assertCount(0, TestObject::get());
|
|
|
|
}
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-02-08 22:28:32 +01:00
|
|
|
public function testCreateWithTransaction()
|
|
|
|
{
|
2018-10-04 09:25:53 +02:00
|
|
|
// First/Second in a successful transaction
|
2018-02-08 22:28:32 +01:00
|
|
|
DB::get_conn()->transactionStart();
|
|
|
|
$obj = new TransactionTest\TestObject();
|
|
|
|
$obj->Title = 'First page';
|
|
|
|
$obj->write();
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-02-08 22:28:32 +01:00
|
|
|
$obj = new TransactionTest\TestObject();
|
|
|
|
$obj->Title = 'Second page';
|
|
|
|
$obj->write();
|
2018-10-04 09:25:53 +02:00
|
|
|
DB::get_conn()->transactionEnd();
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-10-04 09:25:53 +02:00
|
|
|
// Third/Fourth in a rolled back transaction
|
|
|
|
DB::get_conn()->transactionStart();
|
2018-02-08 22:28:32 +01:00
|
|
|
$obj = new TransactionTest\TestObject();
|
|
|
|
$obj->Title = 'Third page';
|
|
|
|
$obj->write();
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-02-08 22:28:32 +01:00
|
|
|
$obj = new TransactionTest\TestObject();
|
|
|
|
$obj->Title = 'Fourth page';
|
|
|
|
$obj->write();
|
2018-10-04 09:25:53 +02:00
|
|
|
DB::get_conn()->transactionRollback();
|
2016-12-16 05:34:21 +01:00
|
|
|
|
2018-02-08 22:28:32 +01:00
|
|
|
|
|
|
|
$first = DataObject::get(TransactionTest\TestObject::class, "\"Title\"='First page'");
|
|
|
|
$second = DataObject::get(TransactionTest\TestObject::class, "\"Title\"='Second page'");
|
|
|
|
$third = DataObject::get(TransactionTest\TestObject::class, "\"Title\"='Third page'");
|
|
|
|
$fourth = DataObject::get(TransactionTest\TestObject::class, "\"Title\"='Fourth page'");
|
|
|
|
|
|
|
|
//These pages should be in the system
|
|
|
|
$this->assertTrue(is_object($first) && $first->exists());
|
|
|
|
$this->assertTrue(is_object($second) && $second->exists());
|
|
|
|
|
2018-10-04 09:25:53 +02:00
|
|
|
//These pages should NOT exist, we rolled back
|
2018-02-08 22:28:32 +01:00
|
|
|
$this->assertFalse(is_object($third) && $third->exists());
|
|
|
|
$this->assertFalse(is_object($fourth) && $fourth->exists());
|
2016-12-16 05:34:21 +01:00
|
|
|
}
|
2018-10-18 09:19:39 +02:00
|
|
|
|
|
|
|
public function testReadOnlyTransaction()
|
|
|
|
{
|
|
|
|
if (!DB::get_conn()->supportsTransactions()) {
|
2019-09-16 04:33:20 +02:00
|
|
|
$this->markTestSkipped('Current database doesn\'t support transactions');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!DB::get_conn()->supportsTransactionMode('READ ONLY')) {
|
|
|
|
$this->markTestSkipped('Current database doesn\'t support READ ONLY transactions');
|
2018-10-18 09:19:39 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$page = new TestObject();
|
|
|
|
$page->Title = 'Read only success';
|
|
|
|
$page->write();
|
|
|
|
|
|
|
|
DB::get_conn()->transactionStart('READ ONLY');
|
|
|
|
|
|
|
|
try {
|
|
|
|
$page = new TestObject();
|
|
|
|
$page->Title = 'Read only page failed';
|
|
|
|
$page->write();
|
|
|
|
DB::get_conn()->transactionEnd();
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
//could not write this record
|
|
|
|
//We need to do a rollback or a commit otherwise we'll get error messages
|
|
|
|
DB::get_conn()->transactionRollback();
|
|
|
|
}
|
|
|
|
|
|
|
|
DataObject::flush_and_destroy_cache();
|
|
|
|
|
|
|
|
$success = DataObject::get_one(TestObject::class, "\"Title\"='Read only success'");
|
|
|
|
$fail = DataObject::get_one(TestObject::class, "\"Title\"='Read only page failed'");
|
|
|
|
|
|
|
|
//This page should be in the system
|
2021-10-27 04:39:47 +02:00
|
|
|
$this->assertIsObject($success);
|
2018-10-18 09:19:39 +02:00
|
|
|
$this->assertTrue($success->exists());
|
|
|
|
|
|
|
|
//This page should NOT exist, we had 'read only' permissions
|
|
|
|
$this->assertNull($fail);
|
|
|
|
}
|
2011-03-22 23:49:26 +01:00
|
|
|
}
|