Completed
Push — master ( d01da5...f225b8 )
by Daniel
11:15
created

SQLSelectTest::testAddOrderBy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 9
rs 9.6666
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;
9
use SilverStripe\PostgreSQL\PostgreSQLDatabase;
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 $extraDataObjects = array(
19
        SQLSelectTest\TestObject::class,
20
        SQLSelectTest\TestBase::class,
21
        SQLSelectTest\TestChild::class
22
    );
23
24
    protected $oldDeprecation = null;
25
26
    public function setUp()
27
    {
28
        parent::setUp();
29
        $this->oldDeprecation = Deprecation::dump_settings();
30
    }
31
32
    public 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
0 ignored issues
show
Bug introduced by
The class SilverStripe\SQLite\SQLite3Database does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
171
            || DB::get_conn() instanceof PostgreSQLDatabase)
0 ignored issues
show
Bug introduced by
The class SilverStripe\PostgreSQL\PostgreSQLDatabase does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
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
    public function testZeroLimit()
271
    {
272
        $query = new SQLSelect();
273
        $query->setFrom("MyTable");
274
        $query->setLimit(0);
275
276
        $this->assertSQLEquals(
277
            'SELECT * FROM MyTable',
278
            $query->sql($parameters)
279
        );
280
    }
281
282
    public function testZeroLimitWithOffset()
283
    {
284
        if (!(DB::get_conn() instanceof MySQLDatabase || DB::get_conn() instanceof SQLite3Database
0 ignored issues
show
Bug introduced by
The class SilverStripe\SQLite\SQLite3Database does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
285
            || DB::get_conn() instanceof PostgreSQLDatabase)
0 ignored issues
show
Bug introduced by
The class SilverStripe\PostgreSQL\PostgreSQLDatabase does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
286
        ) {
287
            $this->markTestIncomplete();
288
        }
289
290
        $query = new SQLSelect();
291
        $query->setFrom("MyTable");
292
        $query->setLimit(0, 99);
293
294
        $this->assertSQLEquals(
295
            'SELECT * FROM MyTable LIMIT 0 OFFSET 99',
296
            $query->sql($parameters)
297
        );
298
    }
299
300
    /**
301
     * @expectedException InvalidArgumentException
302
     */
303
    public function testNegativeLimit()
304
    {
305
        $query = new SQLSelect();
306
        $query->setLimit(-10);
307
    }
308
309
    /**
310
     * @expectedException InvalidArgumentException
311
     */
312
    public function testNegativeOffset()
313
    {
314
        $query = new SQLSelect();
315
        $query->setLimit(1, -10);
316
    }
317
318
    /**
319
     * @expectedException InvalidArgumentException
320
     */
321
    public function testNegativeOffsetAndLimit()
322
    {
323
        $query = new SQLSelect();
324
        $query->setLimit(-10, -10);
325
    }
326
327
    public function testReverseOrderBy()
328
    {
329
        $query = new SQLSelect();
330
        $query->setFrom('MyTable');
331
332
        // default is ASC
333
        $query->setOrderBy("Name");
334
        $query->reverseOrderBy();
335
336
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name DESC', $query->sql($parameters));
337
338
        $query->setOrderBy("Name DESC");
339
        $query->reverseOrderBy();
340
341
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name ASC', $query->sql($parameters));
342
343
        $query->setOrderBy(array("Name" => "ASC"));
344
        $query->reverseOrderBy();
345
346
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name DESC', $query->sql($parameters));
347
348
        $query->setOrderBy(array("Name" => 'DESC', 'Color' => 'asc'));
349
        $query->reverseOrderBy();
350
351
        $this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name ASC, Color DESC', $query->sql($parameters));
352
353
        $query->setOrderBy('implode("MyName","Color") DESC');
354
        $query->reverseOrderBy();
355
356
        $this->assertSQLEquals(
357
            'SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',
358
            $query->sql($parameters)
359
        );
360
    }
361
362
    public function testFiltersOnID()
