More tidying.
[yaffs-website] / vendor / gabordemooij / redbean / testing / RedUNIT / Base / Writecache.php
1 <?php
2
3 namespace RedUNIT\Base;
4
5 use RedUNIT\Base as Base;
6 use RedBeanPHP\Facade as R;
7
8 /**
9  * Writecache
10  *
11  * The Query Writer Cache tries to avoid unnecessary queries
12  * by using cache markers. This means repeatingly fetching the
13  * same parent bean for instance (in loop) won't hurt performance.
14  * The Query Writer Cache gets emptied if the chain of markers
15  * gets broken. All 'non destructive' queries are marked, as long
16  * as no other queries have been executed we can safely assume
17  * nothing in the database has changed for the current request.
18  * You might want to turn this form of caching off in long running
19  * PHP processes.
20  *
21  * @file    RedUNIT/Base/Writecache.php
22  * @desc    Tests the Query Writer cache implemented in the
23  * @author  Gabor de Mooij and the RedBeanPHP Community
24  * @license New BSD/GPLv2
25  *
26  * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
27  * This source file is subject to the New BSD/GPLv2 License that is bundled
28  * with this source code in the file license.txt.
29  */
30 class Writecache extends Base
31 {
32
33         /**
34          * What drivers should be loaded for this test pack?
35          */
36         public function getTargetDrivers()
37         {
38                 return array( 'mysql', 'pgsql', 'sqlite', 'CUBRID' );
39         }
40
41         /**
42          * Test whether cache size remains constant (per type).
43          * Avoiding potential memory leaks. (Issue #424).
44          *
45          * @return void
46          */
47         public function testCacheSize()
48         {
49                 R::nuke();
50                 R::useWriterCache( TRUE );
51                 $writer = R::getWriter();
52                 $bean = R::dispense( 'bean' );
53                 $bean->prop = 1;
54                 R::store( $bean );
55                 $writer->flushCache( 20 );
56                 $count = $writer->flushCache();
57                 asrt( $count, 0 );
58                 R::find( 'bean', ' prop < ? ', array( 1 ) );
59                 $count = $writer->flushCache();
60                 asrt( $count, 2 );
61                 R::find( 'bean', ' prop < ? ', array( 2 ) );
62                 $count = $writer->flushCache();
63                 asrt( $count, 5 );
64                 R::find( 'bean', ' prop < ? ', array( 2 ) );
65                 $count = $writer->flushCache();
66                 asrt( $count, 5 );
67                 for( $i = 0; $i < 40; $i ++ ) {
68                         R::find( 'bean', ' prop < ? ', array( $i ) );
69                 }
70                 $count = $writer->flushCache();
71                 asrt( $count, 85 );
72                 for( $i = 0; $i < 120; $i ++ ) {
73                         R::find( 'bean', ' prop < ? ', array( $i ) );
74                 }
75                 $count = $writer->flushCache( 1 );
76                 asrt( $count, 85 );
77                 for( $i = 0; $i < 20; $i ++ ) {
78                         R::find( 'bean', ' prop < ? ', array( $i ) );
79                 }
80                 $count = $writer->flushCache( 20 );
81                 asrt( $count, 9 );
82         }
83
84         /**
85          * When using fetchAs(), Query Cache does not recognize objects
86          * that have been previously fetched, see issue #400.
87          */
88         public function testCachingAndFetchAs()
89         {
90                 testpack( 'Testing whether you can cache multiple records of the same type' );
91                 R::debug( true, 1 );
92                 $logger = R::getDatabaseAdapter()->getDatabase()->getLogger();
93                 R::nuke();
94                 $coauthor1 = R::dispense( 'author' );
95                 $coauthor1->name = 'John';
96                 $book = R::dispense( 'book' );
97                 $book->title = 'a Funny Tale';
98                 $book->coauthor = $coauthor1;
99                 $id = R::store( $book );
100                 $coauthor = R::dispense( 'author' );
101                 $coauthor->name = 'Pete';
102                 $book = R::dispense( 'book' );
103                 $book->title = 'a Funny Tale 2';
104                 $book->coauthor = $coauthor;
105                 $id = R::store( $book );
106                 $book = R::dispense( 'book' );
107                 $book->title = 'a Funny Tale 3';
108                 $book->coauthor = $coauthor1;
109                 $id = R::store( $book );
110                 $books = R::find( 'book' );
111                 $logger->clear();
112                 $authors = array();
113                 $authorsByName = array();
114                 foreach($books as $book) {
115                         $coAuthor = $book->with( ' ORDER BY title ASC ' )
116                                 ->fetchAs( 'author' )->coauthor;
117                         $authors[] = $coAuthor->name;
118                         $authorsByName[ $coAuthor->name ] = $coAuthor;
119                 }
120                 asrt( count( $logger->grep( 'SELECT' ) ), 2 ); //must be 2! 3 if cache does not work!
121                 asrt( count( $authors ), 3 );
122                 asrt( isset( $authorsByName[ 'John' ] ), TRUE );
123                 asrt( isset( $authorsByName[ 'Pete' ] ), TRUE );
124                 $logger->clear();
125                 $authors = array();
126                 $authorsByName = array();
127                 foreach($books as $book) {
128                         $coAuthor = $book->with( ' ORDER BY title DESC ' )
129                                 ->fetchAs( 'author' )->coauthor;
130                         $authors[] = $coAuthor->name;
131                         $authorsByName[ $coAuthor->name ] = $coAuthor;
132                 }
133                 asrt( count( $logger->grep( 'SELECT' ) ), 0 ); //must be 0!
134                 asrt( count( $authors ), 3 );
135                 asrt( isset( $authorsByName[ 'John' ] ), TRUE );
136                 asrt( isset( $authorsByName[ 'Pete' ] ), TRUE );
137         }
138
139         /**
140          * Test effects of cache.
141          *
142          * @return void
143          */
144         public function testCachingEffects()
145         {
146                 testpack( 'Testing WriteCache Query Writer Cache' );
147                 R::setNarrowFieldMode( FALSE );
148                 R::useWriterCache( FALSE );
149                 R::debug( TRUE, 1 );
150                 $logger = R::getDatabaseAdapter()->getDatabase()->getLogger();
151                 $book = R::dispense( 'book' )->setAttr( 'title', 'ABC' );
152                 $book->ownPage[] = R::dispense( 'page' );
153                 $id = R::store( $book );
154                 // Test load cache -- without
155                 $logger->clear();
156                 $book = R::load( 'book', $id );
157                 $book = R::load( 'book', $id );
158                 asrt( count( $logger->grep( 'SELECT' ) ), 2 );
159                 // With cache
160                 R::useWriterCache( TRUE );
161                 $logger->clear();
162                 $book = R::load( 'book', $id );
163                 $book = R::load( 'book', $id );
164                 asrt( count( $logger->grep( 'SELECT' ) ), 1 );
165                 R::useWriterCache( FALSE );
166                 // Test find cache
167                 $logger->clear();
168                 $book = R::find( 'book' );
169                 $book = R::find( 'book' );
170                 asrt( count( $logger->grep( 'SELECT' ) ), 2 );
171                 // With cache
172                 R::getWriter()->setUseCache( TRUE );
173                 $logger->clear();
174                 $book = R::find( 'book' );
175                 $book = R::find( 'book' );
176                 asrt( count( $logger->grep( 'SELECT' ) ), 1 );
177                 R::getWriter()->setUseCache( FALSE );
178                 // Test combinations
179                 $logger->clear();
180                 $book = R::findOne( 'book', ' id = ? ', array( $id ) );
181                 $book->ownPage;
182                 R::batch( 'book', array( $id ) );
183                 $book = R::findOne( 'book', ' id = ? ', array( $id ) );
184                 $book->ownPage;
185                 R::batch( 'book', array( $id ) );
186                 asrt( count( $logger->grep( 'SELECT' ) ), 6 );
187                 // With cache
188                 R::getWriter()->setUseCache( TRUE );
189                 $logger->clear();
190                 R::batch( 'book', array( $id ) );
191                 $book = R::findOne( 'book', ' id = ? ', array( $id ) );
192                 $book->ownPage;
193                 $book = R::findOne( 'book', ' id = ? ', array( $id ) );
194                 $book->ownPage;
195                 asrt( count( $logger->grep( 'SELECT' ) ), 3 );
196                 R::getWriter()->setUseCache( FALSE );
197                 // Test auto flush
198                 $logger->clear();
199                 $book = R::findOne( 'book' );
200                 $book->name = 'X';
201                 R::store( $book );
202                 $book = R::findOne( 'book' );
203                 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
204                 // With cache
205                 R::getWriter()->setUseCache( TRUE );
206                 $logger->clear();
207                 $book = R::findOne( 'book' );
208                 $book->name = 'Y';
209                 // Will flush
210                 R::store( $book );
211                 $book = R::findOne( 'book' );
212                 // Now the same, auto flushed
213                 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
214                 R::getWriter()->setUseCache( FALSE );
215                 // Test whether delete flushes as well (because uses selectRecord - might be a gotcha!)
216                 R::store( R::dispense( 'garbage' ) );
217                 $garbage = R::findOne( 'garbage' );
218                 $logger->clear();
219                 $book = R::findOne( 'book' );
220                 R::trash( $garbage );
221                 $book = R::findOne( 'book' );
222                 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
223                 R::store( R::dispense( 'garbage' ) );
224                 $garbage = R::findOne( 'garbage' );
225                 // With cache
226                 R::getWriter()->setUseCache( TRUE );
227                 $logger->clear();
228                 $book = R::findOne( 'book' );
229                 R::trash( $garbage );
230                 $book = R::findOne( 'book' );
231                 // Now the same, auto flushed
232                 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
233                 R::getWriter()->setUseCache( FALSE );
234                 R::store( R::dispense( 'garbage' ) );
235                 $garbage = R::findOne( 'garbage' );
236                 // With cache
237                 R::getWriter()->setUseCache( TRUE );
238                 $logger->clear();
239                 $book = R::findOne( 'book' );
240                 R::getWriter()->queryRecord( 'garbage', array( 'id' => array( $garbage->id ) ) );
241                 $book = R::findOne( 'book' );
242                 // Now the same, auto flushed
243                 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
244                 $page = R::dispense('page');
245                 $book->sharedPage[] = $page;
246                 R::store( $book );
247                 $logger->clear();
248                 $link = R::getWriter()->queryRecordLink( 'book', 'page', $book->id, $page->id );
249                 asrt( count( $logger->grep( 'SELECT' ) ), 1 );
250                 $link = R::getWriter()->queryRecordLink( 'book', 'page', $book->id, $page->id );
251                 asrt( count( $logger->grep( 'SELECT' ) ), 1 );
252                 R::getWriter()->setUseCache( FALSE );
253                 $link = R::getWriter()->queryRecordLink( 'book', 'page', $book->id, $page->id );
254                 asrt( count( $logger->grep( 'SELECT' ) ), 2 );
255                 R::getWriter()->setUseCache( TRUE );
256                 R::setNarrowFieldMode( TRUE );
257         }
258
259         /**
260          * Try to fool the cache :)
261          *
262          * @return void
263          */
264         public function testRegressions()
265         {
266                 testpack( 'Testing possible regressions: Try to fool the cache' );
267                 $str = 'SELECT * FROM ' . R::getWriter()->esc( 'bean', TRUE ) . ' WHERE ( ' . R::getWriter()->esc( 'id', TRUE ) . '  IN ( 1)  ) ';
268                 $bean = R::dispense( 'bean' );
269                 $bean->title = 'abc';
270                 $id = R::store( $bean );
271                 $bean = R::load( 'bean', $id );
272                 $bean->title = 'xxx';
273                 R::store( $bean );
274                 // Fire exact same query so cache may think no other query has been fired
275                 R::exec( $str );
276                 $bean = R::load( 'bean', $id );
277                 asrt( $bean->title, 'xxx' );
278         }
279
280         /**
281          * Test keep-cache comment.
282          *
283          * @return void
284          */
285         public function testKeepCacheCommentInSQL()
286         {
287                 $bean = R::dispense( 'bean' );
288                 $bean->title = 'abc';
289                 $id = R::store( $bean );
290                 $bean = R::load( 'bean', $id );
291                 $bean->title = 'xxx';
292                 R::store( $bean );
293                 // Causes flush even though it contains -- keep-cache (not at the end, not intended)
294                 R::findOne( 'bean', ' title = ? ', array( '-- keep-cache' ) );
295                 $bean = R::load( 'bean', $id );
296                 asrt( $bean->title, 'xxx' );
297         }
298
299         /**
300          *
301          * Same as above.. test keep cache.
302          *
303          * @return void
304          */
305         public function testInstructNoDrop()
306         {
307                 $str = 'SELECT * FROM ' . R::getWriter()->esc( 'bean', TRUE ) . ' -- keep-cache';
308                 $bean = R::dispense( 'bean' );
309                 $bean->title = 'abc';
310                 $id = R::store( $bean );
311                 $bean = R::load( 'bean', $id );
312                 $bean->title = 'xxx';
313                 R::store( $bean );
314                 R::exec( $str );
315                 $bean = R::load( 'bean', $id );
316                 asrt( $bean->title, 'abc' );
317                 R::nuke();
318                 // Now INSTRUCT the cache to not drop the cache CASE 2
319                 $str = 'SELECT * FROM ' . R::getWriter()->esc( 'bean', TRUE ) . ' -- keep-cache';
320                 $bean = R::dispense( 'bean' );
321                 $bean->title = 'abc';
322                 $id = R::store( $bean );
323                 $bean = R::load( 'bean', $id );
324                 $bean->title = 'xxx';
325                 R::store( $bean );
326                 R::findOne( 'bean', ' title = ? ', array( 'cache' ) );
327                 $bean = R::load( 'bean', $id );
328                 asrt( $bean->title, 'xxx' );
329         }
330
331         /**
332          * Can we confuse the cache?
333          *
334          * @return void
335          */
336         public function testConfusionRegression()
337         {
338                 testpack( 'Testing possible confusion regression' );
339                 $bean = R::dispense( 'bean' );
340                 $bean->title = 'abc';
341                 $id1 = R::store( $bean );
342                 $bean = R::dispense( 'bean' );
343                 $bean->title = 'abc2';
344                 $id2 = R::store( $bean );
345                 $bean = R::load( 'bean', $id1 );
346                 asrt( $bean->title, 'abc' );
347                 $bean = R::load( 'bean', $id2 );
348                 asrt( $bean->title, 'abc2' );
349         }
350
351         /**
352          * Test Ghost beans....
353          *
354          * @return void
355          */
356         public function testGhostBeans()
357         {
358                 testpack( 'Testing ghost beans' );
359                 $bean = R::dispense( 'bean' );
360                 $bean->title = 'abc';
361                 $id1 = R::store( $bean );
362                 R::trash( $bean );
363                 $bean = R::load( 'bean', $id1 );
364                 asrt( (int) $bean->id, 0 );
365         }
366
367         /**
368          * Test explicit flush.
369          *
370          * @return void
371          */
372         public function testExplicitCacheFlush()
373         {
374                 testpack( 'Test cache flush (explicit)' );
375                 R::setNarrowFieldMode( FALSE );
376                 R::debug( true, 1 );
377                 $logger = R::getDatabaseAdapter()->getDatabase()->getLogger();
378                 $bean = R::dispense( 'bean' );
379                 $bean->title = 'abc';
380                 $id1 = R::store( $bean );
381                 $logger->clear();
382                 $bean = R::load( 'bean', $id1 );
383                 asrt( $bean->title, 'abc' );
384                 asrt( count( $logger->grep( 'SELECT *' ) ), 1 );
385                 $bean = R::load( 'bean', $id1 );
386                 asrt( count( $logger->grep( 'SELECT *' ) ), 1 );
387                 R::getWriter()->flushCache();
388                 $bean = R::load( 'bean', $id1 );
389                 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
390                 R::getWriter()->flushCache();
391                 R::getWriter()->setUseCache( FALSE );
392                 R::setNarrowFieldMode( TRUE );
393         }
394 }