Commit c435286e authored by doctrine's avatar doctrine

Mysql bulk insert support => huge performance increase for mysql session

parent 81160cb9
...@@ -725,7 +725,7 @@ class Doctrine_DQL_Parser { ...@@ -725,7 +725,7 @@ class Doctrine_DQL_Parser {
$reference = implode(".",$a); $reference = implode(".",$a);
$objTable = $this->session->getTable(end($a)); $objTable = $this->session->getTable(end($a));
$where = $objTable->getTableName().".".$field." ".$operator." ".$value; $where = $objTable->getTableName().".".$field." ".$operator." ".$value;
if(count($a) > 1 AND isset($a[1])) { if(count($a) > 1 && isset($a[1])) {
$root = $a[0]; $root = $a[0];
$fk = $this->tnames[$root]->getForeignKey($a[1]); $fk = $this->tnames[$root]->getForeignKey($a[1]);
if($fk instanceof Doctrine_Association) { if($fk instanceof Doctrine_Association) {
......
...@@ -29,43 +29,45 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab ...@@ -29,43 +29,45 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab
* @var $dbh the database handle * @var $dbh the database handle
*/ */
private $dbh; private $dbh;
/**
* @see Doctrine_Session::STATE_* constants
* @var boolean $state the current state of the session
*/
private $state = 0;
/**
* @var integer $transaction_level the nesting level of transactions, used by transaction methods
*/
private $transaction_level = 0;
/**
* @var PDO $cacheHandler
*/
private $cacheHandler;
/** /**
* @var array $tables an array containing all the initialized Doctrine_Table objects * @var array $tables an array containing all the initialized Doctrine_Table objects
* keys representing Doctrine_Table component names and values as Doctrine_Table objects * keys representing Doctrine_Table component names and values as Doctrine_Table objects
*/ */
private $tables = array(); protected $tables = array();
/** /**
* @see Doctrine_Session::STATE_* constants * @var Doctrine_Validator $validator transaction validator
* @var boolean $state the current state of the session
*/ */
private $state = 0; protected $validator;
/** /**
* @var array $update two dimensional pending update list, the records in * @var array $update two dimensional pending update list, the records in
* this list will be updated when transaction is committed * this list will be updated when transaction is committed
*/ */
private $update = array(array()); protected $update = array();
/** /**
* @var array $insert two dimensional pending insert list, the records in * @var array $insert two dimensional pending insert list, the records in
* this list will be inserted when transaction is committed * this list will be inserted when transaction is committed
*/ */
private $insert = array(array()); protected $insert = array();
/** /**
* @var array $delete two dimensional pending delete list, the records in * @var array $delete two dimensional pending delete list, the records in
* this list will be deleted when transaction is committed * this list will be deleted when transaction is committed
*/ */
private $delete = array(array()); protected $delete = array();
/**
* @var integer $transaction_level the nesting level of transactions, used by transaction methods
*/
private $transaction_level = 0;
/**
* @var Doctrine_Validator $validator transaction validator
*/
private $validator;
/**
* @var PDO $cacheHandler
*/
private $cacheHandler;
...@@ -410,6 +412,9 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab ...@@ -410,6 +412,9 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab
* @return void * @return void
*/ */
public function bulkInsert() { public function bulkInsert() {
if(empty($this->insert))
return false;
foreach($this->insert as $name => $inserts) { foreach($this->insert as $name => $inserts) {
if( ! isset($inserts[0])) if( ! isset($inserts[0]))
continue; continue;
...@@ -443,6 +448,7 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab ...@@ -443,6 +448,7 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab
$id++; $id++;
} }
$this->insert($record,$id); $this->insert($record,$id);
// listen the onInsert event // listen the onInsert event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record); $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record);
...@@ -450,7 +456,34 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab ...@@ -450,7 +456,34 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record); $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
} }
} }
$this->insert = array(array()); $this->insert = array();
return true;
}
/**
* returns maximum identifier values
*
* @param array $names an array of component names
* @return array
*/
public function getMaximumValues(array $names) {
$values = array();
foreach($names as $name) {
$table = $this->tables[$name];
$keys = $table->getPrimaryKeys();
$tablename = $table->getTableName();
if(count($keys) == 1 && $keys[0] == "id") {
// record uses auto_increment column
$sql = "SELECT MAX(id) FROM ".$tablename;
$stmt = $this->dbh->query($sql);
$data = $stmt->fetch(PDO::FETCH_NUM);
$values[$tablename] = $data[0];
$stmt->closeCursor();
}
}
return $values;
} }
/** /**
* bulkUpdate * bulkUpdate
...@@ -477,7 +510,7 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab ...@@ -477,7 +510,7 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab
if(isset($record)) if(isset($record))
$record->getTable()->getCache()->deleteMultiple($ids); $record->getTable()->getCache()->deleteMultiple($ids);
} }
$this->update = array(array()); $this->update = array();
} }
/** /**
* bulkDelete * bulkDelete
...@@ -499,7 +532,7 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab ...@@ -499,7 +532,7 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab
$record->getTable()->getCache()->deleteMultiple($ids); $record->getTable()->getCache()->deleteMultiple($ids);
} }
} }
$this->delete = array(array()); $this->delete = array();
} }
...@@ -532,9 +565,7 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab ...@@ -532,9 +565,7 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab
* @param Doctrine_Record $record * @param Doctrine_Record $record
* @return void * @return void
*/ */
public function save(Doctrine_Record $record) { public function save(Doctrine_Record $record) {
switch($record->getState()): switch($record->getState()):
case Doctrine_Record::STATE_TDIRTY: case Doctrine_Record::STATE_TDIRTY:
$this->addInsert($record); $this->addInsert($record);
...@@ -741,6 +772,25 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab ...@@ -741,6 +772,25 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab
$name = $record->getTable()->getComponentName(); $name = $record->getTable()->getComponentName();
$this->delete[$name][] = $record; $this->delete[$name][] = $record;
} }
/**
* @return array
*/
public function getInserts() {
return $this->insert;
}
/**
* @return array
*/
public function getUpdates() {
return $this->update;
}
/**
* @return array
*/
public function getDeletes() {
return $this->delete;
}
/** /**
* returns a string representation of this object * returns a string representation of this object
* @return string * @return string
......
...@@ -34,5 +34,131 @@ class Doctrine_Session_Mysql extends Doctrine_Session_Common { ...@@ -34,5 +34,131 @@ class Doctrine_Session_Mysql extends Doctrine_Session_Common {
return $ids; return $ids;
} }
*/ */
/**
* returns maximum identifier values
*
* @param array $names an array of component names
* @return array
*/
public function getMaximumValues2(array $names) {
$values = array();
foreach($names as $name) {
$table = $this->tables[$name];
$keys = $table->getPrimaryKeys();
$tablename = $table->getTableName();
if(count($keys) == 1 && $keys[0] == "id") {
// record uses auto_increment column
$sql[] = "SELECT MAX(".$tablename.".id) as $tablename FROM ".$tablename;
$values[$tablename] = 0;
$array[] = $tablename;
}
}
$sql = implode(" UNION ",$sql);
$stmt = $this->getDBH()->query($sql);
$data = $stmt->fetchAll(PDO::FETCH_NUM);
foreach($data as $k => $v) {
$values[$array[$k]] = $v[0];
}
return $values;
}
/**
* bulkInsert
* inserts all the objects in the pending insert list into database
*
* @return boolean
*/
public function bulkInsert() {
if(empty($this->insert))
return false;
$values = $this->getMaximumValues(array_keys($this->insert));
foreach($this->insert as $name => $inserts) {
if( ! isset($inserts[0]))
continue;
$record = $inserts[0];
$table = $record->getTable();
$seq = $table->getSequenceName();
$increment = false;
$id = null;
$keys = $table->getPrimaryKeys();
if(count($keys) == 1 && $keys[0] == "id") {
// record uses auto_increment column
$sql = "SELECT MAX(id) FROM ".$record->getTable()->getTableName();
$stmt = $this->getDBH()->query($sql);
$data = $stmt->fetch(PDO::FETCH_NUM);
$id = $data[0];
$stmt->closeCursor();
$increment = true;
}
$marks = array();
$params = array();
foreach($inserts as $k => $record) {
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record);
// listen the onPreInsert event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreInsert($record);
if($increment) {
// record uses auto_increment column
$id++;
}
$array = $record->getModified();
foreach($record->getTable()->getInheritanceMap() as $k=>$v):
$array[$k] = $v;
endforeach;
foreach($array as $k => $value) {
if($value instanceof Doctrine_Record) {
$array[$k] = $value->getID();
$record->set($k,$value->getID());
}
}
if(isset($this->validator)) {
if( ! $this->validator->validateRecord($record)) {
continue;
}
}
$key = implode(", ",array_keys($array));
if( ! isset($params[$key]))
$params[$key] = array();
$marks[$key][] = "(".substr(str_repeat("?, ",count($array)),0,-2).")";
$params[$key] = array_merge($params[$key], array_values($array));
$record->setID($id);
// listen the onInsert event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record);
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
}
if( ! empty($marks)) {
foreach($marks as $key => $list) {
$query = "INSERT INTO ".$table->getTableName()." (".$key.") VALUES ".implode(", ", $list);
$stmt = $this->getDBH()->prepare($query);
$stmt->execute($params[$key]);
}
}
}
$this->insert = array();
return true;
}
} }
?> ?>
...@@ -58,7 +58,6 @@ class Doctrine_UnitTestCase extends UnitTestCase { ...@@ -58,7 +58,6 @@ class Doctrine_UnitTestCase extends UnitTestCase {
foreach($tables as $name) { foreach($tables as $name) {
$table = $this->session->getTable($name); $table = $this->session->getTable($name);
$table->getCache()->deleteAll(); $table->getCache()->deleteAll();
} }
...@@ -73,14 +72,13 @@ class Doctrine_UnitTestCase extends UnitTestCase { ...@@ -73,14 +72,13 @@ class Doctrine_UnitTestCase extends UnitTestCase {
$groups = new Doctrine_Collection($this->session->getTable("Group")); $groups = new Doctrine_Collection($this->session->getTable("Group"));
$groups[0]->name = "Drama Actors"; $groups[0]->name = "Drama Actors";
$groups[0]->save();
$groups[1]->name = "Quality Actors"; $groups[1]->name = "Quality Actors";
$groups[1]->save();
$groups[2]->name = "Action Actors"; $groups[2]->name = "Action Actors";
$groups[2]["Phonenumber"][0]->phonenumber = "123 123"; $groups[2]["Phonenumber"][0]->phonenumber = "123 123";
$groups[2]->save(); $groups->save();
$users = new Doctrine_Collection($this->session->getTable("User")); $users = new Doctrine_Collection($this->session->getTable("User"));
......
...@@ -23,8 +23,9 @@ error_reporting(E_ALL); ...@@ -23,8 +23,9 @@ error_reporting(E_ALL);
$test = new GroupTest("Doctrine Framework Unit Tests"); $test = new GroupTest("Doctrine Framework Unit Tests");
$test->addTestCase(new Doctrine_SessionTestCase()); $test->addTestCase(new Doctrine_SessionTestCase());
$test->addTestCase(new Doctrine_RecordTestCase()); $test->addTestCase(new Doctrine_RecordTestCase());
...@@ -42,7 +43,6 @@ $test->addTestCase(new Doctrine_EventListenerTestCase()); ...@@ -42,7 +43,6 @@ $test->addTestCase(new Doctrine_EventListenerTestCase());
$test->addTestCase(new Doctrine_BatchIteratorTestCase()); $test->addTestCase(new Doctrine_BatchIteratorTestCase());
$test->addTestCase(new Doctrine_DQL_ParserTestCase()); $test->addTestCase(new Doctrine_DQL_ParserTestCase());
$test->addTestCase(new Doctrine_ConfigurableTestCase()); $test->addTestCase(new Doctrine_ConfigurableTestCase());
......
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