diff --git a/classes/Cache.class.php b/classes/Cache.class.php index aa1edda83..d11d8626c 100644 --- a/classes/Cache.class.php +++ b/classes/Cache.class.php @@ -37,6 +37,14 @@ class Doctrine_Cache implements iDoctrine_Cache { public function fetch($id) { throw new InvalidKeyException(); } + /** + * implemented by child classes + * @param array $keys + * @return boolean + */ + public function fetchMultiple($keys) { + return false; + } /** * implemented by child classes * @param integer $id @@ -48,7 +56,7 @@ class Doctrine_Cache implements iDoctrine_Cache { /** * implemented by child classes */ - public function deleteMultiple() { + public function deleteMultiple($keys) { return 0; } /** diff --git a/classes/Cache/Sqlite.class.php b/classes/Cache/Sqlite.class.php index 308d57e5b..0c815fcf6 100644 --- a/classes/Cache/Sqlite.class.php +++ b/classes/Cache/Sqlite.class.php @@ -16,7 +16,7 @@ class Doctrine_Cache_Sqlite { * INSERT constant * used as a base for SQL INSERT queries */ - const INSERT = "INSERT INTO %s (id, object) VALUES (?, ?)"; + const INSERT = "REPLACE INTO %s (id, object) VALUES (?, ?)"; /** * DELETE constant * used as a base for SQL DELETE queries @@ -43,14 +43,11 @@ class Doctrine_Cache_Sqlite { if( ! is_dir($dir)) mkdir($dir, 0777); - $this->path = $dir.DIRECTORY_SEPARATOR; - - $this->dbh = new PDO("sqlite:".$this->path."data.cache"); - $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $this->path = $dir.DIRECTORY_SEPARATOR; + $this->dbh = $this->table->getSession()->getCacheHandler(); try { - $this->dbh->query("CREATE TABLE ".$this->table->getTableName()." (id INTEGER, object TEXT)"); + $this->dbh->query("CREATE TABLE ".$this->table->getTableName()." (id INTEGER UNIQUE, object TEXT)"); } catch(PDOException $e) { } @@ -76,11 +73,12 @@ class Doctrine_Cache_Sqlite { $stmt = $this->dbh->query(sprintf(self::INSERT,$this->table->getTableName())); $stmt->execute(array($id, serialize($clone))); + return true; } /** * fetches a Doctrine_Record from the cache - * @param integer $id + * @param mixed $id * @return mixed false on failure, Doctrine_Record on success */ public function fetch($id) { @@ -88,7 +86,7 @@ class Doctrine_Cache_Sqlite { $stmt->execute(array($id)); $data = $stmt->fetch(PDO::FETCH_NUM); - if($data === false) + if($data === false) throw new InvalidKeyException(); $this->fetched[] = $id; @@ -109,8 +107,10 @@ class Doctrine_Cache_Sqlite { */ public function fetchMultiple(array $keys) { $count = (count($keys)-1); + $keys = array_values($keys); $sql = sprintf(self::SELECT,$this->table->getTableName(),"IN (".str_repeat("?, ",$count)."?)"); $stmt = $this->dbh->query($sql); + $stmt->execute($keys); while($data = $stmt->fetch(PDO::FETCH_NUM)) { @@ -133,6 +133,7 @@ class Doctrine_Cache_Sqlite { return $stmt->rowCount(); } /** + * @param mixed $id * @return void */ public function delete($id) { @@ -163,6 +164,7 @@ class Doctrine_Cache_Sqlite { if(empty($keys)) return 0; + $keys = array_values($keys); $count = (count($keys)-1); $sql = sprintf(self::DELETE,$this->table->getTableName(),"IN (".str_repeat("?, ",$count)."?)"); $stmt = $this->dbh->query($sql); @@ -245,7 +247,12 @@ class Doctrine_Cache_Sqlite { } return false; } - + /** + * @param mixed $id + */ + public function addDelete($id) { + $this->delete[] = $id; + } /** * destructor * the purpose of this destructor is to save all the fetched diff --git a/classes/Collection/Batch.class.php b/classes/Collection/Batch.class.php index d61977294..07250a11d 100644 --- a/classes/Collection/Batch.class.php +++ b/classes/Collection/Batch.class.php @@ -83,18 +83,8 @@ class Doctrine_Collection_Batch extends Doctrine_Collection { elseif(is_array($this->data[$i])) $id = $this->data[$i]["id"]; - $load = false; - // check the cache - // no need of fetching the same data twice - try { - $record = $this->table->getCache()->fetch($id); - } catch(InvalidKeyException $ex) { - $load = true; - } - - if($load) - $a[] = $id; + $a[$i] = $id; endfor; $c = count($a); @@ -104,20 +94,21 @@ class Doctrine_Collection_Batch extends Doctrine_Collection { $query .= substr(str_repeat("?, ",count($a)),0,-2); $query .= ($c > 1)?")":""; - $stmt = $this->table->getSession()->execute($query,$a); - + $stmt = $this->table->getSession()->execute($query,array_values($a)); - while($row = $stmt->fetch(PDO::FETCH_ASSOC)): + foreach($a as $k => $id) { + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if($row === false) + break; $this->table->setData($row); - if(is_object($this->data[$e])) { - $this->data[$e]->factoryRefresh($this->table); + if(is_object($this->data[$k])) { + $this->data[$k]->factoryRefresh($this->table); } else { - $this->data[$e] = $this->table->getRecord(); + $this->data[$k] = $this->table->getRecord(); } - $e++; - endwhile; + } $this->loaded[$x] = true; return true; @@ -134,20 +125,9 @@ class Doctrine_Collection_Batch extends Doctrine_Collection { if(isset($this->data[$key])) { switch(gettype($this->data[$key])): case "array": - try { - - // try to fetch the Doctrine_Record from cache - if( ! isset($this->data[$key]["id"])) - throw new InvalidKeyException(); - - $this->data[$key] = $this->table->getCache()->fetch($this->data[$key]["id"]); - - } catch(InvalidKeyException $e) { - - // Doctrine_Record didn't exist in cache - $this->table->setData($this->data[$key]); - $this->data[$key] = $this->table->getProxy(); - } + // Doctrine_Record didn't exist in cache + $this->table->setData($this->data[$key]); + $this->data[$key] = $this->table->getProxy(); $this->data[$key]->addCollection($this); break; diff --git a/classes/DB.class.php b/classes/DB.class.php index 1f6846f44..df5305d3a 100644 --- a/classes/DB.class.php +++ b/classes/DB.class.php @@ -24,26 +24,40 @@ class Doctrine_DB extends PDO implements Countable, IteratorAggregate { $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array("Doctrine_DBStatement",array($this))); } + + + public static function getConn($dsn,$username = null, $password = null) { + static $instance; + + if( ! isset($instance)) { + $instance = new Doctrine_DB($dsn,$username,$password); + } + return $instance; + } + /** * @param string $dsn PEAR::DB like DSN * format: schema://user:password@address/dbname */ public static function getConnection($dsn = null) { - static $instance; - if( ! isset($instance)) { + static $instance = array(); + $md5 = md5($dsn); + + if( ! isset($instance[$md5])) { if( ! isset($dsn)) { $a = parse_url(self::DSN); } else { $a = parse_url($dsn); } $e = array(); + $e[0] = $a["scheme"].":host=".$a["host"].";dbname=".substr($a["path"],1); $e[1] = $a["user"]; $e[2] = $a["pass"]; - - $instance = new Doctrine_DB($e[0],$e[1],$e[2]); + + $instance[$md5] = new Doctrine_DB($e[0],$e[1],$e[2]); } - return $instance; + return $instance[$md5]; } /** * @param string $query query to be executed diff --git a/classes/Record.class.php b/classes/Record.class.php index 0ae7d4ea0..e943976cf 100644 --- a/classes/Record.class.php +++ b/classes/Record.class.php @@ -173,8 +173,6 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite unset($this->data['id']); $this->table->setData(array()); - - $this->table->getCache()->store($this); } } /** @@ -331,7 +329,6 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $this->loaded = true; $this->state = Doctrine_Record::STATE_CLEAN; - $this->getTable()->getCache()->store($this); return true; } /** @@ -345,7 +342,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite if($this->id != $data["id"]) throw new Doctrine_Refresh_Exception(); - $this->data = $data; + $this->data = $data; $this->cleanData(); @@ -353,8 +350,6 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $this->state = Doctrine_Record::STATE_CLEAN; $this->modified = array(); $this->loaded = true; - - $this->getTable()->getCache()->store($this); } /** * return the factory that created this data access object diff --git a/classes/Session.class.php b/classes/Session.class.php index 5765e66ac..5d75ab1d9 100644 --- a/classes/Session.class.php +++ b/classes/Session.class.php @@ -60,6 +60,10 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab private $transaction_level = 0; private $validator; + /** + * @var PDO $cacheHandler + */ + private $cacheHandler; @@ -75,10 +79,25 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab $this->state = Doctrine_Session::STATE_OPEN; $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); - $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + switch($this->getAttribute(Doctrine::ATTR_CACHE)): + case Doctrine::CACHE_SQLITE: + $dir = $this->getAttribute(Doctrine::ATTR_CACHE_DIR).DIRECTORY_SEPARATOR; + $dsn = "sqlite:".$dir."data.cache"; + + $this->cacheHandler = Doctrine_DB::getConn($dsn); + $this->cacheHandler->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->cacheHandler->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + break; + endswitch; $this->getAttribute(Doctrine::ATTR_LISTENER)->onOpen($this); } + + public function getCacheHandler() { + return $this->cacheHandler; + } /** * @return integer the session state */ @@ -398,7 +417,7 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab */ public function bulkInsert() { foreach($this->insert as $name => $inserts) { - if( ! isset($inserts[0])) + if( ! isset($inserts[0])) continue; $record = $inserts[0]; @@ -435,8 +454,6 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record); $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record); - - $record->getTable()->getCache()->store($record); } } $this->insert = array(array()); @@ -448,6 +465,8 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab */ 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 @@ -456,9 +475,13 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab $this->update($record); // listen the onUpdate event $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onUpdate($record); - + $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record); + + $ids[] = $record->getID(); } + if(isset($record)) + $record->getTable()->getCache()->deleteMultiple($ids); } $this->update = array(array()); } @@ -611,7 +634,6 @@ abstract class Doctrine_Session extends Doctrine_Configurable implements Countab $record->setID($record->getID()); - $record->getTable()->getCache()->delete($record->getID()); return true; } /** diff --git a/classes/Table.class.php b/classes/Table.class.php index 30b76e810..087bad093 100644 --- a/classes/Table.class.php +++ b/classes/Table.class.php @@ -442,14 +442,6 @@ class Doctrine_Table extends Doctrine_Configurable { */ final public function find($id = null) { if($id !== null) { - try { - // try to get from cache - $record = $this->cache->fetch($id); - return $record; - } catch(InvalidKeyException $e) { - // do nothing - } - $query = $this->query." WHERE ".implode(" = ? && ",$this->primaryKeys)." = ?"; $query = $this->applyInheritance($query); @@ -510,14 +502,6 @@ class Doctrine_Table extends Doctrine_Configurable { */ final public function getProxy($id = null) { if($id !== null) { - $id = (int) $id; - try { - // try to get from cache - $record = $this->cache->fetch($id); - return $record; - } catch(InvalidKeyException $e) { - // do nothing - } $query = "SELECT ".implode(", ",$this->primaryKeys)." FROM ".$this->getTableName()." WHERE ".implode(" = ? && ",$this->primaryKeys)." = ?"; $query = $this->applyInheritance($query); @@ -558,11 +542,7 @@ class Doctrine_Table extends Doctrine_Configurable { foreach($data as $row) { $this->data = $row; - try { - $record = $this->getCache()->fetch($this->data["id"]); - } catch(InvalidKeyException $e) { - $record = $this->getRecord(); - } + $record = $this->getRecord(); $coll->add($record); } return $coll; diff --git a/tests/CacheSqliteTestCase.class.php b/tests/CacheSqliteTestCase.class.php index 51d475b29..df164fca7 100644 --- a/tests/CacheSqliteTestCase.class.php +++ b/tests/CacheSqliteTestCase.class.php @@ -12,7 +12,7 @@ class Doctrine_Cache_SqliteTestCase extends Doctrine_UnitTestCase { $this->cache = new Doctrine_Cache_Sqlite($this->objTable); $this->cache->deleteAll(); } - /** + public function testStore() { // does not store proxy objects $this->assertFalse($this->cache->store($this->objTable->getProxy(4))); @@ -104,8 +104,6 @@ class Doctrine_Cache_SqliteTestCase extends Doctrine_UnitTestCase { $this->manager->setAttribute(Doctrine::ATTR_CACHE_SIZE, 3); $this->assertEqual($this->cache->clean(), 3); - } - */ } ?> diff --git a/tests/RecordTestCase.class.php b/tests/RecordTestCase.class.php index d099e17e0..b64f4f399 100644 --- a/tests/RecordTestCase.class.php +++ b/tests/RecordTestCase.class.php @@ -161,26 +161,6 @@ class Doctrine_RecordTestCase extends Doctrine_UnitTestCase { $this->assertTrue($p->getObject() instanceof Doctrine_Session); $this->assertTrue($p->getCode() == Doctrine_Debugger::EVENT_COMMIT); - if($this->manager->getAttribute(Doctrine::ATTR_CACHE) !== Doctrine::CACHE_NONE) { - $p = array_pop($debug); - - $this->assertTrue($p->getObject() instanceof Doctrine_Record); - $this->assertTrue($p->getCode() == Doctrine_Debugger::EVENT_SLEEP); - } - - $p = array_pop($debug); - $this->assertTrue($p->getObject() instanceof Doctrine_Record); - $this->assertTrue($p->getCode() == Doctrine_Debugger::EVENT_SAVE); - - $p = array_pop($debug); - $this->assertTrue($p->getObject() instanceof Doctrine_Record); - $this->assertTrue($p->getCode() == Doctrine_Debugger::EVENT_INSERT); - - - $p = array_pop($debug); - $this->assertTrue($p->getObject() instanceof Doctrine_Record); - $this->assertTrue($p->getCode() == Doctrine_Debugger::EVENT_PREINSERT); - $this->new->delete(); $this->assertTrue($this->new->getState() == Doctrine_Record::STATE_TCLEAN); } diff --git a/tests/UnitTestCase.class.php b/tests/UnitTestCase.class.php index 825872128..bcc3fe7b5 100644 --- a/tests/UnitTestCase.class.php +++ b/tests/UnitTestCase.class.php @@ -61,6 +61,7 @@ class Doctrine_UnitTestCase extends UnitTestCase { foreach($tables as $name) { $table = $this->session->getTable($name); + $table->getCache()->deleteAll(); } @@ -128,6 +129,9 @@ class Doctrine_UnitTestCase extends UnitTestCase { $this->users = $users; $this->session->flush(); } + public function getSession() { + return $this->session; + } public function clearCache() { foreach($this->tables as $name) { $table = $this->session->getTable($name); diff --git a/tests/run.php b/tests/run.php index 72617b874..95c50f979 100644 --- a/tests/run.php +++ b/tests/run.php @@ -36,13 +36,14 @@ $test->addTestCase(new Doctrine_AccessTestCase()); $test->addTestCase(new Doctrine_ConfigurableTestCase()); $test->addTestCase(new Doctrine_EventListenerTestCase()); + $test->addTestCase(new Doctrine_DQL_ParserTestCase()); $test->addTestCase(new Doctrine_BatchIteratorTestCase()); -/** -$test->addTestCase(new Doctrine_Cache_FileTestCase()); -$test->addTestCase(new Doctrine_Cache_SqliteTestCase()); -*/ + +//$test->addTestCase(new Doctrine_Cache_FileTestCase()); +//$test->addTestCase(new Doctrine_Cache_SqliteTestCase()); + @@ -51,6 +52,16 @@ $test->addTestCase(new Doctrine_Cache_SqliteTestCase()); $test->run(new HtmlReporter()); +$cache = Doctrine_Manager::getInstance()->getCurrentSession()->getCacheHandler(); +if(isset($cache)) { + $a = $cache->getQueries(); + print "Executed cache queries: ".count($a)."\n"; + /** + foreach($a as $query) { + print $query."\n"; + } + */ +} $dbh = Doctrine_Manager::getInstance()->getCurrentSession()->getDBH(); $a = $dbh->getQueries(); @@ -58,7 +69,6 @@ $a = $dbh->getQueries(); print "Executed queries: ".count($a)."\n"; foreach($a as $query) { - $e = explode(" ",$query); print $query."\n"; }