diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index 8cdf93395..8d637d69b 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -63,6 +63,9 @@ class Selection extends Nette\Object implements \Iterator, IRowContainer, \Array /** @var string */ protected $generalCacheKey; + /** @var array */ + protected $generalCacheTraceKey; + /** @var string */ protected $specificCacheKey; @@ -536,6 +539,11 @@ protected function emptyResultSet($saveCache = TRUE) $this->saveCacheState(); } + if ($saveCache) { + // null only if missing some column + $this->generalCacheTraceKey = NULL; + } + $this->rows = NULL; $this->specificCacheKey = NULL; $this->generalCacheKey = NULL; @@ -590,7 +598,17 @@ protected function getGeneralCacheKey() return $this->generalCacheKey; } - return $this->generalCacheKey = md5(serialize(array(__CLASS__, $this->name, $this->sqlBuilder->getConditions()))); + $key = array(__CLASS__, $this->name, $this->sqlBuilder->getConditions()); + if (!$this->generalCacheTraceKey) { + $trace = array(); + foreach (debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : FALSE) as $item) { + $trace[] = isset($item['file'], $item['line']) ? $item['file'] . $item['line'] : NULL; + }; + $this->generalCacheTraceKey = $trace; + } + + $key[] = $this->generalCacheTraceKey; + return $this->generalCacheKey = md5(serialize($key)); } @@ -714,7 +732,7 @@ public function insert($data) $primarySequenceName = $this->getPrimarySequence(); $primaryKey = $this->context->getInsertId( - !empty($primarySequenceName) + !empty($primarySequenceName) ? $this->context->getConnection()->getSupplementalDriver()->delimite($primarySequenceName) : $primarySequenceName ); diff --git a/tests/Database/Table/Table.cache.observer.phpt b/tests/Database/Table/Table.cache.observer.phpt index 1b77d1339..df0ddd2cb 100644 --- a/tests/Database/Table/Table.cache.observer.phpt +++ b/tests/Database/Table/Table.cache.observer.phpt @@ -13,44 +13,13 @@ require __DIR__ . '/../connect.inc.php'; // create $connection Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -class CacheMock implements Nette\Caching\IStorage -{ - public $writes = 0; - private $defaultBook = array('id' => TRUE, 'author_id' => TRUE); +$cacheStorage = Mockery::mock('Nette\Caching\Istorage'); +$cacheStorage->shouldReceive('read')->withAnyArgs()->once()->andReturn(array('id' => TRUE)); +$cacheStorage->shouldReceive('read')->withAnyArgs()->times(4)->andReturn(array('id' => TRUE, 'author_id' => TRUE)); +$cacheStorage->shouldReceive('write')->with(Mockery::any(), array('id' => TRUE, 'author_id' => TRUE, 'title' => TRUE), array()); - function read($key) - { - $key = substr($key, strpos($key, "\x00") + 1); - switch ($key) { - case "aad5184d8c52b773bd73b5c7c5c819c9": // authors - return array('id' => TRUE); - case "d7dc896279409ab73e6742c667cf8dc1": // book - return $this->defaultBook; - } - } - - function write($key, $data, array $dependencies) - { - $key = substr($key, strpos($key, "\x00") + 1); - $this->writes++; - switch ($key) { - case "aad5184d8c52b773bd73b5c7c5c819c9": - return; - case "d7dc896279409ab73e6742c667cf8dc1": - $this->defaultBook = $data; - return; - } - } - - function lock($key) {} - function remove($key) {} - function clean(array $conditions) {} -} - -$cacheStorage = new CacheMock; $context = new Nette\Database\Context($connection, $structure, $conventions, $cacheStorage); - $queries = 0; $connection->onQuery[] = function($dao, ResultSet $result) use (& $queries) { if (!preg_match('#SHOW|CONSTRAINT_NAME|pg_catalog|sys\.|SET|PRAGMA|FROM sqlite_#i', $result->queryString)) { @@ -70,5 +39,5 @@ unset($book, $author); foreach ($stack as $selection) $selection->__destruct(); $authors->__destruct(); -Assert::same(1, $cacheStorage->writes); Assert::same(3, $queries); +Mockery::close(); diff --git a/tests/Database/Table/Table.cache.observer2.phpt b/tests/Database/Table/Table.cache.observer2.phpt index 9c06a8169..c7417a8a0 100644 --- a/tests/Database/Table/Table.cache.observer2.phpt +++ b/tests/Database/Table/Table.cache.observer2.phpt @@ -27,23 +27,22 @@ class CacheMock extends MemoryStorage $cacheStorage = new CacheMock; $context = new Nette\Database\Context($connection, $structure, $conventions, $cacheStorage); +for ($i = 0; $i < 2; $i += 1) { + $authors = $context->table('author'); + foreach ($authors as $author) { + $author->name; + } -$authors = $context->table('author'); -foreach ($authors as $author) { - $author->name; -} - - -$authors->where('web IS NOT NULL'); -foreach ($authors as $author) { - $author->web; + if ($i === 0) { + $authors->where('web IS NOT NULL'); + foreach ($authors as $author) { + $author->web; + } + $authors->__destruct(); + } else { + $sql = $authors->getSql(); + } } -$authors->__destruct(); - - -$authors = $context->table('author'); -Assert::equal(reformat('SELECT [id], [name] FROM [author]'), $authors->getSql()); - - +Assert::equal(reformat('SELECT [id], [name] FROM [author]'), $sql); Assert::same(2, $cacheStorage->writes); diff --git a/tests/Database/Table/Table.cache.phpt b/tests/Database/Table/Table.cache.phpt index 0993c178d..6c451a477 100644 --- a/tests/Database/Table/Table.cache.phpt +++ b/tests/Database/Table/Table.cache.phpt @@ -13,25 +13,41 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverN test(function() use ($context) { // Testing Selection caching - $bookSelection = $context->table('book')->wherePrimary(2); - Assert::same(reformat('SELECT * FROM [book] WHERE ([book].[id] = ?)'), $bookSelection->getSql()); - + $sql = array(); + for ($i = 0; $i < 4; $i += 1) { + if ($i !== 2) { + $bookSelection = $context->table('book')->wherePrimary(2); + } - $book = $bookSelection->fetch(); - $book->title; - $book->translator; - $bookSelection->__destruct(); - $bookSelection = $context->table('book')->wherePrimary(2); - Assert::same(reformat('SELECT [id], [title], [translator_id] FROM [book] WHERE ([book].[id] = ?)'), $bookSelection->getSql()); + $sql[] = $bookSelection->getSql(); + if ($i !== 2) { + $book = $bookSelection->fetch(); + $book->title; + $book->translator; + if ($i === 1) { + $book->author; + } else { + $bookSelection->__destruct(); + } + } else { + $bookSelection->__destruct(); + } + } - $book = $bookSelection->fetch(); - $book->author_id; - Assert::same(reformat('SELECT * FROM [book] WHERE ([book].[id] = ?)'), $bookSelection->getSql()); + /* + * schedule: + * - fetch all columns / cycle 1 + * - fetch used columns, require another and fetch all again / cycle 2, 3 + * - fetch used column with new used column / cycle 4 + */ - $bookSelection->__destruct(); - $bookSelection = $context->table('book')->wherePrimary(2); - Assert::same(reformat('SELECT [id], [title], [translator_id], [author_id] FROM [book] WHERE ([book].[id] = ?)'), $bookSelection->getSql()); + Assert::same(array( + reformat('SELECT * FROM [book] WHERE ([book].[id] = ?)'), + reformat('SELECT [id], [title], [translator_id] FROM [book] WHERE ([book].[id] = ?)'), + reformat('SELECT * FROM [book] WHERE ([book].[id] = ?)'), + reformat('SELECT [id], [title], [translator_id], [author_id] FROM [book] WHERE ([book].[id] = ?)') + ), $sql); }); @@ -107,61 +123,65 @@ test(function() use ($context) { }); -test(function() use ($context) { - $author = $context->table('author')->get(11); - $books = $author->related('book')->where('translator_id', 99); // 0 rows - foreach ($books as $book) {} - $books->__destruct(); - unset($author); - - $author = $context->table('author')->get(11); - $books = $author->related('book')->where('translator_id', 11); - Assert::same(array('id', 'author_id'), $books->getPreviousAccessedColumns()); +test(function() use ($context) { // Test saving joining keys even with 0 rows + $cols = array(); + for ($i = 0; $i < 2; $i += 1) { + $author = $context->table('author')->get(11); + $books = $author->related('book')->where('translator_id', 99); // 0 rows + $cols[] = $books->getPreviousAccessedColumns(); + foreach ($books as $book) {} + $books->__destruct(); + } + + Assert::same(array( + array(), + array('id', 'author_id'), + ), $cols); }); test(function() use ($context) { // Test saving the union of needed cols, the second call is subset - $author = $context->table('author')->get(11); - $books = $author->related('book'); - foreach ($books as $book) { - $book->translator_id; - $book->title; - } - $books->__destruct(); - - $author = $context->table('author')->get(11); - $books = $author->related('book'); - foreach ($books as $book) { - $book->title; + $cols = array(); + for ($i = 0; $i < 3; $i += 1) { + $author = $context->table('author')->get(11); + $books = $author->related('book'); + $cols[] = $books->getPreviousAccessedColumns(); + foreach ($books as $book) { + if ($i === 0) { + $book->translator_id; + } + $book->title; + } + $books->__destruct(); } - $books->__destruct(); - $author = $context->table('author')->get(11); - Assert::same( - reformat('SELECT [id], [author_id], [translator_id], [title] FROM [book] WHERE ([book].[author_id] IN (?))'), - $author->related('book')->getSql() - ); + Assert::same(array( + array(), + array('id', 'author_id', 'translator_id', 'title'), + array('id', 'author_id', 'translator_id', 'title'), + ), $cols); }); test(function() use ($context) { // Test saving the union of needed cols, the second call is not subset - $author = $context->table('author')->get(11); - $books = $author->related('book'); - foreach ($books as $book) { - $book->translator_id; - } - $books->__destruct(); - - $author = $context->table('author')->get(11); - $books = $author->related('book'); - foreach ($books as $book) { - $book->title; + $cols = array(); + for ($i = 0; $i < 3; $i += 1) { + $author = $context->table('author')->get(11); + $books = $author->related('book'); + $cols[] = $books->getPreviousAccessedColumns(); + foreach ($books as $book) { + if ($i === 0) { + $book->translator_id; + } else { + $book->title; + } + } + $books->__destruct(); } - $books->__destruct(); - $author = $context->table('author')->get(11); - Assert::same( - reformat('SELECT [id], [author_id], [translator_id], [title] FROM [book] WHERE ([book].[author_id] IN (?))'), - $author->related('book')->getSql() - ); + Assert::same(array( + array(), + array('id', 'author_id', 'translator_id'), + array('id', 'author_id', 'translator_id', 'title'), + ), $cols); });