db backup prior to drupal security update
[yaffs-website] / vendor / gabordemooij / redbean / testing / RedUNIT / Mysql / Writer.php
1 <?php
2
3 namespace RedUNIT\Mysql;
4
5 use RedBeanPHP\Facade as R;
6 use RedBeanPHP\AssociationManager as AssociationManager;
7 use RedBeanPHP\QueryWriter as QueryWriter;
8 use RedBeanPHP\QueryWriter\MySQL as MySQL;
9 use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
10 use RedBeanPHP\RedException\SQL as SQL;
11 use RedBeanPHP\RedException as RedException;
12
13 /**
14  * Writer
15  *
16  * Tests for MySQL and MariaDB Query Writer.
17  * This test class contains Query Writer specific tests.
18  * Use this class to add tests to test Query Writer specific
19  * behaviours, quirks and issues.
20  *
21  * @file    RedUNIT/Mysql/Writer.php
22  * @desc    A collection of database specific writer functions.
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 Writer extends \RedUNIT\Mysql
31 {
32
33         /**
34          * Test Facade bind function method.
35          * Test for MySQL WKT spatial format.
36          */
37         public function testFunctionFilters()
38         {
39                 R::nuke();
40                 R::bindFunc( 'read', 'location.point', 'asText' );
41                 R::bindFunc( 'write', 'location.point', 'GeomFromText' );
42                 R::store(R::dispense('location'));
43                 R::freeze( true );
44                 try {
45                         R::find('location');
46                         fail();
47                 } catch( SQL $exception ) {
48                         pass();
49                 }
50                 R::freeze( false );
51                 try {
52                         R::find('location');
53                         pass();
54                 } catch( SQL $exception ) {
55                         fail();
56                 }
57                 $location = R::dispense( 'location' );
58                 $location->point = 'POINT(14 6)';
59                 R::store($location);
60                 $columns = R::inspect( 'location' );
61                 asrt( $columns['point'], 'point' );
62                 $location = $location->fresh();
63                 asrt( $location->point, 'POINT(14 6)' );
64                 R::nuke();
65                 $location = R::dispense( 'location' );
66                 $location->point = 'LINESTRING(0 0,1 1,2 2)';
67                 R::store($location);
68                 $columns = R::inspect( 'location' );
69                 asrt( $columns['point'], 'linestring' );
70                 $location->bustcache = 2;
71                 R::store($location);
72                 $location = $location->fresh();
73                 asrt( $location->point, 'LINESTRING(0 0,1 1,2 2)' );
74                 R::nuke();
75                 $location = R::dispense( 'location' );
76                 $location->point = 'POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))';
77                 R::store($location);
78                 $columns = R::inspect( 'location' );
79                 asrt( $columns['point'], 'polygon' );
80                 $location->bustcache = 4;
81                 R::store($location);
82                 $location = $location->fresh();
83                 asrt( $location->point, 'POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))' );
84                 R::bindFunc( 'read', 'location.point', NULL );
85                 $location->bustcache = 1;
86                 R::store($location);
87                 $location = $location->fresh();
88                 asrt( ( $location->point === 'POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))' ), FALSE );
89                 $filters = AQueryWriter::getSQLFilters();
90                 asrt( is_array( $filters ), TRUE );
91                 asrt( count( $filters ), 2 );
92                 asrt( isset( $filters[ QueryWriter::C_SQLFILTER_READ] ), TRUE );
93                 asrt( isset( $filters[ QueryWriter::C_SQLFILTER_WRITE] ), TRUE );
94                 R::bindFunc( 'read', 'place.point', 'asText' );
95                 R::bindFunc( 'write', 'place.point', 'GeomFromText' );
96                 R::bindFunc( 'read', 'place.line', 'asText' );
97                 R::bindFunc( 'write', 'place.line', 'GeomFromText' );
98                 R::nuke();
99                 $place = R::dispense( 'place' );
100                 $place->point = 'POINT(13.2 666.6)';
101                 $place->line = 'LINESTRING(9.2 0,3 1.33)';
102                 R::store( $place );
103                 $columns = R::inspect( 'place' );
104                 asrt( $columns['point'], 'point' );
105                 asrt( $columns['line'], 'linestring' );
106                 $place = R::findOne('place');
107                 asrt( $place->point, 'POINT(13.2 666.6)' );
108                 asrt( $place->line, 'LINESTRING(9.2 0,3 1.33)' );
109                 R::bindFunc( 'read', 'place.point', NULL );
110                 R::bindFunc( 'write', 'place.point', NULL );
111                 R::bindFunc( 'read', 'place.line', NULL );
112                 R::bindFunc( 'write', 'place.line', NULL );
113         }
114
115         /**
116          * Test scanning and coding of values.
117          *
118          * @return void
119          */
120         public function testScanningAndCoding()
121         {
122                 $toolbox = R::getToolBox();
123                 $adapter = $toolbox->getDatabaseAdapter();
124                 $writer  = $toolbox->getWriter();
125                 $redbean = $toolbox->getRedBean();
126                 $pdo     = $adapter->getDatabase();
127
128                 $a       = new AssociationManager( $toolbox );
129
130                 $adapter->exec( "DROP TABLE IF EXISTS testtable" );
131
132                 asrt( in_array( "testtable", $adapter->getCol( "show tables" ) ), FALSE );
133
134                 $writer->createTable( "testtable" );
135
136                 asrt( in_array( "testtable", $adapter->getCol( "show tables" ) ), TRUE );
137
138                 asrt( count( array_diff( $writer->getTables(), $adapter->getCol( "show tables" ) ) ), 0 );
139                 asrt( count( array_keys( $writer->getColumns( "testtable" ) ) ), 1 );
140
141                 asrt( in_array( "id", array_keys( $writer->getColumns( "testtable" ) ) ), TRUE );
142                 asrt( in_array( "c1", array_keys( $writer->getColumns( "testtable" ) ) ), FALSE );
143
144                 $writer->addColumn( "testtable", "c1", MySQL::C_DATATYPE_UINT32 );
145
146                 asrt( count( array_keys( $writer->getColumns( "testtable" ) ) ), 2 );
147
148                 asrt( in_array( "c1", array_keys( $writer->getColumns( "testtable" ) ) ), TRUE );
149
150                 foreach ( $writer->sqltype_typeno as $key => $type ) {
151                         if ( $type < 100 ) {
152                                 asrt( $writer->code( $key, TRUE ), $type );
153                         } else {
154                                 asrt( $writer->code( $key, TRUE ), MySQL::C_DATATYPE_SPECIFIED );
155                         }
156                 }
157
158                 asrt( $writer->code( MySQL::C_DATATYPE_SPECIAL_DATETIME ), MySQL::C_DATATYPE_SPECIFIED );
159
160                 asrt( $writer->code( "unknown" ), MySQL::C_DATATYPE_SPECIFIED );
161
162                 asrt( $writer->scanType( FALSE ), MySQL::C_DATATYPE_BOOL );
163                 asrt( $writer->scanType( TRUE ), MySQL::C_DATATYPE_BOOL );
164                 asrt( $writer->scanType( INF ), MySQL::C_DATATYPE_TEXT7 );
165
166                 asrt( $writer->scanType( NULL ), MySQL::C_DATATYPE_BOOL );
167
168                 asrt( $writer->scanType( 2 ), MySQL::C_DATATYPE_UINT32 );
169                 asrt( $writer->scanType( 255 ), MySQL::C_DATATYPE_UINT32 ); //no more uint8
170                 asrt( $writer->scanType( 256 ), MySQL::C_DATATYPE_UINT32 );
171
172                 asrt( $writer->scanType( -1 ), MySQL::C_DATATYPE_DOUBLE );
173                 asrt( $writer->scanType( 1.5 ), MySQL::C_DATATYPE_DOUBLE );
174
175                 asrt( $writer->scanType( "abc" ), MySQL::C_DATATYPE_TEXT7 );
176
177                 asrt( $writer->scanType( str_repeat( 'abcd', 100000 ) ), MySQL::C_DATATYPE_TEXT32 );
178
179                 asrt( $writer->scanType( "2001-10-10", TRUE ), MySQL::C_DATATYPE_SPECIAL_DATE );
180
181                 asrt( $writer->scanType( "2001-10-10 10:00:00", TRUE ), MySQL::C_DATATYPE_SPECIAL_DATETIME );
182
183                 asrt( $writer->scanType( "2001-10-10" ), MySQL::C_DATATYPE_TEXT7 );
184
185                 asrt( $writer->scanType( "2001-10-10 10:00:00" ), MySQL::C_DATATYPE_TEXT7 );
186
187                 asrt( $writer->scanType( "1.23", TRUE ), MySQL::C_DATATYPE_SPECIAL_MONEY );
188                 asrt( $writer->scanType( "12.23", TRUE ), MySQL::C_DATATYPE_SPECIAL_MONEY );
189                 asrt( $writer->scanType( "124.23", TRUE ), MySQL::C_DATATYPE_SPECIAL_MONEY );
190
191                 asrt( $writer->scanType( str_repeat( "lorem ipsum", 100 ) ), MySQL::C_DATATYPE_TEXT16 );
192
193                 $writer->widenColumn( "testtable", "c1", MySQL::C_DATATYPE_UINT32 );
194
195                 $writer->addColumn( "testtable", "special", MySQL::C_DATATYPE_SPECIAL_DATE );
196
197                 $cols = $writer->getColumns( "testtable" );
198
199                 asrt( $writer->code( $cols['special'], TRUE ), MySQL::C_DATATYPE_SPECIAL_DATE );
200
201                 asrt( $writer->code( $cols['special'], FALSE ), MySQL::C_DATATYPE_SPECIFIED );
202
203                 $writer->addColumn( "testtable", "special2", MySQL::C_DATATYPE_SPECIAL_DATETIME );
204
205                 $cols = $writer->getColumns( "testtable" );
206
207                 asrt( $writer->code( $cols['special2'], TRUE ), MySQL::C_DATATYPE_SPECIAL_DATETIME );
208
209                 asrt( $writer->code( $cols['special'], FALSE ), MySQL::C_DATATYPE_SPECIFIED );
210
211                 $cols = $writer->getColumns( "testtable" );
212
213                 asrt( $writer->code( $cols["c1"] ), MySQL::C_DATATYPE_UINT32 );
214
215                 $writer->widenColumn( "testtable", "c1", MySQL::C_DATATYPE_DOUBLE );
216
217                 $cols = $writer->getColumns( "testtable" );
218
219                 asrt( $writer->code( $cols["c1"] ), MySQL::C_DATATYPE_DOUBLE );
220
221                 $writer->widenColumn( "testtable", "c1", MySQL::C_DATATYPE_TEXT7 );
222
223                 $cols = $writer->getColumns( "testtable" );
224
225                 asrt( $writer->code( $cols["c1"] ), MySQL::C_DATATYPE_TEXT7 );
226
227                 $writer->widenColumn( "testtable", "c1", MySQL::C_DATATYPE_TEXT8 );
228
229                 $cols = $writer->getColumns( "testtable" );
230
231                 asrt( $writer->code( $cols["c1"] ), MySQL::C_DATATYPE_TEXT8 );
232
233                 $id  = $writer->updateRecord( "testtable", array( array( "property" => "c1", "value" => "lorem ipsum" ) ) );
234
235                 $row = $writer->queryRecord( "testtable", array( "id" => array( $id ) ) );
236
237                 asrt( $row[0]["c1"], "lorem ipsum" );
238
239                 $writer->updateRecord( "testtable", array( array( "property" => "c1", "value" => "ipsum lorem" ) ), $id );
240
241                 $row = $writer->queryRecord( "testtable", array( "id" => array( $id ) ) );
242
243                 asrt( $row[0]["c1"], "ipsum lorem" );
244
245                 $writer->deleteRecord( "testtable", array( "id" => array( $id ) ) );
246
247                 $row = $writer->queryRecord( "testtable", array( "id" => array( $id ) ) );
248
249                 asrt( empty( $row ), TRUE );
250
251                 $writer->addColumn( "testtable", "c2", MySQL::C_DATATYPE_UINT32 );
252         }
253
254         /**
255          * (FALSE should be stored as 0 not as '')
256          *
257          * @return voids
258          */
259         public function testZeroIssue()
260         {
261                 testpack( "Zero issue" );
262
263                 $toolbox = R::getToolBox();
264                 $redbean = $toolbox->getRedBean();
265                 $adapter = $toolbox->getDatabaseAdapter();
266                 $writer  = $toolbox->getWriter();
267                 $pdo     = $adapter->getDatabase();
268
269                 $pdo->Execute( "DROP TABLE IF EXISTS `zero`" );
270
271                 $bean        = $redbean->dispense( "zero" );
272
273                 $bean->zero  = FALSE;
274                 $bean->title = "bla";
275
276                 $redbean->store( $bean );
277
278                 asrt( count( $redbean->find( "zero", array(), " zero = 0 " ) ), 1 );
279
280                 R::store( R::dispense( 'hack' ) );
281
282                 testpack( "Test RedBean Security - bean interface " );
283
284                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
285
286                 $bean = $redbean->load( "page", "13; drop table hack" );
287
288                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
289                 try {
290                         $bean = $redbean->load( "page where 1; drop table hack", 1 );
291                 } catch (\Exception $e ) {
292                 }
293
294                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
295
296                 $bean     = $redbean->dispense( "page" );
297
298                 $evil     = "; drop table hack";
299
300                 $bean->id = $evil;
301
302                 try {
303                         $redbean->store( $bean );
304                 } catch (\Exception $e ) {
305                 }
306
307                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
308
309                 unset( $bean->id );
310
311                 $bean->name = "\"" . $evil;
312
313                 try {
314                         $redbean->store( $bean );
315                 } catch (\Exception $e ) {
316                 }
317
318                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
319
320                 $bean->name = "'" . $evil;
321
322                 try {
323                         $redbean->store( $bean );
324                 } catch (\Exception $e ) {
325                 }
326
327                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
328
329                 $bean->$evil = 1;
330
331                 try {
332                         $redbean->store( $bean );
333                 } catch (\Exception $e ) {
334                 }
335
336                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
337
338                 unset( $bean->$evil );
339
340                 $bean->id   = 1;
341                 $bean->name = "\"" . $evil;
342
343                 try {
344                         $redbean->store( $bean );
345                 } catch (\Exception $e ) {
346                 }
347
348                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
349
350                 $bean->name = "'" . $evil;
351
352                 try {
353                         $redbean->store( $bean );
354                 } catch (\Exception $e ) {
355                 }
356
357                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
358
359                 $bean->$evil = 1;
360
361                 try {
362                         $redbean->store( $bean );
363                 } catch (\Exception $e ) {
364                 }
365
366                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
367
368                 try {
369                         $redbean->trash( $bean );
370                 } catch (\Exception $e ) {
371                 }
372
373                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
374
375                 try {
376                         $redbean->find( "::", array(), "" );
377                 } catch (\Exception $e ) {
378                         pass();
379                 }
380
381                 $adapter->exec( "drop table if exists sometable" );
382
383                 testpack( "Test RedBean Security - query writer" );
384
385                 try {
386                         $writer->createTable( "sometable` ( `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT , PRIMARY KEY ( `id` ) ) ENGINE = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ; drop table hack; --" );
387                 } catch (\Exception $e ) {
388                 }
389
390                 asrt( in_array( "hack", $adapter->getCol( "show tables" ) ), TRUE );
391
392                 testpack( "Test ANSI92 issue in clearrelations" );
393
394                 $pdo->Execute( "DROP TABLE IF EXISTS book_group" );
395                 $pdo->Execute( "DROP TABLE IF EXISTS author_book" );
396                 $pdo->Execute( "DROP TABLE IF EXISTS book" );
397                 $pdo->Execute( "DROP TABLE IF EXISTS author" );
398
399                 $redbean       = $toolbox->getRedBean();
400
401                 $a             = new AssociationManager( $toolbox );
402
403                 $book          = $redbean->dispense( "book" );
404                 $author1       = $redbean->dispense( "author" );
405                 $author2       = $redbean->dispense( "author" );
406
407                 $book->title   = "My First Post";
408
409                 $author1->name = "Derek";
410                 $author2->name = "Whoever";
411
412                 set1toNAssoc( $a, $book, $author1 );
413                 set1toNAssoc( $a, $book, $author2 );
414
415                 pass();
416
417                 $pdo->Execute( "DROP TABLE IF EXISTS book_group" );
418                 $pdo->Execute( "DROP TABLE IF EXISTS book_author" );
419                 $pdo->Execute( "DROP TABLE IF EXISTS author_book" );
420                 $pdo->Execute( "DROP TABLE IF EXISTS book" );
421                 $pdo->Execute( "DROP TABLE IF EXISTS author" );
422
423                 $redbean       = $toolbox->getRedBean();
424
425                 $a             = new AssociationManager( $toolbox );
426
427                 $book          = $redbean->dispense( "book" );
428                 $author1       = $redbean->dispense( "author" );
429                 $author2       = $redbean->dispense( "author" );
430
431                 $book->title   = "My First Post";
432
433                 $author1->name = "Derek";
434                 $author2->name = "Whoever";
435
436                 $a->associate( $book, $author1 );
437                 $a->associate( $book, $author2 );
438
439                 pass();
440
441                 testpack( "Test Association Issue Group keyword (Issues 9 and 10)" );
442
443                 $pdo->Execute( "DROP TABLE IF EXISTS `book_group`" );
444                 $pdo->Execute( "DROP TABLE IF EXISTS `group`" );
445
446                 $group       = $redbean->dispense( "group" );
447
448                 $group->name = "mygroup";
449
450                 $redbean->store( $group );
451
452                 try {
453                         $a->associate( $group, $book );
454
455                         pass();
456                 } catch ( SQL $e ) {
457                         fail();
458                 }
459
460                 // Test issue SQL error 23000
461                 try {
462                         $a->associate( $group, $book );
463
464                         pass();
465                 } catch ( SQL $e ) {
466                         fail();
467                 }
468
469                 asrt( (int) $adapter->getCell( "select count(*) from book_group" ), 1 ); //just 1 rec!
470
471                 $pdo->Execute( "DROP TABLE IF EXISTS book_group" );
472                 $pdo->Execute( "DROP TABLE IF EXISTS author_book" );
473                 $pdo->Execute( "DROP TABLE IF EXISTS book" );
474                 $pdo->Execute( "DROP TABLE IF EXISTS author" );
475
476                 $redbean       = $toolbox->getRedBean();
477
478                 $a             = new AssociationManager( $toolbox );
479
480                 $book          = $redbean->dispense( "book" );
481                 $author1       = $redbean->dispense( "author" );
482                 $author2       = $redbean->dispense( "author" );
483
484                 $book->title   = "My First Post";
485
486                 $author1->name = "Derek";
487                 $author2->name = "Whoever";
488
489                 $a->unassociate( $book, $author1 );
490                 $a->unassociate( $book, $author2 );
491
492                 pass();
493
494                 $redbean->trash( $redbean->dispense( "bla" ) );
495
496                 pass();
497
498                 $bean       = $redbean->dispense( "bla" );
499
500                 $bean->name = 1;
501                 $bean->id   = 2;
502
503                 $redbean->trash( $bean );
504
505                 pass();
506         }
507
508         /**
509          * Test special data types.
510          *
511          * @return void
512          */
513         public function testTypes()
514         {
515                 testpack( 'Special data types' );
516
517                 $bean       = R::dispense( 'bean' );
518
519                 $bean->date = 'someday';
520
521                 R::store( $bean );
522
523                 $cols = R::getColumns( 'bean' );
524
525                 asrt( $cols['date'], 'varchar(191)' );
526
527                 $bean       = R::dispense( 'bean' );
528
529                 $bean->date = '2011-10-10';
530
531                 R::store( $bean );
532
533                 $cols = R::getColumns( 'bean' );
534
535                 asrt( $cols['date'], 'varchar(191)' );
536         }
537
538         /**
539          * Test date types.
540          *
541          * @return void
542          */
543         public function testTypesDates()
544         {
545                 $bean       = R::dispense( 'bean' );
546
547                 $bean->date = '2011-10-10';
548
549                 R::store( $bean );
550
551                 $cols = R::getColumns( 'bean' );
552
553                 asrt( $cols['date'], 'date' );
554         }
555
556         /**
557          * Test money types.
558          *
559          * @return void
560          */
561         public function testTypesMon()
562         {
563                 $bean       = R::dispense( 'bean' );
564
565                 $bean->amount = '22.99';
566
567                 R::store( $bean );
568
569                 $cols = R::getColumns( 'bean' );
570
571                 asrt( $cols['amount'], 'decimal(10,2)' );
572
573                 R::nuke();
574
575                 $bean       = R::dispense( 'bean' );
576
577                 $bean->amount = '-22.99';
578
579                 R::store( $bean );
580
581                 $cols = R::getColumns( 'bean' );
582
583                 asrt( $cols['amount'], 'decimal(10,2)' );
584         }
585
586
587         /**
588          * Date-time
589          *
590          * @return void
591          */
592         public function testTypesDateTimes()
593         {
594                 $bean       = R::dispense( 'bean' );
595
596                 $bean->date = '2011-10-10 10:00:00';
597
598                 R::store( $bean );
599
600                 $cols = R::getColumns( 'bean' );
601
602                 asrt( $cols['date'], 'datetime' );
603
604                 $bean = R::dispense( 'bean' );
605
606                 try {
607                         $bean        = R::dispense( 'bean' );
608
609                         $bean->title = 123;
610
611                         $bean->setMeta( 'cast.title', 'invalid' );
612
613                         R::store( $bean );
614
615                         fail();
616                 } catch ( RedException $e ) {
617                         pass();
618                 } catch (\Exception $e ) {
619                         fail();
620                 }
621
622                 $bean        = R::dispense( 'bean' );
623
624                 $bean->title = 123;
625
626                 $bean->setMeta( 'cast.title', 'text' );
627
628                 R::store( $bean );
629
630                 $cols = R::getColumns( 'bean' );
631
632                 asrt( $cols['title'], 'text' );
633
634                 R::nuke();
635
636                 $bean        = R::dispense( 'bean' );
637
638                 $bean->title = 123;
639
640                 $bean->setMeta( 'cast.title', 'string' );
641
642                 R::store( $bean );
643
644                 $cols = R::getColumns( 'bean' );
645
646                 asrt( $cols['title'], 'varchar(191)' );
647         }
648
649         /**
650          * Stored and reloads spatial data to see if the
651          * value is preserved correctly.
652          *
653          * @return void
654          */
655         protected function setGetSpatial( $data )
656         {
657                 R::nuke();
658
659                 $place           = R::dispense( 'place' );
660
661                 $place->location = $data;
662
663                 R::store( $place );
664
665                 asrt( R::getCell( 'SELECT AsText(location) FROM place LIMIT 1' ), $data );
666         }
667 }