363
    {
364
        $query = new SQLSelect();
365
        $query->setWhere("ID = 5");
366
        $this->assertTrue(
367
            $query->filtersOnID(),
368
            "filtersOnID() is true with simple unquoted column name"
369
        );
370
371
        $query = new SQLSelect();
372
        $query->setWhere('"ID" = 5');
373
        $this->assertTrue(
374
            $query->filtersOnID(),
375
            "filtersOnID() is true with simple quoted column name"
376
        );
377
378
        $query = new SQLSelect();
379
        $query->setWhere(array('"ID"' => 4));
380
        $this->assertTrue(
381
            $query->filtersOnID(),
382
            "filtersOnID() is true with parameterised quoted column name"
383
        );
384
385
        $query = new SQLSelect();
386
        $query->setWhere(array('"ID" = ?' => 4));
387
        $this->assertTrue(
388
            $query->filtersOnID(),
389
            "filtersOnID() is true with parameterised quoted column name"
390
        );
391
392
        $query = new SQLSelect();
393
        $query->setWhere('"ID" IN (5,4)');
394
        $this->assertTrue(
395
            $query->filtersOnID(),
396
            "filtersOnID() is true with WHERE ID IN"
397
        );
398
399
        $query = new SQLSelect();
400
        $query->setWhere(array('"ID" IN ?' => array(1,2)));
401
        $this->assertTrue(
402
            $query->filtersOnID(),
403
            "filtersOnID() is true with parameterised WHERE ID IN"
404
        );
405
406
        $query = new SQLSelect();
407
        $query->setWhere("ID=5");
408
        $this->assertTrue(
409
            $query->filtersOnID(),
410
            "filtersOnID() is true with simple unquoted column name and no spaces in equals sign"
411
        );
412
413
        $query = new SQLSelect();
414
        $query->setWhere("Identifier = 5");
415
        $this->assertFalse(
416
            $query->filtersOnID(),
417
            "filtersOnID() is false with custom column name (starting with 'id')"
418
        );
419
420
        $query = new SQLSelect();
421
        $query->setWhere("ParentID = 5");
422
        $this->assertFalse(
423
            $query->filtersOnID(),
424
            "filtersOnID() is false with column name ending in 'ID'"
425
        );
426
427
        $query = new SQLSelect();
428
        $query->setWhere("MyTable.ID = 5");
429
        $this->assertTrue(
430
            $query->filtersOnID(),
431
            "filtersOnID() is true with table and column name"
432
        );
433
434
        $query = new SQLSelect();
435
        $query->setWhere("MyTable.ID = 5");
436
        $this->assertTrue(
437
            $query->filtersOnID(),
438
            "filtersOnID() is true with table and quoted column name "
439
        );
440
    }
441
442
    public function testFiltersOnFK()
443
    {
444
        $query = new SQLSelect();
445
        $query->setWhere("ID = 5");
446
        $this->assertFalse(
447
            $query->filtersOnFK(),
448
            "filtersOnFK() is true with simple unquoted column name"
449
        );
450
451
        $query = new SQLSelect();
452
        $query->setWhere("Identifier = 5");
453
        $this->assertFalse(
454
            $query->filtersOnFK(),
455
            "filtersOnFK() is false with custom column name (starting with 'id')"
456
        );
457
458
        $query = new SQLSelect();
459
        $query->setWhere("MyTable.ParentID = 5");
460
        $this->assertTrue(
461
            $query->filtersOnFK(),
462
            "filtersOnFK() is true with table and column name"
463
        );
464
465
        $query = new SQLSelect();
466
        $query->setWhere("MyTable.`ParentID`= 5");
467
        $this->assertTrue(
468
            $query->filtersOnFK(),
469
            "filtersOnFK() is true with table and quoted column name "
470
        );
471
    }
472
473
    public function testInnerJoin()
474
    {
475
        $query = new SQLSelect();
476
        $query->setFrom('MyTable');
477
        $query->addInnerJoin('MyOtherTable', 'MyOtherTable.ID = 2');
478
        $query->addLeftJoin('MyLastTable', 'MyOtherTable.ID = MyLastTable.ID');
479
480
        $this->assertSQLEquals(
481
            'SELECT * FROM MyTable '.
482
            'INNER JOIN "MyOtherTable" ON MyOtherTable.ID = 2 '.
483
            'LEFT JOIN "MyLastTable" ON MyOtherTable.ID = MyLastTable.ID',
484
            $query->sql($parameters)
485
        );
486
487
        $query = new SQLSelect();
488
        $query->setFrom('MyTable');
489
        $query->addInnerJoin('MyOtherTable', 'MyOtherTable.ID = 2', 'table1');
490
        $query->addLeftJoin('MyLastTable', 'MyOtherTable.ID = MyLastTable.ID', 'table2');
491
492
        $this->assertSQLEquals(
493
            'SELECT * FROM MyTable '.
494
            'INNER JOIN "MyOtherTable" AS "table1" ON MyOtherTable.ID = 2 '.
495
            'LEFT JOIN "MyLastTable" AS "table2" ON MyOtherTable.ID = MyLastTable.ID',
496
            $query->sql($parameters)
497
        );
498
    }
