Commit bbc3d3f6 authored by zYne's avatar zYne

Major change on how transactions are being handled: insert / update queries...

Major change on how transactions are being handled: insert / update queries are now executed immediately and only deletes are being gathered (due to delete optimization strategies). Fixes #138, #135
parent cba5846b
......@@ -137,6 +137,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
return $this->dataDict;
}
/**
* getRegexpOperator
* returns the regular expression operator
* (implemented by the connection drivers)
*
......@@ -436,52 +437,54 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
* @return void
*/
public function save(Doctrine_Record $record) {
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record);
switch($record->getState()):
case Doctrine_Record::STATE_TDIRTY:
$this->transaction->addInsert($record);
$this->transaction->insert($record);
break;
case Doctrine_Record::STATE_DIRTY:
case Doctrine_Record::STATE_PROXY:
$this->transaction->addUpdate($record);
$this->transaction->update($record);
break;
case Doctrine_Record::STATE_CLEAN:
case Doctrine_Record::STATE_TCLEAN:
// do nothing
break;
endswitch;
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
}
/**
* saves all related records to $record
*
* @param Doctrine_Record $record
*/
final public function saveRelated(Doctrine_Record $record) {
public function saveRelated(Doctrine_Record $record) {
$saveLater = array();
foreach($record->getReferences() as $k=>$v) {
$fk = $record->getTable()->getRelation($k);
if($fk instanceof Doctrine_Relation_ForeignKey ||
$fk instanceof Doctrine_Relation_LocalKey) {
switch($fk->getType()):
case Doctrine_Relation::ONE_COMPOSITE:
case Doctrine_Relation::MANY_COMPOSITE:
$local = $fk->getLocal();
$foreign = $fk->getForeign();
if($record->getTable()->hasPrimaryKey($fk->getLocal())) {
if( ! $record->exists())
$saveLater[$k] = $fk;
else
$v->save();
} else {
// ONE-TO-ONE relationship
$obj = $record->get($fk->getTable()->getComponentName());
if($obj->getState() != Doctrine_Record::STATE_TCLEAN)
$obj->save();
}
break;
endswitch;
if($fk->isComposite()) {
$local = $fk->getLocal();
$foreign = $fk->getForeign();
if($record->getTable()->hasPrimaryKey($fk->getLocal())) {
if( ! $record->exists())
$saveLater[$k] = $fk;
else
$v->save();
} else {
// ONE-TO-ONE relationship
$obj = $record->get($fk->getTable()->getComponentName());
if($obj->getState() != Doctrine_Record::STATE_TCLEAN)
$obj->save();
}
}
} elseif($fk instanceof Doctrine_Relation_Association) {
$v->save();
}
......@@ -499,7 +502,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
switch($fk->getType()):
case Doctrine_Relation::ONE_COMPOSITE:
case Doctrine_Relation::MANY_COMPOSITE:
$obj = $record->get($record->getTable()->getAlias($fk->getTable()->getComponentName()));
$obj = $record->get($fk->getAlias());
$obj->delete();
break;
endswitch;
......@@ -592,12 +595,17 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
final public function delete(Doctrine_Record $record) {
if( ! $record->exists())
return false;
$this->beginTransaction();
$record->getTable()->getListener()->onPreDelete($record);
$this->deleteComposites($record);
$this->transaction->addDelete($record);
$record->getTable()->getListener()->onDelete($record);
$this->commit();
return true;
}
......
......@@ -46,7 +46,7 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
/**
* @var Doctrine_Connection $conn the connection object
*/
private $connection;
private $conn;
/**
* @see Doctrine_Connection_Transaction::STATE_* constants
* @var boolean $state the current state of the connection
......@@ -110,6 +110,8 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
*/
public function beginTransaction() {
if($this->transaction_level == 0) {
if($this->conn->getAttribute(Doctrine::ATTR_VLD))
$this->validator = new Doctrine_Validator();
if($this->conn->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_PESSIMISTIC) {
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this->conn);
......@@ -129,6 +131,8 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
* if lockmode is short this method starts a transaction
* and commits it instantly
*
* @throws Doctrine_Connection_Transaction_Exception if the transaction fails at PDO level
* @throws Doctrine_Validator_Exception if the transaction fails due to record validations
* @return void
*/
public function commit() {
......@@ -136,7 +140,7 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
$this->transaction_level--;
if($this->transaction_level == 0) {
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionCommit($this->conn);
if($this->conn->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_OPTIMISTIC) {
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this->conn);
......@@ -145,14 +149,8 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
$this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this->conn);
}
if($this->conn->getAttribute(Doctrine::ATTR_VLD))
$this->validator = new Doctrine_Validator();
try {
$this->bulkInsert();
$this->bulkUpdate();
$this->bulkDelete();
} catch(Exception $e) {
......@@ -200,87 +198,6 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionRollback($this->conn);
}
/**
* bulkInsert
* inserts all the objects in the pending insert list into database
*
* @return void
*/
public function bulkInsert() {
if(empty($this->insert))
return false;
foreach($this->insert as $name => $inserts) {
if( ! isset($inserts[0]))
continue;
$record = $inserts[0];
$table = $record->getTable();
$seq = $table->getSequenceName();
$increment = false;
$keys = $table->getPrimaryKeys();
$id = null;
if(count($keys) == 1 && $keys[0] == $table->getIdentifier()) {
$increment = true;
}
foreach($inserts as $k => $record) {
$table->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record);
// listen the onPreInsert event
$table->getAttribute(Doctrine::ATTR_LISTENER)->onPreInsert($record);
$this->insert($record);
if($increment) {
if($k == 0) {
// record uses auto_increment column
$id = $this->conn->getDBH()->lastInsertID();
if( ! $id)
$id = $table->getMaxIdentifier();
}
$record->assignIdentifier($id);
$id++;
} else
$record->assignIdentifier(true);
// listen the onInsert event
$table->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record);
$table->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
}
}
$this->insert = array();
return true;
}
/**
* bulkUpdate
* updates all objects in the pending update list
*
* @return void
*/
public function bulkUpdate() {
foreach($this->update as $name => $updates) {
$ids = array();
foreach($updates as $k => $record) {
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record);
// listen the onPreUpdate event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreUpdate($record);
$this->update($record);
// listen the onUpdate event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onUpdate($record);
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
}
}
$this->update = array();
}
/**
* bulkDelete
* deletes all records from the pending delete list
......@@ -296,16 +213,9 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
$record->assignIdentifier(false);
}
if($record instanceof Doctrine_Record) {
$table = $record->getTable();
$table->getListener()->onPreDelete($record);
$params = substr(str_repeat("?, ",count($ids)),0,-2);
$query = "DELETE FROM ".$record->getTable()->getTableName()." WHERE ".$table->getIdentifier()." IN(".$params.")";
$query = "DELETE FROM ".$record->getTable()->getTableName()." WHERE ".$record->getTable()->getIdentifier()." IN(".$params.")";
$this->conn->execute($query,$ids);
$table->getListener()->onDelete($record);
}
}
......@@ -318,6 +228,8 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
* @return boolean
*/
public function update(Doctrine_Record $record) {
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreUpdate($record);
$array = $record->getPrepared();
if(empty($array))
......@@ -363,6 +275,8 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
$record->assignIdentifier(true);
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onUpdate($record);
return true;
}
/**
......@@ -372,11 +286,20 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
* @return boolean
*/
public function insert(Doctrine_Record $record) {
// listen the onPreInsert event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreInsert($record);
$array = $record->getPrepared();
if(empty($array))
return false;
$table = $record->getTable();
$keys = $table->getPrimaryKeys();
$seq = $record->getTable()->getSequenceName();
if( ! empty($seq)) {
......@@ -398,6 +321,20 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
$stmt = $this->conn->getDBH()->prepare($sql);
$stmt->execute(array_values($array));
if(count($keys) == 1 && $keys[0] == $table->getIdentifier()) {
$id = $this->conn->getDBH()->lastInsertID();
if( ! $id)
$id = $table->getMaxIdentifier();
$record->assignIdentifier($id);
} else
$record->assignIdentifier(true);
// listen the onInsert event
$table->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record);
return true;
}
......
......@@ -811,14 +811,14 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
* @return void
*/
final public function save(Doctrine_Connection $conn = null) {
if ($conn == null) {
if ($conn === null) {
$conn = $this->table->getConnection();
}
$conn->beginTransaction();
$saveLater = $conn->saveRelated($this);
$conn->save($this);
$conn->save($this);
foreach($saveLater as $fk) {
$table = $fk->getTable();
......@@ -890,7 +890,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
}
foreach($this->table->getInheritanceMap() as $k => $v) {
$old = $this->get($k);
$old = $this->get($k, false);
if((string) $old !== (string) $v || $old === null) {
$a[$k] = $v;
......
......@@ -131,6 +131,16 @@ abstract class Doctrine_Relation {
final public function getForeign() {
return $this->foreign;
}
/**
* isComposite
* returns whether or not this relation is a composite relation
*
* @return boolean
*/
final public function isComposite() {
return ($this->type == Doctrine_Relation::ONE_COMPOSITE ||
$this->type == Doctrine_Relation::MANY_COMPOSITE);
}
/**
* isOneToOne
* returns whether or not this relation is a one-to-one relation
......
......@@ -165,24 +165,28 @@ class Doctrine_EventListenerTestCase extends Doctrine_UnitTestCase {
$this->logger->clear();
$e->save();
$this->assertEqual($this->logger->pop(), 'onTransactionCommit');
$this->assertEqual($this->logger->pop(), 'onPreTransactionCommit');
$this->assertEqual($this->logger->pop(), 'onSave');
$this->assertEqual($this->logger->pop(), 'onInsert');
$this->assertEqual($this->logger->pop(), 'onPreInsert');
$this->assertEqual($this->logger->pop(), 'onPreSave');
$this->assertEqual($this->logger->pop(), 'onTransactionBegin');
$this->assertEqual($this->logger->pop(), 'onPreTransactionBegin');
$e->name = "test 1";
$e->save();
$this->assertEqual($this->logger->pop(), 'onTransactionCommit');
$this->assertEqual($this->logger->pop(), 'onPreTransactionCommit');
$this->assertEqual($this->logger->pop(), 'onSave');
$this->assertEqual($this->logger->pop(), 'onUpdate');
$this->assertEqual($this->logger->pop(), 'onPreUpdate');
$this->assertEqual($this->logger->pop(), 'onPreSave');
$this->assertEqual($this->logger->pop(), 'onTransactionBegin');
$this->assertEqual($this->logger->pop(), 'onPreTransactionBegin');
......@@ -191,7 +195,9 @@ class Doctrine_EventListenerTestCase extends Doctrine_UnitTestCase {
$e->delete();
$this->assertEqual($this->logger->pop(), 'onTransactionCommit');
$this->assertEqual($this->logger->pop(), 'onPreTransactionCommit');
$this->assertEqual($this->logger->pop(), 'onDelete');
$this->assertEqual($this->logger->pop(), 'onPreDelete');
$this->assertEqual($this->logger->pop(), 'onTransactionBegin');
$this->assertEqual($this->logger->pop(), 'onPreTransactionBegin');
......
......@@ -9,17 +9,19 @@ class Doctrine_RecordTestCase extends Doctrine_UnitTestCase {
$this->tables[] = "GzipTest";
parent::prepareTables();
}
public function testIssetForPrimaryKey() {
$this->assertTrue(isset($this->users[0]->id));
$this->assertTrue(isset($this->users[0]['id']));
$this->assertTrue($this->users[0]->contains('id'));
$user = new User();
$this->assertFalse(isset($user->id));
$this->assertFalse(isset($user['id']));
$this->assertFalse($user->contains('id'));
}
public function testNotNullConstraint() {
$null = new NotNullTest();
......@@ -31,9 +33,11 @@ class Doctrine_RecordTestCase extends Doctrine_UnitTestCase {
$this->fail();
} catch(Doctrine_Exception $e) {
$this->pass();
$this->connection->rollback();
}
}
public function testGzipType() {
$gzip = new GzipTest();
$gzip->gzip = "compressed";
......@@ -263,7 +267,7 @@ class Doctrine_RecordTestCase extends Doctrine_UnitTestCase {
$this->assertEqual($user->name, null);
}
public function testDateTimeType() {
$date = new DateTest();
......@@ -617,17 +621,12 @@ class Doctrine_RecordTestCase extends Doctrine_UnitTestCase {
$user = new User();
$user->name = "John Locke";
$user->save();
$this->assertTrue($user->getModified() == array());
$this->assertTrue($user->getState() == Doctrine_Record::STATE_CLEAN);
$debug = $this->listener->getMessages();
$p = array_pop($debug);
$this->assertTrue($p->getObject() instanceof Doctrine_Connection);
$this->assertTrue($p->getCode() == Doctrine_EventListener_Debugger::EVENT_COMMIT);
$user->delete();
$this->assertTrue($user->getState() == Doctrine_Record::STATE_TCLEAN);
$this->assertEqual($user->getState(), Doctrine_Record::STATE_TCLEAN);
}
public function testUpdate() {
......@@ -662,13 +661,12 @@ class Doctrine_RecordTestCase extends Doctrine_UnitTestCase {
$user->Phonenumber = $coll;
$this->assertEqual($user->Phonenumber->count(), 0);
$user->save();
$user->getTable()->clear();
$user = $this->objTable->find(5);
$this->assertEqual($user->Phonenumber->count(), 0);
$this->assertEqual(get_class($user->Phonenumber), "Doctrine_Collection_Immediate");
$user->Phonenumber[0]->phonenumber;
......
......@@ -66,6 +66,7 @@ class Doctrine_UnitTestCase extends UnitTestCase {
$this->dbh = $this->connection->getDBH();
$this->listener = $this->manager->getAttribute(Doctrine::ATTR_LISTENER);
$this->manager->setAttribute(Doctrine::ATTR_LISTENER, $this->listener);
} else {
//$this->dbh = Doctrine_DB::getConnection();
$this->dbh = Doctrine_DB::getConn("sqlite::memory:");
......@@ -74,6 +75,7 @@ class Doctrine_UnitTestCase extends UnitTestCase {
$this->listener = new Doctrine_EventListener_Debugger();
$this->manager->setAttribute(Doctrine::ATTR_LISTENER, $this->listener);
}
$this->connection->setListener(new Doctrine_EventListener());
$this->query = new Doctrine_Query($this->connection);
$this->prepareTables();
$this->prepareData();
......
......@@ -5,6 +5,7 @@ ob_start();
require_once("ConfigurableTestCase.php");
require_once("ManagerTestCase.php");
require_once("ConnectionTestCase.php");
require_once("ConnectionTransactionTestCase.php");
require_once("TableTestCase.php");
require_once("EventListenerTestCase.php");
require_once("BatchIteratorTestCase.php");
......@@ -33,21 +34,23 @@ require_once("BooleanTestCase.php");
require_once("EnumTestCase.php");
require_once("RelationAccessTestCase.php");
require_once("DataDictSqliteTestCase.php");
error_reporting(E_ALL);
$test = new GroupTest("Doctrine Framework Unit Tests");
$test->addTestCase(new Doctrine_EventListenerTestCase());
$test->addTestCase(new Doctrine_RecordTestCase());
$test->addTestCase(new Doctrine_Connection_Transaction_TestCase());
$test->addTestCase(new Doctrine_ConnectionTestCase());
$test->addTestCase(new Doctrine_DB_TestCase());
$test->addTestCase(new Doctrine_RecordTestCase());
$test->addTestCase(new Doctrine_AccessTestCase());
$test->addTestCase(new Doctrine_EventListenerTestCase());
$test->addTestCase(new Doctrine_TableTestCase());
$test->addTestCase(new Doctrine_ManagerTestCase());
......@@ -84,18 +87,18 @@ $test->addTestCase(new Doctrine_CollectionTestCase());
$test->addTestCase(new Doctrine_Query_ReferenceModel_TestCase());
$test->addTestCase(new Doctrine_QueryTestCase());
$test->addTestCase(new Doctrine_EnumTestCase());
$test->addTestCase(new Doctrine_RelationAccessTestCase());
$test->addTestCase(new Doctrine_EventListener_Chain_TestCase());
$test->addTestCase(new Doctrine_DataDict_Sqlite_TestCase());
$test->addTestCase(new Doctrine_BooleanTestCase());
$test->addTestCase(new Doctrine_QueryTestCase());
$test->addTestCase(new Doctrine_EventListener_Chain_TestCase());
//$test->addTestCase(new Doctrine_Cache_FileTestCase());
//$test->addTestCase(new Doctrine_Cache_SqliteTestCase());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment