Passed
Push — fix-6460 ( 379c3e...254990 )
by Sam
09:57
created

SQLSelectTest::testParameterisedInnerJoins()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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