499
500
    public function testJoinSubSelect()
501
    {
502
503
        $query = new SQLSelect();
504
        $query->setFrom('MyTable');
505
        $query->addInnerJoin(
506
            '(SELECT * FROM MyOtherTable)',
507
            'Mot.MyTableID = MyTable.ID',
508
            'Mot'
509
        );
510
        $query->addLeftJoin(
511
            '(SELECT MyLastTable.MyOtherTableID, COUNT(1) as MyLastTableCount FROM MyLastTable '
512
            . 'GROUP BY MyOtherTableID)',
513
            'Mlt.MyOtherTableID = Mot.ID',
514
            'Mlt'
515
        );
516
        $query->setOrderBy('COALESCE(Mlt.MyLastTableCount, 0) DESC');
517
518
        $this->assertSQLEquals(
519
            'SELECT *, COALESCE(Mlt.MyLastTableCount, 0) AS "_SortColumn0" FROM MyTable '.
520
            'INNER JOIN (SELECT * FROM MyOtherTable) AS "Mot" ON Mot.MyTableID = MyTable.ID ' .
521
            'LEFT JOIN (SELECT MyLastTable.MyOtherTableID, COUNT(1) as MyLastTableCount FROM MyLastTable '
522
            . 'GROUP BY MyOtherTableID) AS "Mlt" ON Mlt.MyOtherTableID = Mot.ID ' .
523
            'ORDER BY "_SortColumn0" DESC',
524
            $query->sql($parameters)
525
        );
526
    }
527
528
    public function testSetWhereAny()
529
    {
530
        $query = new SQLSelect();
531
        $query->setFrom('MyTable');
532
533
        $query->setWhereAny(
534
            array(
535
            'Monkey' => 'Chimp',
536
            'Color' => 'Brown'
537
            )
538
        );
539
        $sql = $query->sql($parameters);
540
        $this->assertSQLEquals("SELECT * FROM MyTable WHERE ((Monkey = ?) OR (Color = ?))", $sql);
541
        $this->assertEquals(array('Chimp', 'Brown'), $parameters);
542
    }
543
544
    public function testSelectFirst()
545
    {
546
        // Test first from sequence
547
        $query = new SQLSelect();
548
        $query->setFrom('"SQLSelectTest_DO"');
549
        $query->setOrderBy('"Name"');
550
        $result = $query->firstRow()->execute();
551
552
        $records = array();
553
        foreach ($result as $row) {
554
            $records[] = $row;
555
        }
556
557
        $this->assertCount(1, $records);
558
        $this->assertEquals('Object 1', $records[0]['Name']);
559
560
        // Test first from empty sequence
561
        $query = new SQLSelect();
562
        $query->setFrom('"SQLSelectTest_DO"');
563
        $query->setOrderBy('"Name"');
564
        $query->setWhere(array('"Name"' => 'Nonexistent Object'));
565
        $result = $query->firstRow()->execute();
566
567
        $records = array();
568
        foreach ($result as $row) {
569
            $records[] = $row;
570
        }
571
572
        $this->assertCount(0, $records);
573
574
        // Test that given the last item, the 'first' in this list matches the last
575
        $query = new SQLSelect();
576
        $query->setFrom('"SQLSelectTest_DO"');
577
        $query->setOrderBy('"Name"');
578
        $query->setLimit(1, 1);
579
        $result = $query->firstRow()->execute();
580
581
        $records = array();
582
        foreach ($result as $row) {
583
            $records[] = $row;
584
        }
585
586
        $this->assertCount(1, $records);
587
        $this->assertEquals('Object 2', $records[0]['Name']);
588
    }
589
590
    public function testSelectLast()
