Completed
Push — master ( fe927f...32a670 )
by Robbie
21:23 queued 12:16
created

SQLSelectTest::testBaseTableAliases()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 33
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 22
nc 1
nop 0
dl 0
loc 33
rs 9.568
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use SilverStripe\ORM\DB;
6
use SilverStripe\ORM\Connect\MySQLDatabase;
7
use SilverStripe\ORM\Queries\SQLSelect;
8
use SilverStripe\SQLite\SQLite3Database;
0 ignored issues
show
Bug introduced by
The type SilverStripe\SQLite\SQLite3Database was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use SilverStripe\PostgreSQL\PostgreSQLDatabase;
0 ignored issues
show
Bug introduced by
The type SilverStripe\PostgreSQL\PostgreSQLDatabase was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use SilverStripe\Dev\Deprecation;
11
use SilverStripe\Dev\SapphireTest;
12
13
class SQLSelectTest extends SapphireTest
14
{
15
16
    protected static $fixture_file = 'SQLSelectTest.yml';
17
18
    protected static $extra_dataobjects = array(
19
        SQLSelectTest\TestObject::class,
20
        SQLSelectTest\TestBase::class,
21
        SQLSelectTest\TestChild::class
22
    );
23
24
    protected $oldDeprecation = null;
25
26
    protected function setUp()
27
    {
28
        parent::setUp();
29
        $this->oldDeprecation = Deprecation::dump_settings();
30
    }
31
32
    protected function tearDown()
33
    {
34
        Deprecation::restore_settings($this->oldDeprecation);
35
        parent::tearDown();
36
    }
37
38
    public function testCount()
39
    {
40
41
        //basic counting
42
        $qry = SQLSelectTest\TestObject::get()->dataQuery()->getFinalisedQuery();
43
        $ids = $this->allFixtureIDs(SQLSelectTest\TestObject::class);
44
        $count = $qry->count('"SQLSelectTest_DO"."ID"');
45
        $this->assertEquals(count($ids), $count);
46
        $this->assertInternalType("int", $count);
47
        //test with `having`
48
        if (DB::get_conn() instanceof MySQLDatabase) {
49
            $qry->setSelect(array(
50
                'Date' => 'MAX("Date")',
51
                'Common' => '"Common"',
52
            ));
53
            $qry->setGroupBy('"Common"');
54
            $qry->setHaving('"Date" > 2012-02-01');
55
            $count = $qry->count('"SQLSelectTest_DO"."ID"');
56
            $this->assertEquals(1, $count);
57
            $this->assertInternalType("int", $count);
58
        }
59
    }
60
    public function testUnlimitedRowCount()
61
    {
62
        //basic counting
63
        $qry = SQLSelectTest\TestObject::get()->dataQuery()->getFinalisedQuery();
64
        $ids = $this->allFixtureIDs(SQLSelectTest\TestObject::class);
65
        $qry->setLimit(1);
66
        $count = $qry->unlimitedRowCount('"SQLSelectTest_DO"."ID"');
67
        $this->assertEquals(count($ids), $count);
68
        $this->assertInternalType("int", $count);
69
        // Test without column - SQLSelect has different logic for this
70
        $count = $qry->unlimitedRowCount();
71
        $this->assertEquals(2, $count);
72
        $this->assertInternalType("int", $count);
73
        //test with `having`
74
        if (DB::get_conn() instanceof MySQLDatabase) {
75
            $qry->setHaving('"Date" > 2012-02-01');
76
            $count = $qry->unlimitedRowCount('"SQLSelectTest_DO"."ID"');
77
            $this->assertEquals(1, $count);
78
            $this->assertInternalType("int", $count);
79
        }
80
    }
81
82
    public function testEmptyQueryReturnsNothing()
83
    {
84
        $query = new SQLSelect();
85
        $this->assertSQLEquals('', $query->sql($parameters));
86
    }
87
88
    public function testSelectFromBasicTable()
89
    {
90
        $query = new SQLSelect();
91
        $query->setFrom('MyTable');
92
        $this->assertSQLEquals("SELECT * FROM MyTable", $query->sql($parameters));
93
        $query->addFrom('MyJoin');
94
        $this->assertSQLEquals("SELECT * FROM MyTable MyJoin", $query->sql($parameters));
95
    }
96
97
    public function testSelectFromUserSpecifiedFields()
98
    {
99
        $query = new SQLSelect();
100
        $query->setSelect(array("Name", "Title", "Description"));
101
        $query->setFrom("MyTable");
102
        $this->assertSQLEquals("SELECT Name, Title, Description FROM MyTable", $query->sql($parameters));
103
    }
104
105
    public function testSelectWithWhereClauseFilter()
106
    {
107
        $query = new SQLSelect();
108
        $query->setSelect(array("Name","Meta"));
109
        $query->setFrom("MyTable");
110
        $query->setWhere("Name = 'Name'");
111
        $query->addWhere("Meta = 'Test'");
112
        $this->assertSQLEquals(
113
            "SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test')",
114
            $query->sql($parameters)
115
        );
116
    }
117
118
    public function testSelectWithConstructorParameters()
119
    {
120
        $query = new SQLSelect(array("Foo", "Bar"), "FooBarTable");
121
        $this->assertSQLEquals("SELECT Foo, Bar FROM FooBarTable", $query->sql($parameters));
122
        $query = new SQLSelect(array("Foo", "Bar"), "FooBarTable", array("Foo = 'Boo'"));
123
        $this->assertSQLEquals("SELECT Foo, Bar FROM FooBarTable WHERE (Foo = 'Boo')", $query->sql($parameters));
124
    }
125
126
    public function testSelectWithChainedMethods()
127
    {
128
        $query = new SQLSelect();
129
        $query->setSelect("Name", "Meta")->setFrom("MyTable")->setWhere("Name = 'Name'")->addWhere("Meta = 'Test'");
130
        $this->assertSQLEquals(
131
            "SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test')",
132
            $query->sql($parameters)
133
        );
134
    }
135
136
    public function testCanSortBy()
137
    {
138
        $query = new SQLSelect();
139
        $query->setSelect("Name", "Meta")->setFrom("MyTable")->setWhere("Name = 'Name'")->addWhere("Meta = 'Test'");
140
        $this->assertTrue($query->canSortBy('Name ASC'));
141
        $this->assertTrue($query->canSortBy('Name'));
142
    }
143
144
    /**
145
     * Test multiple order by SQL clauses.
146
     */
147
    public function testAddOrderBy()
148
    {
149
        $query = new SQLSelect();
150
        $query->setSelect('ID', "Title")
151
            ->setFrom('Page')
152
            ->addOrderBy('(ID % 2)  = 0', 'ASC')
153
            ->addOrderBy('ID > 50', 'ASC');
154
        $this->assertSQLEquals(
155
            'SELECT ID, Title, (ID % 2)  = 0 AS "_SortColumn0", ID > 50 AS "_SortColumn1"'
156
            . ' FROM Page ORDER BY "_SortColumn0" ASC, "_SortColumn1" ASC',
157
            $query->sql($parameters)
158
        );
159
    }
160
161
    public function testSelectWithChainedFilterParameters()
162
    {
163
        $query = new SQLSelect();
164
        $query->setSelect(array("Name","Meta"))->setFrom("MyTable");
165
        $query->setWhere("Name = 'Name'")->addWhere("Meta = 'Test'")->addWhere("Beta != 'Gamma'");
166
        $this->assertSQLEquals(
167
            "SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test') AND (Beta != 'Gamma')",
168
            $query->sql($parameters)
169
        );
170
    }
171
172
    public function testSelectWithLimitClause()
173
    {
174
        if (!(DB::get_conn() instanceof MySQLDatabase
175
            || DB::get_conn() instanceof SQLite3Database
176
            || DB::get_conn() instanceof PostgreSQLDatabase)
177
        ) {
178
            $this->markTestSkipped('This test requires either a MySQL, SQLite or PostgreSQL database');
179
        }
180
181
        $query = new SQLSelect();
182
        $query->setFrom("MyTable");
183
        $query->setLimit(99);
184
        $this->assertSQLEquals("SELECT * FROM MyTable LIMIT 99", $query->sql($parameters));
185
186
        // array limit with start (MySQL specific)
187
        $query = new SQLSelect();
188
        $query->setFrom("MyTable");
189
        $query->setLimit(99, 97);
190
        $this->assertSQLEquals("SELECT * FROM MyTable LIMIT 99 OFFSET 97", $query->sql($parameters));
191
    }
192
193
    public function testSelectWithOrderbyClause()
194
    {
195
        $query = new SQLSelect();
196
        $query->setFrom("MyTable");
197
        $query->setOrderBy('MyName');
198
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName ASC', $query->sql($parameters));
199
200
        $query = new SQLSelect();
201
        $query->setFrom("MyTable");
202
        $query->setOrderBy('MyName desc');
203
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName DESC', $query->sql($parameters));
204
205
        $query = new SQLSelect();
206
        $query->setFrom("MyTable");
207
        $query->setOrderBy('MyName ASC, Color DESC');
208
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName ASC, Color DESC', $query->sql($parameters));
209
210
        $query = new SQLSelect();
211
        $query->setFrom("MyTable");
212
        $query->setOrderBy('MyName ASC, Color');
213
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName ASC, Color ASC', $query->sql($parameters));
214
215
        $query = new SQLSelect();
216
        $query->setFrom("MyTable");
217
        $query->setOrderBy(array('MyName' => 'desc'));
218
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName DESC', $query->sql($parameters));
219
220
        $query = new SQLSelect();
221
        $query->setFrom("MyTable");
222
        $query->setOrderBy(array('MyName' => 'desc', 'Color'));
223
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName DESC, Color ASC', $query->sql($parameters));
224
225
        $query = new SQLSelect();
226
        $query->setFrom("MyTable");
227
        $query->setOrderBy('implode("MyName","Color")');
228
        $this->assertSQLEquals(
229
            'SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',
230
            $query->sql($parameters)
231
        );
232
233
        $query = new SQLSelect();
234
        $query->setFrom("MyTable");
235
        $query->setOrderBy('implode("MyName","Color") DESC');
236
        $this->assertSQLEquals(
237
            'SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" DESC',
238
            $query->sql($parameters)
239
        );
240
241
        $query = new SQLSelect();
242
        $query->setFrom("MyTable");
243
        $query->setOrderBy('RAND()');
244
        $this->assertSQLEquals(
245
            'SELECT *, RAND() AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',
246
            $query->sql($parameters)
247
        );
248
249
        $query = new SQLSelect();
250
        $query->setFrom("MyTable");
251
        $query->addFrom('INNER JOIN SecondTable USING (ID)');
252
        $query->addFrom('INNER JOIN ThirdTable USING (ID)');
253
        $query->setOrderBy('MyName');
254
        $this->assertSQLEquals(
255
            'SELECT * FROM MyTable '
256
            . 'INNER JOIN SecondTable USING (ID) '
257
            . 'INNER JOIN ThirdTable USING (ID) '
258
            . 'ORDER BY MyName ASC',
259
            $query->sql($parameters)
260
        );
261
    }
262
263
    public function testNullLimit()
264
    {
265
        $query = new SQLSelect();
266
        $query->setFrom("MyTable");
267
        $query->setLimit(null);
268
269
        $this->assertSQLEquals(
270
            'SELECT * FROM MyTable',
271
            $query->sql($parameters)
272
        );
273
    }
274
275
    /**
276
     * @expectedException \PHPUnit\Framework\Error\Error
277
     */
278
    public function testZeroLimit()
279
    {
280
        Deprecation::notification_version('4.3.0');
281
        $query = new SQLSelect();
282
        $query->setFrom("MyTable");
283
        $query->setLimit(0);
284
    }
285
286
    /**
287
     * @expectedException \PHPUnit\Framework\Error\Error
288
     */
289
    public function testZeroLimitWithOffset()
290
    {
291
        Deprecation::notification_version('4.3.0');
292
        if (!(DB::get_conn() instanceof MySQLDatabase
293
            || DB::get_conn() instanceof SQLite3Database
294
            || DB::get_conn() instanceof PostgreSQLDatabase
295
        )) {
296
            $this->markTestSkipped('This test requires either a MySQL, SQLite or PostgreSQL database');
297
        }
298
299
        $query = new SQLSelect();
300
        $query->setFrom("MyTable");
301
        $query->setLimit(0, 99);
302
    }
303
304
    /**
305
     * @expectedException InvalidArgumentException
306
     */
307
    public function testNegativeLimit()
308
    {
309
        $query = new SQLSelect();
310
        $query->setLimit(-10);
311
    }
312
313
    /**
314
     * @expectedException InvalidArgumentException
315
     */
316
    public function testNegativeOffset()
317
    {
318
        $query = new SQLSelect();
319
        $query->setLimit(1, -10);
320
    }
321
322
    /**
323
     * @expectedException InvalidArgumentException
324
     */
325
    public function testNegativeOffsetAndLimit()
326
    {
327
        $query = new SQLSelect();
328
        $query->setLimit(-10, -10);
329
    }
330
331
    public function testReverseOrderBy()
332
    {
333
        $query = new SQLSelect();
334
        $query->setFrom('MyTable');
335
336
        // default is ASC
337
        $query->setOrderBy("Name");
338
        $query->reverseOrderBy();
339
340
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name DESC', $query->sql($parameters));
341
342
        $query->setOrderBy("Name DESC");
343
        $query->reverseOrderBy();
344
345
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name ASC', $query->sql($parameters));
346
347
        $query->setOrderBy(array("Name" => "ASC"));
348
        $query->reverseOrderBy();
349
350
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name DESC', $query->sql($parameters));
351
352
        $query->setOrderBy(array("Name" => 'DESC', 'Color' => 'asc'));
353
        $query->reverseOrderBy();
354
355
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name ASC, Color DESC', $query->sql($parameters));
356
357
        $query->setOrderBy('implode("MyName","Color") DESC');
358
        $query->reverseOrderBy();
359
360
        $this->assertSQLEquals(
361
            'SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',
362
            $query->sql($parameters)
363
        );
364
    }
365
366
    public function testFiltersOnID()
367
    {
368
        $query = new SQLSelect();
369
        $query->setWhere("ID = 5");
370
        $this->assertTrue(
371
            $query->filtersOnID(),
372
            "filtersOnID() is true with simple unquoted column name"
373
        );
374
375
        $query = new SQLSelect();
376
        $query->setWhere('"ID" = 5');
377
        $this->assertTrue(
378
            $query->filtersOnID(),
379
            "filtersOnID() is true with simple quoted column name"
380
        );
381
382
        $query = new SQLSelect();
383
        $query->setWhere(array('"ID"' => 4));
384
        $this->assertTrue(
385
            $query->filtersOnID(),
386
            "filtersOnID() is true with parameterised quoted column name"
387
        );
388
389
        $query = new SQLSelect();
390
        $query->setWhere(array('"ID" = ?' => 4));
391
        $this->assertTrue(
392
            $query->filtersOnID(),
393
            "filtersOnID() is true with parameterised quoted column name"
394
        );
395
396
        $query = new SQLSelect();
397
        $query->setWhere('"ID" IN (5,4)');
398
        $this->assertTrue(
399
            $query->filtersOnID(),
400
            "filtersOnID() is true with WHERE ID IN"
401
        );
402
403
        $query = new SQLSelect();
404
        $query->setWhere(array('"ID" IN ?' => array(1,2)));
405
        $this->assertTrue(
406
            $query->filtersOnID(),
407
            "filtersOnID() is true with parameterised WHERE ID IN"
408
        );
409
410
        $query = new SQLSelect();
411
        $query->setWhere("ID=5");
412
        $this->assertTrue(
413
            $query->filtersOnID(),
414
            "filtersOnID() is true with simple unquoted column name and no spaces in equals sign"
415
        );
416
417
        $query = new SQLSelect();
418
        $query->setWhere("Identifier = 5");
419
        $this->assertFalse(
420
            $query->filtersOnID(),
421
            "filtersOnID() is false with custom column name (starting with 'id')"
422
        );
423
424
        $query = new SQLSelect();
425
        $query->setWhere("ParentID = 5");
426
        $this->assertFalse(
427
            $query->filtersOnID(),
428
            "filtersOnID() is false with column name ending in 'ID'"
429
        );
430
431
        $query = new SQLSelect();
432
        $query->setWhere("MyTable.ID = 5");
433
        $this->assertTrue(
434
            $query->filtersOnID(),
435
            "filtersOnID() is true with table and column name"
436
        );
437
438
        $query = new SQLSelect();
439
        $query->setWhere("MyTable.ID = 5");
440
        $this->assertTrue(
441
            $query->filtersOnID(),
442
            "filtersOnID() is true with table and quoted column name "
443
        );
444
    }
445
446
    public function testFiltersOnFK()
447
    {
448
        $query = new SQLSelect();
449
        $query->setWhere("ID = 5");
450
        $this->assertFalse(
451
            $query->filtersOnFK(),
452
            "filtersOnFK() is true with simple unquoted column name"
453
        );
454
455
        $query = new SQLSelect();
456
        $query->setWhere("Identifier = 5");
457
        $this->assertFalse(
458
            $query->filtersOnFK(),
459
            "filtersOnFK() is false with custom column name (starting with 'id')"
460
        );
461
462
        $query = new SQLSelect();
463
        $query->setWhere("MyTable.ParentID = 5");
464
        $this->assertTrue(
465
            $query->filtersOnFK(),
466
            "filtersOnFK() is true with table and column name"
467
        );
468
469
        $query = new SQLSelect();
470
        $query->setWhere("MyTable.`ParentID`= 5");
471
        $this->assertTrue(
472
            $query->filtersOnFK(),
473
            "filtersOnFK() is true with table and quoted column name "
474
        );
475
    }
476
477
    public function testInnerJoin()
478
    {
479
        $query = new SQLSelect();
480
        $query->setFrom('MyTable');
481
        $query->addInnerJoin('MyOtherTable', 'MyOtherTable.ID = 2');
482
        $query->addLeftJoin('MyLastTable', 'MyOtherTable.ID = MyLastTable.ID');
483
484
        $this->assertSQLEquals(
485
            'SELECT * FROM MyTable ' .
486
            'INNER JOIN "MyOtherTable" ON MyOtherTable.ID = 2 ' .
487
            'LEFT JOIN "MyLastTable" ON MyOtherTable.ID = MyLastTable.ID',
488
            $query->sql($parameters)
489
        );
490
491
        $query = new SQLSelect();
492
        $query->setFrom('MyTable');
493
        $query->addInnerJoin('MyOtherTable', 'MyOtherTable.ID = 2', 'table1');
494
        $query->addLeftJoin('MyLastTable', 'MyOtherTable.ID = MyLastTable.ID', 'table2');
495
496
        $this->assertSQLEquals(
497
            'SELECT * FROM MyTable ' .
498
            'INNER JOIN "MyOtherTable" AS "table1" ON MyOtherTable.ID = 2 ' .
499
            'LEFT JOIN "MyLastTable" AS "table2" ON MyOtherTable.ID = MyLastTable.ID',
500
            $query->sql($parameters)
501
        );
502
    }
503
504
    public function testJoinSubSelect()
505
    {
506
507
        $query = new SQLSelect();
508
        $query->setFrom('MyTable');
509
        $query->addInnerJoin(
510
            '(SELECT * FROM MyOtherTable)',
511
            'Mot.MyTableID = MyTable.ID',
512
            'Mot'
513
        );
514
        $query->addLeftJoin(
515
            '(SELECT MyLastTable.MyOtherTableID, COUNT(1) as MyLastTableCount FROM MyLastTable '
516
            . 'GROUP BY MyOtherTableID)',
517
            'Mlt.MyOtherTableID = Mot.ID',
518
            'Mlt'
519
        );
520
        $query->setOrderBy('COALESCE(Mlt.MyLastTableCount, 0) DESC');
521
522
        $this->assertSQLEquals(
523
            'SELECT *, COALESCE(Mlt.MyLastTableCount, 0) AS "_SortColumn0" FROM MyTable ' .
524
            'INNER JOIN (SELECT * FROM MyOtherTable) AS "Mot" ON Mot.MyTableID = MyTable.ID ' .
525
            'LEFT JOIN (SELECT MyLastTable.MyOtherTableID, COUNT(1) as MyLastTableCount FROM MyLastTable '
526
            . 'GROUP BY MyOtherTableID) AS "Mlt" ON Mlt.MyOtherTableID = Mot.ID ' .
527
            'ORDER BY "_SortColumn0" DESC',
528
            $query->sql($parameters)
529
        );
530
    }
531
532
    public function testSetWhereAny()
533
    {
534
        $query = new SQLSelect();
535
        $query->setFrom('MyTable');
536
537
        $query->setWhereAny(
538
            array(
539
            'Monkey' => 'Chimp',
540
            'Color' => 'Brown'
541
            )
542
        );
543
        $sql = $query->sql($parameters);
544
        $this->assertSQLEquals("SELECT * FROM MyTable WHERE ((Monkey = ?) OR (Color = ?))", $sql);
545
        $this->assertEquals(array('Chimp', 'Brown'), $parameters);
546
    }
547
548
    public function testSelectFirst()
549
    {
550
        // Test first from sequence
551
        $query = new SQLSelect();
552
        $query->setFrom('"SQLSelectTest_DO"');
553
        $query->setOrderBy('"Name"');
554
        $result = $query->firstRow()->execute();
555
556
        $records = array();
557
        foreach ($result as $row) {
558
            $records[] = $row;
559
        }
560
561
        $this->assertCount(1, $records);
562
        $this->assertEquals('Object 1', $records[0]['Name']);
563
564
        // Test first from empty sequence
565
        $query = new SQLSelect();
566
        $query->setFrom('"SQLSelectTest_DO"');
567
        $query->setOrderBy('"Name"');
568
        $query->setWhere(array('"Name"' => 'Nonexistent Object'));
569
        $result = $query->firstRow()->execute();
570
571
        $records = array();
572
        foreach ($result as $row) {
573
            $records[] = $row;
574
        }
575
576
        $this->assertCount(0, $records);
577
578
        // Test that given the last item, the 'first' in this list matches the last
579
        $query = new SQLSelect();
580
        $query->setFrom('"SQLSelectTest_DO"');
581
        $query->setOrderBy('"Name"');
582
        $query->setLimit(1, 1);
583
        $result = $query->firstRow()->execute();
584
585
        $records = array();
586
        foreach ($result as $row) {
587
            $records[] = $row;
588
        }
589
590
        $this->assertCount(1, $records);
591
        $this->assertEquals('Object 2', $records[0]['Name']);
592
    }
593
594
    public function testSelectLast()
595
    {
596
        // Test last in sequence
597
        $query = new SQLSelect();
598
        $query->setFrom('"SQLSelectTest_DO"');
599
        $query->setOrderBy('"Name"');
600
        $result = $query->lastRow()->execute();
601
602
        $records = array();
603
        foreach ($result as $row) {
604
            $records[] = $row;
605
        }
606
607
        $this->assertCount(1, $records);
608
        $this->assertEquals('Object 2', $records[0]['Name']);
609
610
        // Test last from empty sequence
611
        $query = new SQLSelect();
612
        $query->setFrom('"SQLSelectTest_DO"');
613
        $query->setOrderBy('"Name"');
614
        $query->setWhere(array("\"Name\" = 'Nonexistent Object'"));
615
        $result = $query->lastRow()->execute();
616
617
        $records = array();
618
        foreach ($result as $row) {
619
            $records[] = $row;
620
        }
621
622
        $this->assertCount(0, $records);
623
624
        // Test that given the first item, the 'last' in this list matches the first
625
        $query = new SQLSelect();
626
        $query->setFrom('"SQLSelectTest_DO"');
627
        $query->setOrderBy('"Name"');
628
        $query->setLimit(1);
629
        $result = $query->lastRow()->execute();
630
631
        $records = array();
632
        foreach ($result as $row) {
633
            $records[] = $row;
634
        }
635
636
        $this->assertCount(1, $records);
637
        $this->assertEquals('Object 1', $records[0]['Name']);
638
    }
639
640
    /**
641
     * Tests aggregate() function
642
     */
643
    public function testAggregate()
644
    {
645
        $query = new SQLSelect('"Common"');
646
        $query->setFrom('"SQLSelectTest_DO"');
647
        $query->setGroupBy('"Common"');
648
649
        $queryClone = $query->aggregate('COUNT(*)', 'cnt');
650
        $result = $queryClone->execute();
651
        $this->assertEquals(array(2), $result->column('cnt'));
652
    }
653
654
    /**
655
     * Tests that an ORDER BY is only added if a LIMIT is set.
656
     */
657
    public function testAggregateNoOrderByIfNoLimit()
658
    {
659
        $query = new SQLSelect();
660
        $query->setFrom('"SQLSelectTest_DO"');
661
        $query->setOrderBy('Common');
662
        $query->setLimit(array());
663
664
        $aggregate = $query->aggregate('MAX("ID")');
665
        $limit = $aggregate->getLimit();
666
        $this->assertEquals(array(), $aggregate->getOrderBy());
667
        $this->assertEquals(array(), $limit);
668
669
        $query = new SQLSelect();
670
        $query->setFrom('"SQLSelectTest_DO"');
671
        $query->setOrderBy('Common');
672
        $query->setLimit(2);
673
674
        $aggregate = $query->aggregate('MAX("ID")');
675
        $limit = $aggregate->getLimit();
676
        $this->assertEquals(array('Common' => 'ASC'), $aggregate->getOrderBy());
677
        $this->assertEquals(array('start' => 0, 'limit' => 2), $limit);
678
    }
679
680
    /**
681
     * Test that "_SortColumn0" is added for an aggregate in the ORDER BY
682
     * clause, in combination with a LIMIT and GROUP BY clause.
683
     * For some databases, like MSSQL, this is a complicated scenario
684
     * because a subselect needs to be done to query paginated data.
685
     */
686
    public function testOrderByContainingAggregateAndLimitOffset()
687
    {
688
        $query = new SQLSelect();
689
        $query->setSelect(array('"Name"', '"Meta"'));
690
        $query->setFrom('"SQLSelectTest_DO"');
691
        $query->setOrderBy(array('MAX("Date")'));
692
        $query->setGroupBy(array('"Name"', '"Meta"'));
693
        $query->setLimit('1', '1');
694
695
        $records = array();
696
        foreach ($query->execute() as $record) {
697
            $records[] = $record;
698
        }
699
700
        $this->assertCount(1, $records);
701
702
        $this->assertEquals('Object 2', $records[0]['Name']);
703
        $this->assertEquals('2012-05-01 09:00:00', $records['0']['_SortColumn0']);
704
    }
705
706
    /**
707
     * Test that multiple order elements are maintained in the given order
708
     */
709
    public function testOrderByMultiple()
710
    {
711
        if (!(DB::get_conn() instanceof MySQLDatabase)) {
712
            $this->markTestSkipped('This test requires a MySQL database');
713
        }
714
715
        $query = new SQLSelect();
716
        $query->setSelect(array('"Name"', '"Meta"'));
717
        $query->setFrom('"SQLSelectTest_DO"');
718
        $query->setOrderBy(array('MID("Name", 8, 1) DESC', '"Name" ASC'));
719
720
        $records = array();
721
        foreach ($query->execute() as $record) {
722
            $records[] = $record;
723
        }
724
725
        $this->assertCount(2, $records);
726
727
        $this->assertEquals('Object 2', $records[0]['Name']);
728
        $this->assertEquals('2', $records[0]['_SortColumn0']);
729
730
        $this->assertEquals('Object 1', $records[1]['Name']);
731
        $this->assertEquals('1', $records[1]['_SortColumn0']);
732
    }
733
734
    public function testSelect()
735
    {
736
        $query = new SQLSelect('"Title"', '"MyTable"');
737
        $query->addSelect('"TestField"');
738
        $this->assertSQLEquals(
739
            'SELECT "Title", "TestField" FROM "MyTable"',
740
            $query->sql()
741
        );
742
743
        // Test replacement of select
744
        $query->setSelect(
745
            array(
746
            'Field' => '"Field"',
747
            'AnotherAlias' => '"AnotherField"'
748
            )
749
        );
750
        $this->assertSQLEquals(
751
            'SELECT "Field", "AnotherField" AS "AnotherAlias" FROM "MyTable"',
752
            $query->sql()
753
        );
754
755
        // Check that ' as ' selects don't get mistaken as aliases
756
        $query->addSelect(
757
            array(
758
            'Relevance' => "MATCH (Title, MenuTitle) AGAINST ('Two as One')"
759
            )
760
        );
761
        $this->assertSQLEquals(
762
            'SELECT "Field", "AnotherField" AS "AnotherAlias", MATCH (Title, MenuTitle) AGAINST (' .
763
            '\'Two as One\') AS "Relevance" FROM "MyTable"',
764
            $query->sql()
765
        );
766
    }
767
768
    /**
769
     * Test passing in a LIMIT with OFFSET clause string.
770
     */
771
    public function testLimitSetFromClauseString()
772
    {
773
        $query = new SQLSelect();
774
        $query->setSelect('*');
775
        $query->setFrom('"SQLSelectTest_DO"');
776
777
        $query->setLimit('20 OFFSET 10');
778
        $limit = $query->getLimit();
779
        $this->assertEquals(20, $limit['limit']);
780
        $this->assertEquals(10, $limit['start']);
781
    }
782
783
    public function testParameterisedInnerJoins()
784
    {
785
        $query = new SQLSelect();
786
        $query->setSelect(array('"SQLSelectTest_DO"."Name"', '"SubSelect"."Count"'));
787
        $query->setFrom('"SQLSelectTest_DO"');
788
        $query->addInnerJoin(
789
            '(SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase" GROUP BY "Title" HAVING "Title" NOT LIKE ?)',
790
            '"SQLSelectTest_DO"."Name" = "SubSelect"."Title"',
791
            'SubSelect',
792
            20,
793
            array('%MyName%')
794
        );
795
        $query->addWhere(array('"SQLSelectTest_DO"."Date" > ?' => '2012-08-08 12:00'));
796
797
        $this->assertSQLEquals(
798
            'SELECT "SQLSelectTest_DO"."Name", "SubSelect"."Count"
799
			FROM "SQLSelectTest_DO" INNER JOIN (SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase"
800
		   GROUP BY "Title" HAVING "Title" NOT LIKE ?) AS "SubSelect" ON "SQLSelectTest_DO"."Name" =
801
		   "SubSelect"."Title"
802
			WHERE ("SQLSelectTest_DO"."Date" > ?)',
803
            $query->sql($parameters)
804
        );
805
        $this->assertEquals(array('%MyName%', '2012-08-08 12:00'), $parameters);
806
        $query->execute();
807
    }
808
809
    public function testParameterisedLeftJoins()
810
    {
811
        $query = new SQLSelect();
812
        $query->setSelect(array('"SQLSelectTest_DO"."Name"', '"SubSelect"."Count"'));
813
        $query->setFrom('"SQLSelectTest_DO"');
814
        $query->addLeftJoin(
815
            '(SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase" GROUP BY "Title" HAVING "Title" NOT LIKE ?)',
816
            '"SQLSelectTest_DO"."Name" = "SubSelect"."Title"',
817
            'SubSelect',
818
            20,
819
            array('%MyName%')
820
        );
821
        $query->addWhere(array('"SQLSelectTest_DO"."Date" > ?' => '2012-08-08 12:00'));
822
823
        $this->assertSQLEquals(
824
            'SELECT "SQLSelectTest_DO"."Name", "SubSelect"."Count"
825
			FROM "SQLSelectTest_DO" LEFT JOIN (SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase"
826
		   GROUP BY "Title" HAVING "Title" NOT LIKE ?) AS "SubSelect" ON "SQLSelectTest_DO"."Name" =
827
		   "SubSelect"."Title"
828
			WHERE ("SQLSelectTest_DO"."Date" > ?)',
829
            $query->sql($parameters)
830
        );
831
        $this->assertEquals(array('%MyName%', '2012-08-08 12:00'), $parameters);
832
        $query->execute();
833
    }
834
835
    public function testBaseTableAliases()
836
    {
837
        $query = SQLSelect::create('*', ['"MyTableAlias"' => '"MyTable"']);
838
        $sql = $query->sql();
839
840
        $this->assertSQLEquals('SELECT * FROM "MyTable" AS "MyTableAlias"', $sql);
841
842
        $query = SQLSelect::create('*', ['MyTableAlias' => '"MyTable"']);
843
        $sql = $query->sql();
844
845
        $this->assertSQLEquals('SELECT * FROM "MyTable" AS "MyTableAlias"', $sql);
846
847
        $query = SQLSelect::create('*', ['"MyTableAlias"' => '"MyTable"']);
848
        $query->addLeftJoin('OtherTable', '"Thing" = "OtherThing"', 'OtherTableAlias');
849
        $sql = $query->sql();
850
851
        $this->assertSQLEquals(
852
            'SELECT *
853
              FROM "MyTable" AS "MyTableAlias"
854
              LEFT JOIN "OtherTable" AS "OtherTableAlias" ON "Thing" = "OtherThing"',
855
            $sql
856
        );
857
858
        $query = SQLSelect::create('*', [
859
            'MyTableAlias' => '"MyTable"',
860
            'ignoredAlias' => ', (SELECT * FROM "MyTable" where "something" = "whatever") as "CrossJoin"'
861
        ]);
862
        $sql = $query->sql();
863
864
        $this->assertSQLEquals(
865
            'SELECT * FROM "MyTable" AS "MyTableAlias" , '.
866
            '(SELECT * FROM "MyTable" where "something" = "whatever") as "CrossJoin"',
867
            $sql
868
        );
869
    }
870
}
871