Skip to content

Commit 408925f

Browse files
committed
Selection: previously accessed columns are cached by stack trace EXPERIMENTAL! [closes #6][closes #15]
1 parent 1943f3c commit 408925f

File tree

4 files changed

+119
-82
lines changed

4 files changed

+119
-82
lines changed

src/Database/Table/Selection.php

+20-2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ class Selection extends Nette\Object implements \Iterator, IRowContainer, \Array
6363
/** @var string */
6464
protected $generalCacheKey;
6565

66+
/** @var array */
67+
protected $generalCacheTraceKey;
68+
6669
/** @var string */
6770
protected $specificCacheKey;
6871

@@ -536,6 +539,11 @@ protected function emptyResultSet($saveCache = TRUE)
536539
$this->saveCacheState();
537540
}
538541

542+
if ($saveCache) {
543+
// null only if missing some column
544+
$this->generalCacheTraceKey = NULL;
545+
}
546+
539547
$this->rows = NULL;
540548
$this->specificCacheKey = NULL;
541549
$this->generalCacheKey = NULL;
@@ -590,7 +598,17 @@ protected function getGeneralCacheKey()
590598
return $this->generalCacheKey;
591599
}
592600

593-
return $this->generalCacheKey = md5(serialize(array(__CLASS__, $this->name, $this->sqlBuilder->getConditions())));
601+
$key = array(__CLASS__, $this->name, $this->sqlBuilder->getConditions());
602+
if (!$this->generalCacheTraceKey) {
603+
$trace = array();
604+
foreach (debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : FALSE) as $item) {
605+
$trace[] = isset($item['file'], $item['line']) ? $item['file'] . $item['line'] : NULL;
606+
};
607+
$this->generalCacheTraceKey = $trace;
608+
}
609+
610+
$key[] = $this->generalCacheTraceKey;
611+
return $this->generalCacheKey = md5(serialize($key));
594612
}
595613

596614

@@ -714,7 +732,7 @@ public function insert($data)
714732

715733
$primarySequenceName = $this->getPrimarySequence();
716734
$primaryKey = $this->context->getInsertId(
717-
!empty($primarySequenceName)
735+
!empty($primarySequenceName)
718736
? $this->context->getConnection()->getSupplementalDriver()->delimite($primarySequenceName)
719737
: $primarySequenceName
720738
);

tests/Database/Table/Table.cache.observer.phpt

+4-4
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ class CacheMock implements Nette\Caching\IStorage
2222
{
2323
$key = substr($key, strpos($key, "\x00") + 1);
2424
switch ($key) {
25-
case "aad5184d8c52b773bd73b5c7c5c819c9": // authors
25+
case "ee566f725ded1b420878277389b7f107": // authors
2626
return array('id' => TRUE);
27-
case "d7dc896279409ab73e6742c667cf8dc1": // book
27+
case "2a143ae537f422ad7bcef299a821cbad": // book
2828
return $this->defaultBook;
2929
}
3030
}
@@ -34,9 +34,9 @@ class CacheMock implements Nette\Caching\IStorage
3434
$key = substr($key, strpos($key, "\x00") + 1);
3535
$this->writes++;
3636
switch ($key) {
37-
case "aad5184d8c52b773bd73b5c7c5c819c9":
37+
case "ee566f725ded1b420878277389b7f107":
3838
return;
39-
case "d7dc896279409ab73e6742c667cf8dc1":
39+
case "2a143ae537f422ad7bcef299a821cbad":
4040
$this->defaultBook = $data;
4141
return;
4242
}

tests/Database/Table/Table.cache.observer2.phpt

+15-16
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,22 @@ class CacheMock extends MemoryStorage
2727
$cacheStorage = new CacheMock;
2828
$context = new Nette\Database\Context($connection, $structure, $conventions, $cacheStorage);
2929