591
    {
592
        // Test last in sequence
593
        $query = new SQLSelect();
594
        $query->setFrom('"SQLSelectTest_DO"');
595
        $query->setOrderBy('"Name"');
596
        $result = $query->lastRow()->execute();
597
598
        $records = array();
599
        foreach ($result as $row) {
600
            $records[] = $row;
601
        }
602
603
        $this->assertCount(1, $records);
604
        $this->assertEquals('Object 2', $records[0]['Name']);
605
606
        // Test last from empty sequence
607
        $query = new SQLSelect();
608
        $query->setFrom('"SQLSelectTest_DO"');
609
        $query->setOrderBy('"Name"');
610
        $query->setWhere(array("\"Name\" = 'Nonexistent Object'"));
611
        $result = $query->lastRow()->execute();
612
613
        $records = array();
614
        foreach ($result as $row) {
615
            $records[] = $row;
616
        }
617
618
        $this->assertCount(0, $records);
619
620
        // Test that given the first item, the 'last' in this list matches the first
621
        $query = new SQLSelect();
622
        $query->setFrom('"SQLSelectTest_DO"');
623
        $query->setOrderBy('"Name"');
624
        $query->setLimit(1);
625
        $result = $query->lastRow()->execute();
626
627
        $records = array();
628
        foreach ($result as $row) {
629
            $records[] = $row;
630
        }
631
632
        $this->assertCount(1, $records);
633
        $this->assertEquals('Object 1', $records[0]['Name']);
634
    }
635
636
    /**
637
     * Tests aggregate() function
638
     */
639
    public function testAggregate()
640
    {
641
        $query = new SQLSelect('"Common"');
642
        $query->setFrom('"SQLSelectTest_DO"');
643
        $query->setGroupBy('"Common"');
644
645
        $queryClone = $query->aggregate('COUNT(*)', 'cnt');
646
        $result = $queryClone->execute();
647
        $this->assertEquals(array(2), $result->column('cnt'));
648
    }
649
650
    /**
651
     * Tests that an ORDER BY is only added if a LIMIT is set.
652
     */
653
    public function testAggregateNoOrderByIfNoLimit()
654
    {
655
        $query = new SQLSelect();
656
        $query->setFrom('"SQLSelectTest_DO"');
657
        $query->setOrderBy('Common');
658
        $query->setLimit(array());
659
660
        $aggregate = $query->aggregate('MAX("ID")');
661
        $limit = $aggregate->getLimit();
662
        $this->assertEquals(array(), $aggregate->getOrderBy());
663
        $this->assertEquals(array(), $limit);
664
665
        $query = new SQLSelect();
666
        $query->setFrom('"SQLSelectTest_DO"');
667
        $query->setOrderBy('Common');
668
        $query->setLimit(2);
669
670
        $aggregate = $query->aggregate('MAX("ID")');
671
        $limit = $aggregate->getLimit();
672
        $this->assertEquals(array('Common' => 'ASC'), $aggregate->getOrderBy());
673
        $this->assertEquals(array('start' => 0, 'limit' => 2), $limit);
674
    }
675
676
    /**
677
     * Test that "_SortColumn0" is added for an aggregate in the ORDER BY
678
     * clause, in combination with a LIMIT and GROUP BY clause.
679
     * For some databases, like MSSQL, this is a complicated scenario
680
     * because a subselect needs to be done to query paginated data.
681
     */
682
    public function testOrderByContainingAggregateAndLimitOffset()
683
    {
684
        $query = new SQLSelect();
685
        $query->setSelect(array('"Name"', '"Meta"'));
686
        $query->setFrom('"SQLSelectTest_DO"');
687
        $query->setOrderBy(array('MAX("Date")'));
688
        $query->setGroupBy(array('"Name"', '"Meta"'));
689
        $query->setLimit('1', '1');
690
691
        $records = array();
692
        foreach ($query->execute() as $record) {
693
            $records[] = $record;
694
        }
695
696
        $this->assertCount(1, $records);
697
698
        $this->assertEquals('Object 2', $records[0]['Name']);
699
        $this->assertEquals('2012-05-01 09:00:00', $records['0']['_SortColumn0']);
700
    }
701
702
    /**
703
     * Test that multiple order elements are maintained in the given order
704
     */
705
    public function testOrderByMultiple()
706
    {
707
        if (DB::get_conn() instanceof MySQLDatabase) {
708
            $query = new SQLSelect();
709
            $query->setSelect(array('"Name"', '"Meta"'));
710
            $query->setFrom('"SQLSelectTest_DO"');
711
            $query->setOrderBy(array('MID("Name", 8, 1) DESC', '"Name" ASC'));
712
713
            $records = array();
714
            foreach ($query->execute() as $record) {
715
                $records[] = $record;
716
            }
717
718
            $this->assertCount(2, $records);
719
720
            $this->assertEquals('Object 2', $records[0]['Name']);
721
            $this->assertEquals('2', $records[0]['_SortColumn0']);
722
723
            $this->assertEquals('Object 1', $records[1]['Name']);
724
            $this->assertEquals('1', $records[1]['_SortColumn0']);
725
        }
726
    }
727
728
    public function testSelect()
