Convert all array() syntax to []
[lhc/web/wiklou.git] / tests / phpunit / includes / db / DatabaseSQLTest.php
1 <?php
2
3 /**
4 * Test the abstract database layer
5 * This is a non DBMS depending test.
6 */
7 class DatabaseSQLTest extends MediaWikiTestCase {
8
9 /**
10 * @var DatabaseTestHelper
11 */
12 private $database;
13
14 protected function setUp() {
15 parent::setUp();
16 $this->database = new DatabaseTestHelper( __CLASS__ );
17 }
18
19 protected function assertLastSql( $sqlText ) {
20 $this->assertEquals(
21 $this->database->getLastSqls(),
22 $sqlText
23 );
24 }
25
26 /**
27 * @dataProvider provideSelect
28 * @covers DatabaseBase::select
29 */
30 public function testSelect( $sql, $sqlText ) {
31 $this->database->select(
32 $sql['tables'],
33 $sql['fields'],
34 isset( $sql['conds'] ) ? $sql['conds'] : [],
35 __METHOD__,
36 isset( $sql['options'] ) ? $sql['options'] : [],
37 isset( $sql['join_conds'] ) ? $sql['join_conds'] : []
38 );
39 $this->assertLastSql( $sqlText );
40 }
41
42 public static function provideSelect() {
43 return [
44 [
45 [
46 'tables' => 'table',
47 'fields' => [ 'field', 'alias' => 'field2' ],
48 'conds' => [ 'alias' => 'text' ],
49 ],
50 "SELECT field,field2 AS alias " .
51 "FROM table " .
52 "WHERE alias = 'text'"
53 ],
54 [
55 [
56 'tables' => 'table',
57 'fields' => [ 'field', 'alias' => 'field2' ],
58 'conds' => [ 'alias' => 'text' ],
59 'options' => [ 'LIMIT' => 1, 'ORDER BY' => 'field' ],
60 ],
61 "SELECT field,field2 AS alias " .
62 "FROM table " .
63 "WHERE alias = 'text' " .
64 "ORDER BY field " .
65 "LIMIT 1"
66 ],
67 [
68 [
69 'tables' => [ 'table', 't2' => 'table2' ],
70 'fields' => [ 'tid', 'field', 'alias' => 'field2', 't2.id' ],
71 'conds' => [ 'alias' => 'text' ],
72 'options' => [ 'LIMIT' => 1, 'ORDER BY' => 'field' ],
73 'join_conds' => [ 't2' => [
74 'LEFT JOIN', 'tid = t2.id'
75 ] ],
76 ],
77 "SELECT tid,field,field2 AS alias,t2.id " .
78 "FROM table LEFT JOIN table2 t2 ON ((tid = t2.id)) " .
79 "WHERE alias = 'text' " .
80 "ORDER BY field " .
81 "LIMIT 1"
82 ],
83 [
84 [
85 'tables' => [ 'table', 't2' => 'table2' ],
86 'fields' => [ 'tid', 'field', 'alias' => 'field2', 't2.id' ],
87 'conds' => [ 'alias' => 'text' ],
88 'options' => [ 'LIMIT' => 1, 'GROUP BY' => 'field', 'HAVING' => 'COUNT(*) > 1' ],
89 'join_conds' => [ 't2' => [
90 'LEFT JOIN', 'tid = t2.id'
91 ] ],
92 ],
93 "SELECT tid,field,field2 AS alias,t2.id " .
94 "FROM table LEFT JOIN table2 t2 ON ((tid = t2.id)) " .
95 "WHERE alias = 'text' " .
96 "GROUP BY field HAVING COUNT(*) > 1 " .
97 "LIMIT 1"
98 ],
99 [
100 [
101 'tables' => [ 'table', 't2' => 'table2' ],
102 'fields' => [ 'tid', 'field', 'alias' => 'field2', 't2.id' ],
103 'conds' => [ 'alias' => 'text' ],
104 'options' => [
105 'LIMIT' => 1,
106 'GROUP BY' => [ 'field', 'field2' ],
107 'HAVING' => [ 'COUNT(*) > 1', 'field' => 1 ]
108 ],
109 'join_conds' => [ 't2' => [
110 'LEFT JOIN', 'tid = t2.id'
111 ] ],
112 ],
113 "SELECT tid,field,field2 AS alias,t2.id " .
114 "FROM table LEFT JOIN table2 t2 ON ((tid = t2.id)) " .
115 "WHERE alias = 'text' " .
116 "GROUP BY field,field2 HAVING (COUNT(*) > 1) AND field = '1' " .
117 "LIMIT 1"
118 ],
119 [
120 [
121 'tables' => [ 'table' ],
122 'fields' => [ 'alias' => 'field' ],
123 'conds' => [ 'alias' => [ 1, 2, 3, 4 ] ],
124 ],
125 "SELECT field AS alias " .
126 "FROM table " .
127 "WHERE alias IN ('1','2','3','4')"
128 ],
129 ];
130 }
131
132 /**
133 * @dataProvider provideUpdate
134 * @covers DatabaseBase::update
135 */
136 public function testUpdate( $sql, $sqlText ) {
137 $this->database->update(
138 $sql['table'],
139 $sql['values'],
140 $sql['conds'],
141 __METHOD__,
142 isset( $sql['options'] ) ? $sql['options'] : []
143 );
144 $this->assertLastSql( $sqlText );
145 }
146
147 public static function provideUpdate() {
148 return [
149 [
150 [
151 'table' => 'table',
152 'values' => [ 'field' => 'text', 'field2' => 'text2' ],
153 'conds' => [ 'alias' => 'text' ],
154 ],
155 "UPDATE table " .
156 "SET field = 'text'" .
157 ",field2 = 'text2' " .
158 "WHERE alias = 'text'"
159 ],
160 [
161 [
162 'table' => 'table',
163 'values' => [ 'field = other', 'field2' => 'text2' ],
164 'conds' => [ 'id' => '1' ],
165 ],
166 "UPDATE table " .
167 "SET field = other" .
168 ",field2 = 'text2' " .
169 "WHERE id = '1'"
170 ],
171 [
172 [
173 'table' => 'table',
174 'values' => [ 'field = other', 'field2' => 'text2' ],
175 'conds' => '*',
176 ],
177 "UPDATE table " .
178 "SET field = other" .
179 ",field2 = 'text2'"
180 ],
181 ];
182 }
183
184 /**
185 * @dataProvider provideDelete
186 * @covers DatabaseBase::delete
187 */
188 public function testDelete( $sql, $sqlText ) {
189 $this->database->delete(
190 $sql['table'],
191 $sql['conds'],
192 __METHOD__
193 );
194 $this->assertLastSql( $sqlText );
195 }
196
197 public static function provideDelete() {
198 return [
199 [
200 [
201 'table' => 'table',
202 'conds' => [ 'alias' => 'text' ],
203 ],
204 "DELETE FROM table " .
205 "WHERE alias = 'text'"
206 ],
207 [
208 [
209 'table' => 'table',
210 'conds' => '*',
211 ],
212 "DELETE FROM table"
213 ],
214 ];
215 }
216
217 /**
218 * @dataProvider provideUpsert
219 * @covers DatabaseBase::upsert
220 */
221 public function testUpsert( $sql, $sqlText ) {
222 $this->database->upsert(
223 $sql['table'],
224 $sql['rows'],
225 $sql['uniqueIndexes'],
226 $sql['set'],
227 __METHOD__
228 );
229 $this->assertLastSql( $sqlText );
230 }
231
232 public static function provideUpsert() {
233 return [
234 [
235 [
236 'table' => 'upsert_table',
237 'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
238 'uniqueIndexes' => [ 'field' ],
239 'set' => [ 'field' => 'set' ],
240 ],
241 "BEGIN; " .
242 "UPDATE upsert_table " .
243 "SET field = 'set' " .
244 "WHERE ((field = 'text')); " .
245 "INSERT IGNORE INTO upsert_table " .
246 "(field,field2) " .
247 "VALUES ('text','text2'); " .
248 "COMMIT"
249 ],
250 ];
251 }
252
253 /**
254 * @dataProvider provideDeleteJoin
255 * @covers DatabaseBase::deleteJoin
256 */
257 public function testDeleteJoin( $sql, $sqlText ) {
258 $this->database->deleteJoin(
259 $sql['delTable'],
260 $sql['joinTable'],
261 $sql['delVar'],
262 $sql['joinVar'],
263 $sql['conds'],
264 __METHOD__
265 );
266 $this->assertLastSql( $sqlText );
267 }
268
269 public static function provideDeleteJoin() {
270 return [
271 [
272 [
273 'delTable' => 'table',
274 'joinTable' => 'table_join',
275 'delVar' => 'field',
276 'joinVar' => 'field_join',
277 'conds' => [ 'alias' => 'text' ],
278 ],
279 "DELETE FROM table " .
280 "WHERE field IN (" .
281 "SELECT field_join FROM table_join WHERE alias = 'text'" .
282 ")"
283 ],
284 [
285 [
286 'delTable' => 'table',
287 'joinTable' => 'table_join',
288 'delVar' => 'field',
289 'joinVar' => 'field_join',
290 'conds' => '*',
291 ],
292 "DELETE FROM table " .
293 "WHERE field IN (" .
294 "SELECT field_join FROM table_join " .
295 ")"
296 ],
297 ];
298 }
299
300 /**
301 * @dataProvider provideInsert
302 * @covers DatabaseBase::insert
303 */
304 public function testInsert( $sql, $sqlText ) {
305 $this->database->insert(
306 $sql['table'],
307 $sql['rows'],
308 __METHOD__,
309 isset( $sql['options'] ) ? $sql['options'] : []
310 );
311 $this->assertLastSql( $sqlText );
312 }
313
314 public static function provideInsert() {
315 return [
316 [
317 [
318 'table' => 'table',
319 'rows' => [ 'field' => 'text', 'field2' => 2 ],
320 ],
321 "INSERT INTO table " .
322 "(field,field2) " .
323 "VALUES ('text','2')"
324 ],
325 [
326 [
327 'table' => 'table',
328 'rows' => [ 'field' => 'text', 'field2' => 2 ],
329 'options' => 'IGNORE',
330 ],
331 "INSERT IGNORE INTO table " .
332 "(field,field2) " .
333 "VALUES ('text','2')"
334 ],
335 [
336 [
337 'table' => 'table',
338 'rows' => [
339 [ 'field' => 'text', 'field2' => 2 ],
340 [ 'field' => 'multi', 'field2' => 3 ],
341 ],
342 'options' => 'IGNORE',
343 ],
344 "INSERT IGNORE INTO table " .
345 "(field,field2) " .
346 "VALUES " .
347 "('text','2')," .
348 "('multi','3')"
349 ],
350 ];
351 }
352
353 /**
354 * @dataProvider provideInsertSelect
355 * @covers DatabaseBase::insertSelect
356 */
357 public function testInsertSelect( $sql, $sqlText ) {
358 $this->database->insertSelect(
359 $sql['destTable'],
360 $sql['srcTable'],
361 $sql['varMap'],
362 $sql['conds'],
363 __METHOD__,
364 isset( $sql['insertOptions'] ) ? $sql['insertOptions'] : [],
365 isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : []
366 );
367 $this->assertLastSql( $sqlText );
368 }
369
370 public static function provideInsertSelect() {
371 return [
372 [
373 [
374 'destTable' => 'insert_table',
375 'srcTable' => 'select_table',
376 'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
377 'conds' => '*',
378 ],
379 "INSERT INTO insert_table " .
380 "(field_insert,field) " .
381 "SELECT field_select,field2 " .
382 "FROM select_table"
383 ],
384 [
385 [
386 'destTable' => 'insert_table',
387 'srcTable' => 'select_table',
388 'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
389 'conds' => [ 'field' => 2 ],
390 ],
391 "INSERT INTO insert_table " .
392 "(field_insert,field) " .
393 "SELECT field_select,field2 " .
394 "FROM select_table " .
395 "WHERE field = '2'"
396 ],
397 [
398 [
399 'destTable' => 'insert_table',
400 'srcTable' => 'select_table',
401 'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
402 'conds' => [ 'field' => 2 ],
403 'insertOptions' => 'IGNORE',
404 'selectOptions' => [ 'ORDER BY' => 'field' ],
405 ],
406 "INSERT IGNORE INTO insert_table " .
407 "(field_insert,field) " .
408 "SELECT field_select,field2 " .
409 "FROM select_table " .
410 "WHERE field = '2' " .
411 "ORDER BY field"
412 ],
413 ];
414 }
415
416 /**
417 * @dataProvider provideReplace
418 * @covers DatabaseBase::replace
419 */
420 public function testReplace( $sql, $sqlText ) {
421 $this->database->replace(
422 $sql['table'],
423 $sql['uniqueIndexes'],
424 $sql['rows'],
425 __METHOD__
426 );
427 $this->assertLastSql( $sqlText );
428 }
429
430 public static function provideReplace() {
431 return [
432 [
433 [
434 'table' => 'replace_table',
435 'uniqueIndexes' => [ 'field' ],
436 'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
437 ],
438 "DELETE FROM replace_table " .
439 "WHERE ( field='text' ); " .
440 "INSERT INTO replace_table " .
441 "(field,field2) " .
442 "VALUES ('text','text2')"
443 ],
444 [
445 [
446 'table' => 'module_deps',
447 'uniqueIndexes' => [ [ 'md_module', 'md_skin' ] ],
448 'rows' => [
449 'md_module' => 'module',
450 'md_skin' => 'skin',
451 'md_deps' => 'deps',
452 ],
453 ],
454 "DELETE FROM module_deps " .
455 "WHERE ( md_module='module' AND md_skin='skin' ); " .
456 "INSERT INTO module_deps " .
457 "(md_module,md_skin,md_deps) " .
458 "VALUES ('module','skin','deps')"
459 ],
460 [
461 [
462 'table' => 'module_deps',
463 'uniqueIndexes' => [ [ 'md_module', 'md_skin' ] ],
464 'rows' => [
465 [
466 'md_module' => 'module',
467 'md_skin' => 'skin',
468 'md_deps' => 'deps',
469 ], [
470 'md_module' => 'module2',
471 'md_skin' => 'skin2',
472 'md_deps' => 'deps2',
473 ],
474 ],
475 ],
476 "DELETE FROM module_deps " .
477 "WHERE ( md_module='module' AND md_skin='skin' ); " .
478 "INSERT INTO module_deps " .
479 "(md_module,md_skin,md_deps) " .
480 "VALUES ('module','skin','deps'); " .
481 "DELETE FROM module_deps " .
482 "WHERE ( md_module='module2' AND md_skin='skin2' ); " .
483 "INSERT INTO module_deps " .
484 "(md_module,md_skin,md_deps) " .
485 "VALUES ('module2','skin2','deps2')"
486 ],
487 [
488 [
489 'table' => 'module_deps',
490 'uniqueIndexes' => [ 'md_module', 'md_skin' ],
491 'rows' => [
492 [
493 'md_module' => 'module',
494 'md_skin' => 'skin',
495 'md_deps' => 'deps',
496 ], [
497 'md_module' => 'module2',
498 'md_skin' => 'skin2',
499 'md_deps' => 'deps2',
500 ],
501 ],
502 ],
503 "DELETE FROM module_deps " .
504 "WHERE ( md_module='module' ) OR ( md_skin='skin' ); " .
505 "INSERT INTO module_deps " .
506 "(md_module,md_skin,md_deps) " .
507 "VALUES ('module','skin','deps'); " .
508 "DELETE FROM module_deps " .
509 "WHERE ( md_module='module2' ) OR ( md_skin='skin2' ); " .
510 "INSERT INTO module_deps " .
511 "(md_module,md_skin,md_deps) " .
512 "VALUES ('module2','skin2','deps2')"
513 ],
514 [
515 [
516 'table' => 'module_deps',
517 'uniqueIndexes' => [],
518 'rows' => [
519 'md_module' => 'module',
520 'md_skin' => 'skin',
521 'md_deps' => 'deps',
522 ],
523 ],
524 "INSERT INTO module_deps " .
525 "(md_module,md_skin,md_deps) " .
526 "VALUES ('module','skin','deps')"
527 ],
528 ];
529 }
530
531 /**
532 * @dataProvider provideNativeReplace
533 * @covers DatabaseBase::nativeReplace
534 */
535 public function testNativeReplace( $sql, $sqlText ) {
536 $this->database->nativeReplace(
537 $sql['table'],
538 $sql['rows'],
539 __METHOD__
540 );
541 $this->assertLastSql( $sqlText );
542 }
543
544 public static function provideNativeReplace() {
545 return [
546 [
547 [
548 'table' => 'replace_table',
549 'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
550 ],
551 "REPLACE INTO replace_table " .
552 "(field,field2) " .
553 "VALUES ('text','text2')"
554 ],
555 ];
556 }
557
558 /**
559 * @dataProvider provideConditional
560 * @covers DatabaseBase::conditional
561 */
562 public function testConditional( $sql, $sqlText ) {
563 $this->assertEquals( trim( $this->database->conditional(
564 $sql['conds'],
565 $sql['true'],
566 $sql['false']
567 ) ), $sqlText );
568 }
569
570 public static function provideConditional() {
571 return [
572 [
573 [
574 'conds' => [ 'field' => 'text' ],
575 'true' => 1,
576 'false' => 'NULL',
577 ],
578 "(CASE WHEN field = 'text' THEN 1 ELSE NULL END)"
579 ],
580 [
581 [
582 'conds' => [ 'field' => 'text', 'field2' => 'anothertext' ],
583 'true' => 1,
584 'false' => 'NULL',
585 ],
586 "(CASE WHEN field = 'text' AND field2 = 'anothertext' THEN 1 ELSE NULL END)"
587 ],
588 [
589 [
590 'conds' => 'field=1',
591 'true' => 1,
592 'false' => 'NULL',
593 ],
594 "(CASE WHEN field=1 THEN 1 ELSE NULL END)"
595 ],
596 ];
597 }
598
599 /**
600 * @dataProvider provideBuildConcat
601 * @covers DatabaseBase::buildConcat
602 */
603 public function testBuildConcat( $stringList, $sqlText ) {
604 $this->assertEquals( trim( $this->database->buildConcat(
605 $stringList
606 ) ), $sqlText );
607 }
608
609 public static function provideBuildConcat() {
610 return [
611 [
612 [ 'field', 'field2' ],
613 "CONCAT(field,field2)"
614 ],
615 [
616 [ "'test'", 'field2' ],
617 "CONCAT('test',field2)"
618 ],
619 ];
620 }
621
622 /**
623 * @dataProvider provideBuildLike
624 * @covers DatabaseBase::buildLike
625 */
626 public function testBuildLike( $array, $sqlText ) {
627 $this->assertEquals( trim( $this->database->buildLike(
628 $array
629 ) ), $sqlText );
630 }
631
632 public static function provideBuildLike() {
633 return [
634 [
635 'text',
636 "LIKE 'text'"
637 ],
638 [
639 [ 'text', new LikeMatch( '%' ) ],
640 "LIKE 'text%'"
641 ],
642 [
643 [ 'text', new LikeMatch( '%' ), 'text2' ],
644 "LIKE 'text%text2'"
645 ],
646 [
647 [ 'text', new LikeMatch( '_' ) ],
648 "LIKE 'text_'"
649 ],
650 ];
651 }
652
653 /**
654 * @dataProvider provideUnionQueries
655 * @covers DatabaseBase::unionQueries
656 */
657 public function testUnionQueries( $sql, $sqlText ) {
658 $this->assertEquals( trim( $this->database->unionQueries(
659 $sql['sqls'],
660 $sql['all']
661 ) ), $sqlText );
662 }
663
664 public static function provideUnionQueries() {
665 return [
666 [
667 [
668 'sqls' => [ 'RAW SQL', 'RAW2SQL' ],
669 'all' => true,
670 ],
671 "(RAW SQL) UNION ALL (RAW2SQL)"
672 ],
673 [
674 [
675 'sqls' => [ 'RAW SQL', 'RAW2SQL' ],
676 'all' => false,
677 ],
678 "(RAW SQL) UNION (RAW2SQL)"
679 ],
680 [
681 [
682 'sqls' => [ 'RAW SQL', 'RAW2SQL', 'RAW3SQL' ],
683 'all' => false,
684 ],
685 "(RAW SQL) UNION (RAW2SQL) UNION (RAW3SQL)"
686 ],
687 ];
688 }
689
690 /**
691 * @covers DatabaseBase::commit
692 */
693 public function testTransactionCommit() {
694 $this->database->begin( __METHOD__ );
695 $this->database->commit( __METHOD__ );
696 $this->assertLastSql( 'BEGIN; COMMIT' );
697 }
698
699 /**
700 * @covers DatabaseBase::rollback
701 */
702 public function testTransactionRollback() {
703 $this->database->begin( __METHOD__ );
704 $this->database->rollback( __METHOD__ );
705 $this->assertLastSql( 'BEGIN; ROLLBACK' );
706 }
707
708 /**
709 * @covers DatabaseBase::dropTable
710 */
711 public function testDropTable() {
712 $this->database->setExistingTables( [ 'table' ] );
713 $this->database->dropTable( 'table', __METHOD__ );
714 $this->assertLastSql( 'DROP TABLE table' );
715 }
716
717 /**
718 * @covers DatabaseBase::dropTable
719 */
720 public function testDropNonExistingTable() {
721 $this->assertFalse(
722 $this->database->dropTable( 'non_existing', __METHOD__ )
723 );
724 }
725
726 /**
727 * @dataProvider provideMakeList
728 * @covers DatabaseBase::makeList
729 */
730 public function testMakeList( $list, $mode, $sqlText ) {
731 $this->assertEquals( trim( $this->database->makeList(
732 $list, $mode
733 ) ), $sqlText );
734 }
735
736 public static function provideMakeList() {
737 return [
738 [
739 [ 'value', 'value2' ],
740 LIST_COMMA,
741 "'value','value2'"
742 ],
743 [
744 [ 'field', 'field2' ],
745 LIST_NAMES,
746 "field,field2"
747 ],
748 [
749 [ 'field' => 'value', 'field2' => 'value2' ],
750 LIST_AND,
751 "field = 'value' AND field2 = 'value2'"
752 ],
753 [
754 [ 'field' => null, "field2 != 'value2'" ],
755 LIST_AND,
756 "field IS NULL AND (field2 != 'value2')"
757 ],
758 [
759 [ 'field' => [ 'value', null, 'value2' ], 'field2' => 'value2' ],
760 LIST_AND,
761 "(field IN ('value','value2') OR field IS NULL) AND field2 = 'value2'"
762 ],
763 [
764 [ 'field' => [ null ], 'field2' => null ],
765 LIST_AND,
766 "field IS NULL AND field2 IS NULL"
767 ],
768 [
769 [ 'field' => 'value', 'field2' => 'value2' ],
770 LIST_OR,
771 "field = 'value' OR field2 = 'value2'"
772 ],
773 [
774 [ 'field' => 'value', 'field2' => null ],
775 LIST_OR,
776 "field = 'value' OR field2 IS NULL"
777 ],
778 [
779 [ 'field' => [ 'value', 'value2' ], 'field2' => [ 'value' ] ],
780 LIST_OR,
781 "field IN ('value','value2') OR field2 = 'value'"
782 ],
783 [
784 [ 'field' => [ null, 'value', null, 'value2' ], "field2 != 'value2'" ],
785 LIST_OR,
786 "(field IN ('value','value2') OR field IS NULL) OR (field2 != 'value2')"
787 ],
788 [
789 [ 'field' => 'value', 'field2' => 'value2' ],
790 LIST_SET,
791 "field = 'value',field2 = 'value2'"
792 ],
793 [
794 [ 'field' => 'value', 'field2' => null ],
795 LIST_SET,
796 "field = 'value',field2 = NULL"
797 ],
798 [
799 [ 'field' => 'value', "field2 != 'value2'" ],
800 LIST_SET,
801 "field = 'value',field2 != 'value2'"
802 ],
803 ];
804 }
805 }