30+
for ($i = 0; $i < 2; $i += 1) {
31+
$authors = $context->table('author');
32+
foreach ($authors as $author) {
33+
$author->name;
34+
}
3035

31-
$authors = $context->table('author');
32-
foreach ($authors as $author) {
33-
$author->name;
34-
}
35-
36-
37-
$authors->where('web IS NOT NULL');
38-
foreach ($authors as $author) {
39-
$author->web;
36+
if ($i === 0) {
37+
$authors->where('web IS NOT NULL');
38+
foreach ($authors as $author) {
39+
$author->web;
40+
}
41+
$authors->__destruct();
42+
} else {
43+
$sql = $authors->getSql();
44+
}
4045
}
4146

42-
$authors->__destruct();
43-
44-
45-
$authors = $context->table('author');
46-
Assert::equal(reformat('SELECT [id], [name] FROM [author]'), $authors->getSql());
47-
48-
47+
Assert::equal(reformat('SELECT [id], [name] FROM [author]'), $sql);
4948
Assert::same(2, $cacheStorage->writes);

tests/Database/Table/Table.cache.phpt

+80-60
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,41 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverN
1313

1414

1515
test(function() use ($context) { // Testing Selection caching
16-
$bookSelection = $context->table('book')->wherePrimary(2);
17-
Assert::same(reformat('SELECT * FROM [book] WHERE ([book].[id] = ?)'), $bookSelection->getSql());
18-
16+
$sql = array();
17+
for ($i = 0; $i < 4; $i += 1) {
18+
if ($i !== 2) {
19+
$bookSelection = $context->table('book')->wherePrimary(2);
20+
}
1921

20-
$book = $bookSelection->fetch();
21-
$book->title;
22-
$book->translator;
23-
$bookSelection->__destruct();
24-
$bookSelection = $context->table('book')->wherePrimary(2);
25-
Assert::same(reformat('SELECT [id], [title], [translator_id] FROM [book] WHERE ([book].[id] = ?)'), $bookSelection->getSql());
22+
$sql[] = $bookSelection->getSql();
2623

24+
if ($i !== 2) {
25+
$book = $bookSelection->fetch();
26+
$book->title;
27+
$book->translator;
28+
if ($i === 1) {
29+
$book->author;
30+
} else {
31+
$bookSelection->__destruct();
32+
}
33+
} else {
34+
$bookSelection->__destruct();
35+
}
36+
}
2737

28-
$book = $bookSelection->fetch();
29-
$book->author_id;
30-
Assert::same(reformat('SELECT * FROM [book] WHERE ([book].[id] = ?)'), $bookSelection->getSql());
38+
/*
39+
* schedule:
40+
* - fetch all columns / cycle 1
41+
* - fetch used columns, require another and fetch all again / cycle 2, 3
42+
* - fetch used column with new used column / cycle 4
43+
*/
3144

32-
$bookSelection->__destruct();
33-
$bookSelection = $context->table('book')->wherePrimary(2);
34-
Assert::same(reformat('SELECT [id], [title], [translator_id], [author_id] FROM [book] WHERE ([book].[id] = ?)'), $bookSelection->getSql());
45+
Assert::same(array(
46+
reformat('SELECT * FROM [book] WHERE ([book].[id] = ?)'),
47+
reformat('SELECT [id], [title], [translator_id] FROM [book] WHERE ([book].[id] = ?)'),
48+
reformat('SELECT * FROM [book] WHERE ([book].[id] = ?)'),
49+
reformat('SELECT [id], [title], [translator_id], [author_id] FROM [book] WHERE ([book].[id] = ?)')
50+
), $sql);
3551
});
3652

3753

@@ -107,61 +123,65 @@ test(function() use ($context) {
107123
});
108124

109125

110-
test(function() use ($context) {
111-
$author = $context->table('author')->get(11);
112-
$books = $author->related('book')->where('translator_id', 99); // 0 rows
113-
foreach ($books as $book) {}
114-
$books->__destruct();
115-
unset($author);
116-
117-
$author = $context->table('author')->get(11);
118-
$books = $author->related('book')->where('translator_id', 11);
119-
Assert::same(array('id', 'author_id'), $books->getPreviousAccessedColumns());
126+
test(function() use ($context) { // Test saving joining keys even with 0 rows
127+
$cols = array();
128+
for ($i = 0; $i < 2; $i += 1) {
129+
$author = $context->table('author')->get(11);
130+
$books = $author->related('book')->where('translator_id', 99); // 0 rows
131+
$cols[] = $books->getPreviousAccessedColumns();
132+
foreach ($books as $book) {}
133+
$books->__destruct();
134+
}
135+
136+
Assert::same(array(
137+
array(),
138+
array('id', 'author_id'),
139+
), $cols);
120140
});
121141

122142

123143
test(function() use ($context) { // Test saving the union of needed cols, the second call is subset
124-
$author = $context->table('author')->get(11);
125-
$books = $author->related('book');
126-
foreach ($books as $book) {
127-
$book->translator_id;
128-
$book->title;
129-
}
130-
$books->__destruct();
131-
132-
$author = $context->table('author')->get(11);
133-
$books = $author->related('book');
134-
foreach ($books as $book) {
135-
$book->title;
144+
$cols = array();
145+
for ($i = 0; $i < 3; $i += 1) {
146+
$author = $context->table('author')->get(11);
147+
$books = $author->related('book');
148+
$cols[] = $books->getPreviousAccessedColumns();
149+
foreach ($books as $book) {
150+
if ($i === 0) {
151+
$book->translator_id;
152+
}
153+
$book->title;
154+
}
155+
$books->__destruct();
136156
}
137-
$books->__destruct();
138157

139-
$author = $context->table('author')->get(11);
140-
Assert::same(
141-
reformat('SELECT [id], [author_id], [translator_id], [title] FROM [book] WHERE ([book].[author_id] IN (?))'),
142-
$author->related('book')->getSql()
143-
);
158+
Assert::same(array(
159+
array(),
160+
array('id', 'author_id', 'translator_id', 'title'),
161+
array('id', 'author_id', 'translator_id', 'title'),
162+
), $cols);
144163
});
145164

146165

147166
test(function() use ($context) { // Test saving the union of needed cols, the second call is not subset
148-
$author = $context->table('author')->get(11);
149-
$books = $author->related('book');
150-
foreach ($books as $book) {
151-
$book->translator_id;
152-
}
153-
$books->__destruct();
154-
155-
$author = $context->table('author')->get(11);
156-
$books = $author->related('book');
157-
foreach ($books as $book) {
158-
$book->title;
167+
$cols = array();
168+
for ($i = 0; $i < 3; $i += 1) {
169+
$author = $context->table('author')->get(11);
170+
$books = $author->related('book');
171+
$cols[] = $books->getPreviousAccessedColumns();
172+
foreach ($books as $book) {
173+
if ($i === 0) {
174+
$book->translator_id;
175+
} else {
176+
$book->title;
177+
}
178+
}
179+
$books->__destruct();
159180
}
160-
$books->__destruct();
161181

162-
$author = $context->table('author')->get(11);
163-
Assert::same(
164-
reformat('SELECT [id], [author_id], [translator_id], [title] FROM [book] WHERE ([book].[author_id] IN (?))'),
165-
$author->related('book')->getSql()
166-
);
182+
Assert::same(array(
183+
array(),
184+
array('id', 'author_id', 'translator_id'),
185+
array('id', 'author_id', 'translator_id', 'title'),
186+
), $cols);
167187
});

0 commit comments

Comments
 (0)