729
    {
730
        $query = new SQLSelect('"Title"', '"MyTable"');
731
        $query->addSelect('"TestField"');
732
        $this->assertSQLEquals(
733
            'SELECT "Title", "TestField" FROM "MyTable"',
734
            $query->sql()
735
        );
736
737
        // Test replacement of select
738
        $query->setSelect(
739
            array(
740
            'Field' => '"Field"',
741
            'AnotherAlias' => '"AnotherField"'
742
            )
743
        );
744
        $this->assertSQLEquals(
745
            'SELECT "Field", "AnotherField" AS "AnotherAlias" FROM "MyTable"',
746
            $query->sql()
747
        );
748
749
        // Check that ' as ' selects don't get mistaken as aliases
750
        $query->addSelect(
751
            array(
752
            'Relevance' => "MATCH (Title, MenuTitle) AGAINST ('Two as One')"
753
            )
754
        );
755
        $this->assertSQLEquals(
756
            'SELECT "Field", "AnotherField" AS "AnotherAlias", MATCH (Title, MenuTitle) AGAINST (' .
757
            '\'Two as One\') AS "Relevance" FROM "MyTable"',
758
            $query->sql()
759
        );
760
    }
761
762
    /**
763
     * Test passing in a LIMIT with OFFSET clause string.
764
     */
765
    public function testLimitSetFromClauseString()
766
    {
767
        $query = new SQLSelect();
768
        $query->setSelect('*');
769
        $query->setFrom('"SQLSelectTest_DO"');
770
771
        $query->setLimit('20 OFFSET 10');
772
        $limit = $query->getLimit();
773
        $this->assertEquals(20, $limit['limit']);
774
        $this->assertEquals(10, $limit['start']);
775
    }
776
777
    public function testParameterisedInnerJoins()
778
    {
779
        $query = new SQLSelect();
780
        $query->setSelect(array('"SQLSelectTest_DO"."Name"', '"SubSelect"."Count"'));
781
        $query->setFrom('"SQLSelectTest_DO"');
782
        $query->addInnerJoin(
783
            '(SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase" GROUP BY "Title" HAVING "Title" NOT LIKE ?)',
784
            '"SQLSelectTest_DO"."Name" = "SubSelect"."Title"',
785
            'SubSelect',
786
            20,
787
            array('%MyName%')
788
        );
789
        $query->addWhere(array('"SQLSelectTest_DO"."Date" > ?' => '2012-08-08 12:00'));
790
791
        $this->assertSQLEquals(
792
            'SELECT "SQLSelectTest_DO"."Name", "SubSelect"."Count"
793
			FROM "SQLSelectTest_DO" INNER JOIN (SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase"
794
		   GROUP BY "Title" HAVING "Title" NOT LIKE ?) AS "SubSelect" ON "SQLSelectTest_DO"."Name" =
795
		   "SubSelect"."Title"
796
			WHERE ("SQLSelectTest_DO"."Date" > ?)',
797
            $query->sql($parameters)
798
        );
799
        $this->assertEquals(array('%MyName%', '2012-08-08 12:00'), $parameters);
800
        $query->execute();
801
    }
802
803
    public function testParameterisedLeftJoins()
804
    {
805
        $query = new SQLSelect();
806
        $query->setSelect(array('"SQLSelectTest_DO"."Name"', '"SubSelect"."Count"'));
807
        $query->setFrom('"SQLSelectTest_DO"');
808
        $query->addLeftJoin(
809
            '(SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase" GROUP BY "Title" HAVING "Title" NOT LIKE ?)',
810
            '"SQLSelectTest_DO"."Name" = "SubSelect"."Title"',
811
            'SubSelect',
812
            20,
813
            array('%MyName%')
814
        );
815
        $query->addWhere(array('"SQLSelectTest_DO"."Date" > ?' => '2012-08-08 12:00'));
816
817
        $this->assertSQLEquals(
818
            'SELECT "SQLSelectTest_DO"."Name", "SubSelect"."Count"
819
			FROM "SQLSelectTest_DO" LEFT JOIN (SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase"
820
		   GROUP BY "Title" HAVING "Title" NOT LIKE ?) AS "SubSelect" ON "SQLSelectTest_DO"."Name" =
821
		   "SubSelect"."Title"
822
			WHERE ("SQLSelectTest_DO"."Date" > ?)',
823
            $query->sql($parameters)
824
        );
825
        $this->assertEquals(array('%MyName%', '2012-08-08 12:00'), $parameters);
826
        $query->execute();
827
    }
828
}
829