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