Completed
Push — namespace-template ( 7967f2...367a36 )
by Sam
10:48
created

SQLSelectTest::testSelectWithOrderbyClause()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 64
Code Lines 54

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 64
rs 9.3956
cc 1
eloc 54
nc 1
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @package framework
5
 * @subpackage tests
6
 */
7
class SQLSelectTest extends SapphireTest {
8
9
	protected static $fixture_file = 'SQLSelectTest.yml';
10
11
	protected $extraDataObjects = array(
12
		'SQLSelectTest_DO',
13
		'SQLSelectTestBase',
14
		'SQLSelectTestChild'
15
	);
16
17
	protected $oldDeprecation = null;
18
19
	public function setUp() {
20
		parent::setUp();
21
		$this->oldDeprecation = Deprecation::dump_settings();
22
	}
23
24
	public function tearDown() {
25
		Deprecation::restore_settings($this->oldDeprecation);
26
		parent::tearDown();
27
	}
28
29
	public function testCount() {
30
31
		//basic counting
32
		$qry = SQLSelectTest_DO::get()->dataQuery()->getFinalisedQuery();
33
		$qry->setGroupBy('"Common"');
34
		$ids = $this->allFixtureIDs('SQLSelectTest_DO');
35
		$count = $qry->count('"SQLSelectTest_DO"."ID"');
36
		$this->assertEquals(count($ids), $count);
37
		$this->assertInternalType("int", $count);
38
		//test with `having`
39 View Code Duplication
		if (DB::get_conn() instanceof MySQLDatabase) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
40
			$qry->setHaving('"Date" > 2012-02-01');
41
			$count = $qry->count('"SQLSelectTest_DO"."ID"');
42
			$this->assertEquals(1, $count);
43
			$this->assertInternalType("int", $count);
44
		}
45
	}
46
	public function testUnlimitedRowCount() {
47
		//basic counting
48
		$qry = SQLSelectTest_DO::get()->dataQuery()->getFinalisedQuery();
49
		$ids = $this->allFixtureIDs('SQLSelectTest_DO');
50
		$qry->setLimit(1);
51
		$count = $qry->unlimitedRowCount('"SQLSelectTest_DO"."ID"');
52
		$this->assertEquals(count($ids), $count);
53
		$this->assertInternalType("int", $count);
54
		// Test without column - SQLSelect has different logic for this
55
		$count = $qry->unlimitedRowCount();
56
		$this->assertEquals(2, $count);
57
		$this->assertInternalType("int", $count);
58
		//test with `having`
59 View Code Duplication
		if (DB::get_conn() instanceof MySQLDatabase) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
60
			$qry->setHaving('"Date" > 2012-02-01');
61
			$count = $qry->unlimitedRowCount('"SQLSelectTest_DO"."ID"');
62
			$this->assertEquals(1, $count);
63
			$this->assertInternalType("int", $count);
64
		}
65
	}
66
67
	public function testEmptyQueryReturnsNothing() {
68
		$query = new SQLSelect();
69
		$this->assertSQLEquals('', $query->sql($parameters));
70
	}
71
72
	public function testSelectFromBasicTable() {
73
		$query = new SQLSelect();
74
		$query->setFrom('MyTable');
75
		$this->assertSQLEquals("SELECT * FROM MyTable", $query->sql($parameters));
76
		$query->addFrom('MyJoin');
77
		$this->assertSQLEquals("SELECT * FROM MyTable MyJoin", $query->sql($parameters));
78
	}
79
80
	public function testSelectFromUserSpecifiedFields() {
81
		$query = new SQLSelect();
82
		$query->setSelect(array("Name", "Title", "Description"));
83
		$query->setFrom("MyTable");
84
		$this->assertSQLEquals("SELECT Name, Title, Description FROM MyTable", $query->sql($parameters));
85
	}
86
87
	public function testSelectWithWhereClauseFilter() {
88
		$query = new SQLSelect();
89
		$query->setSelect(array("Name","Meta"));
90
		$query->setFrom("MyTable");
91
		$query->setWhere("Name = 'Name'");
92
		$query->addWhere("Meta = 'Test'");
93
		$this->assertSQLEquals(
94
			"SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test')",
95
			$query->sql($parameters)
96
		);
97
	}
98
99
	public function testSelectWithConstructorParameters() {
100
		$query = new SQLSelect(array("Foo", "Bar"), "FooBarTable");
101
		$this->assertSQLEquals("SELECT Foo, Bar FROM FooBarTable", $query->sql($parameters));
102
		$query = new SQLSelect(array("Foo", "Bar"), "FooBarTable", array("Foo = 'Boo'"));
103
		$this->assertSQLEquals("SELECT Foo, Bar FROM FooBarTable WHERE (Foo = 'Boo')", $query->sql($parameters));
104
	}
105
106
	public function testSelectWithChainedMethods() {
107
		$query = new SQLSelect();
108
		$query->setSelect("Name","Meta")->setFrom("MyTable")->setWhere("Name = 'Name'")->addWhere("Meta = 'Test'");
109
		$this->assertSQLEquals(
110
			"SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test')",
111
			$query->sql($parameters)
112
		);
113
	}
114
115 View Code Duplication
	public function testCanSortBy() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
116
		$query = new SQLSelect();
117
		$query->setSelect("Name","Meta")->setFrom("MyTable")->setWhere("Name = 'Name'")->addWhere("Meta = 'Test'");
118
		$this->assertTrue($query->canSortBy('Name ASC'));
119
		$this->assertTrue($query->canSortBy('Name'));
120
	}
121
122 View Code Duplication
	public function testSelectWithChainedFilterParameters() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
123
		$query = new SQLSelect();
124
		$query->setSelect(array("Name","Meta"))->setFrom("MyTable");
125
		$query->setWhere("Name = 'Name'")->addWhere("Meta = 'Test'")->addWhere("Beta != 'Gamma'");
126
		$this->assertSQLEquals(
127
			"SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test') AND (Beta != 'Gamma')",
128
			$query->sql($parameters)
129
		);
130
	}
131
132
	public function testSelectWithLimitClause() {
133 View Code Duplication
		if(!(DB::get_conn() instanceof MySQLDatabase || DB::get_conn() instanceof SQLite3Database
0 ignored issues
show
Bug introduced by
The class 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...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
134
				|| DB::get_conn() instanceof PostgreSQLDatabase)) {
0 ignored issues
show
Bug introduced by
The class 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...
135
			$this->markTestIncomplete();
136
		}
137
138
		$query = new SQLSelect();
139
		$query->setFrom("MyTable");
140
		$query->setLimit(99);
141
		$this->assertSQLEquals("SELECT * FROM MyTable LIMIT 99", $query->sql($parameters));
142
143
		// array limit with start (MySQL specific)
144
		$query = new SQLSelect();
145
		$query->setFrom("MyTable");
146
		$query->setLimit(99, 97);
147
		$this->assertSQLEquals("SELECT * FROM MyTable LIMIT 99 OFFSET 97", $query->sql($parameters));
148
	}
149
150
	public function testSelectWithOrderbyClause() {
151
		$query = new SQLSelect();
152
		$query->setFrom("MyTable");
153
		$query->setOrderBy('MyName');
154
		$this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName ASC', $query->sql($parameters));
155
156
		$query = new SQLSelect();
157
		$query->setFrom("MyTable");
158
		$query->setOrderBy('MyName desc');
159
		$this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName DESC', $query->sql($parameters));
160
161
		$query = new SQLSelect();
162
		$query->setFrom("MyTable");
163
		$query->setOrderBy('MyName ASC, Color DESC');
164
		$this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName ASC, Color DESC', $query->sql($parameters));
165
166
		$query = new SQLSelect();
167
		$query->setFrom("MyTable");
168
		$query->setOrderBy('MyName ASC, Color');
169
		$this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName ASC, Color ASC', $query->sql($parameters));
170
171
		$query = new SQLSelect();
172
		$query->setFrom("MyTable");
173
		$query->setOrderBy(array('MyName' => 'desc'));
174
		$this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName DESC', $query->sql($parameters));
175
176
		$query = new SQLSelect();
177
		$query->setFrom("MyTable");
178
		$query->setOrderBy(array('MyName' => 'desc', 'Color'));
179
		$this->assertSQLEquals('SELECT * FROM MyTable ORDER BY MyName DESC, Color ASC', $query->sql($parameters));
180
181
		$query = new SQLSelect();
182
		$query->setFrom("MyTable");
183
		$query->setOrderBy('implode("MyName","Color")');
184
		$this->assertSQLEquals(
185
			'SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',
186
			$query->sql($parameters));
187
188
		$query = new SQLSelect();
189
		$query->setFrom("MyTable");
190
		$query->setOrderBy('implode("MyName","Color") DESC');
191
		$this->assertSQLEquals(
192
			'SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" DESC',
193
			$query->sql($parameters));
194
195
		$query = new SQLSelect();
196
		$query->setFrom("MyTable");
197
		$query->setOrderBy('RAND()');
198
		$this->assertSQLEquals(
199
			'SELECT *, RAND() AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',
200
			$query->sql($parameters));
201
202
		$query = new SQLSelect();
203
		$query->setFrom("MyTable");
204
		$query->addFrom('INNER JOIN SecondTable USING (ID)');
205
		$query->addFrom('INNER JOIN ThirdTable USING (ID)');
206
		$query->setOrderBy('MyName');
207
		$this->assertSQLEquals(
208
			'SELECT * FROM MyTable '
209
			. 'INNER JOIN SecondTable USING (ID) '
210
			. 'INNER JOIN ThirdTable USING (ID) '
211
			. 'ORDER BY MyName ASC',
212
			$query->sql($parameters));
213
	}
214
215 View Code Duplication
	public function testNullLimit() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
216
		$query = new SQLSelect();
217
		$query->setFrom("MyTable");
218
		$query->setLimit(null);
219
220
		$this->assertSQLEquals(
221
			'SELECT * FROM MyTable',
222
			$query->sql($parameters)
223
		);
224
	}
225
226 View Code Duplication
	public function testZeroLimit() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
227
		$query = new SQLSelect();
228
		$query->setFrom("MyTable");
229
		$query->setLimit(0);
230
231
		$this->assertSQLEquals(
232
			'SELECT * FROM MyTable',
233
			$query->sql($parameters)
234
		);
235
	}
236
237
	public function testZeroLimitWithOffset() {
238 View Code Duplication
		if(!(DB::get_conn() instanceof MySQLDatabase || DB::get_conn() instanceof SQLite3Database
0 ignored issues
show
Bug introduced by
The class 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...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
239
				|| DB::get_conn() instanceof PostgreSQLDatabase)) {
0 ignored issues
show
Bug introduced by
The class 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...
240
			$this->markTestIncomplete();
241
		}
242
243
		$query = new SQLSelect();
244
		$query->setFrom("MyTable");
245
		$query->setLimit(0, 99);
246
247
		$this->assertSQLEquals(
248
			'SELECT * FROM MyTable LIMIT 0 OFFSET 99',
249
			$query->sql($parameters)
250
		);
251
	}
252
253
	/**
254
	 * @expectedException InvalidArgumentException
255
	 */
256
	public function testNegativeLimit() {
257
		$query = new SQLSelect();
258
		$query->setLimit(-10);
259
	}
260
261
	/**
262
	 * @expectedException InvalidArgumentException
263
	 */
264
	public function testNegativeOffset() {
265
		$query = new SQLSelect();
266
		$query->setLimit(1, -10);
267
	}
268
269
	/**
270
	 * @expectedException InvalidArgumentException
271
	 */
272
	public function testNegativeOffsetAndLimit() {
273
		$query = new SQLSelect();
274
		$query->setLimit(-10, -10);
275
	}
276
277
	public function testReverseOrderBy() {
278
		$query = new SQLSelect();
279
		$query->setFrom('MyTable');
280
281
		// default is ASC
282
		$query->setOrderBy("Name");
283
		$query->reverseOrderBy();
284
285
		$this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name DESC',$query->sql($parameters));
286
287
		$query->setOrderBy("Name DESC");
288
		$query->reverseOrderBy();
289
290
		$this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name ASC',$query->sql($parameters));
291
292
		$query->setOrderBy(array("Name" => "ASC"));
293
		$query->reverseOrderBy();
294
295
		$this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name DESC',$query->sql($parameters));
296
297
		$query->setOrderBy(array("Name" => 'DESC', 'Color' => 'asc'));
298
		$query->reverseOrderBy();
299
300
		$this->assertSQLEquals('SELECT * FROM MyTable ORDER BY Name ASC, Color DESC',$query->sql($parameters));
301
302
		$query->setOrderBy('implode("MyName","Color") DESC');
303
		$query->reverseOrderBy();
304
305
		$this->assertSQLEquals(
306
			'SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',
307
			$query->sql($parameters));
308
	}
309
310
	public function testFiltersOnID() {
311
		$query = new SQLSelect();
312
		$query->setWhere("ID = 5");
313
		$this->assertTrue(
314
			$query->filtersOnID(),
315
			"filtersOnID() is true with simple unquoted column name"
316
		);
317
318
		$query = new SQLSelect();
319
		$query->setWhere('"ID" = 5');
320
		$this->assertTrue(
321
			$query->filtersOnID(),
322
			"filtersOnID() is true with simple quoted column name"
323
		);
324
325
		$query = new SQLSelect();
326
		$query->setWhere(array('"ID"' => 4));
327
		$this->assertTrue(
328
			$query->filtersOnID(),
329
			"filtersOnID() is true with parameterised quoted column name"
330
		);
331
332
		$query = new SQLSelect();
333
		$query->setWhere(array('"ID" = ?' => 4));
334
		$this->assertTrue(
335
			$query->filtersOnID(),
336
			"filtersOnID() is true with parameterised quoted column name"
337
		);
338
339
		$query = new SQLSelect();
340
		$query->setWhere('"ID" IN (5,4)');
341
		$this->assertTrue(
342
			$query->filtersOnID(),
343
			"filtersOnID() is true with WHERE ID IN"
344
		);
345
346
		$query = new SQLSelect();
347
		$query->setWhere(array('"ID" IN ?' => array(1,2)));
348
		$this->assertTrue(
349
			$query->filtersOnID(),
350
			"filtersOnID() is true with parameterised WHERE ID IN"
351
		);
352
353
		$query = new SQLSelect();
354
		$query->setWhere("ID=5");
355
		$this->assertTrue(
356
			$query->filtersOnID(),
357
			"filtersOnID() is true with simple unquoted column name and no spaces in equals sign"
358
		);
359
360
		$query = new SQLSelect();
361
		$query->setWhere("Identifier = 5");
362
		$this->assertFalse(
363
			$query->filtersOnID(),
364
			"filtersOnID() is false with custom column name (starting with 'id')"
365
		);
366
367
		$query = new SQLSelect();
368
		$query->setWhere("ParentID = 5");
369
		$this->assertFalse(
370
			$query->filtersOnID(),
371
			"filtersOnID() is false with column name ending in 'ID'"
372
		);
373
374
		$query = new SQLSelect();
375
		$query->setWhere("MyTable.ID = 5");
376
		$this->assertTrue(
377
			$query->filtersOnID(),
378
			"filtersOnID() is true with table and column name"
379
		);
380
381
		$query = new SQLSelect();
382
		$query->setWhere("MyTable.ID = 5");
383
		$this->assertTrue(
384
			$query->filtersOnID(),
385
			"filtersOnID() is true with table and quoted column name "
386
		);
387
	}
388
389
	public function testFiltersOnFK() {
390
		$query = new SQLSelect();
391
		$query->setWhere("ID = 5");
392
		$this->assertFalse(
393
			$query->filtersOnFK(),
394
			"filtersOnFK() is true with simple unquoted column name"
395
		);
396
397
		$query = new SQLSelect();
398
		$query->setWhere("Identifier = 5");
399
		$this->assertFalse(
400
			$query->filtersOnFK(),
401
			"filtersOnFK() is false with custom column name (starting with 'id')"
402
		);
403
404
		$query = new SQLSelect();
405
		$query->setWhere("MyTable.ParentID = 5");
406
		$this->assertTrue(
407
			$query->filtersOnFK(),
408
			"filtersOnFK() is true with table and column name"
409
		);
410
411
		$query = new SQLSelect();
412
		$query->setWhere("MyTable.`ParentID`= 5");
413
		$this->assertTrue(
414
			$query->filtersOnFK(),
415
			"filtersOnFK() is true with table and quoted column name "
416
		);
417
	}
418
419
	public function testInnerJoin() {
420
		$query = new SQLSelect();
421
		$query->setFrom('MyTable');
422
		$query->addInnerJoin('MyOtherTable', 'MyOtherTable.ID = 2');
423
		$query->addLeftJoin('MyLastTable', 'MyOtherTable.ID = MyLastTable.ID');
424
425
		$this->assertSQLEquals('SELECT * FROM MyTable '.
426
			'INNER JOIN "MyOtherTable" ON MyOtherTable.ID = 2 '.
427
			'LEFT JOIN "MyLastTable" ON MyOtherTable.ID = MyLastTable.ID',
428
			$query->sql($parameters)
429
		);
430
431
		$query = new SQLSelect();
432
		$query->setFrom('MyTable');
433
		$query->addInnerJoin('MyOtherTable', 'MyOtherTable.ID = 2', 'table1');
434
		$query->addLeftJoin('MyLastTable', 'MyOtherTable.ID = MyLastTable.ID', 'table2');
435
436
		$this->assertSQLEquals('SELECT * FROM MyTable '.
437
			'INNER JOIN "MyOtherTable" AS "table1" ON MyOtherTable.ID = 2 '.
438
			'LEFT JOIN "MyLastTable" AS "table2" ON MyOtherTable.ID = MyLastTable.ID',
439
			$query->sql($parameters)
440
		);
441
	}
442
443
	public function testJoinSubSelect() {
444
445
		$query = new SQLSelect();
446
		$query->setFrom('MyTable');
447
		$query->addInnerJoin('(SELECT * FROM MyOtherTable)',
448
			'Mot.MyTableID = MyTable.ID', 'Mot');
449
		$query->addLeftJoin('(SELECT MyLastTable.MyOtherTableID, COUNT(1) as MyLastTableCount FROM MyLastTable '
450
			. 'GROUP BY MyOtherTableID)',
451
			'Mlt.MyOtherTableID = Mot.ID', 'Mlt');
452
		$query->setOrderBy('COALESCE(Mlt.MyLastTableCount, 0) DESC');
453
454
		$this->assertSQLEquals('SELECT *, COALESCE(Mlt.MyLastTableCount, 0) AS "_SortColumn0" FROM MyTable '.
455
			'INNER JOIN (SELECT * FROM MyOtherTable) AS "Mot" ON Mot.MyTableID = MyTable.ID ' .
456
			'LEFT JOIN (SELECT MyLastTable.MyOtherTableID, COUNT(1) as MyLastTableCount FROM MyLastTable '
457
			. 'GROUP BY MyOtherTableID) AS "Mlt" ON Mlt.MyOtherTableID = Mot.ID ' .
458
			'ORDER BY "_SortColumn0" DESC',
459
			$query->sql($parameters)
460
		);
461
462
	}
463
464
	public function testSetWhereAny() {
465
		$query = new SQLSelect();
466
		$query->setFrom('MyTable');
467
468
		$query->setWhereAny(array(
469
			'Monkey' => 'Chimp',
470
			'Color' => 'Brown'
471
		));
472
		$sql = $query->sql($parameters);
473
		$this->assertSQLEquals("SELECT * FROM MyTable WHERE ((Monkey = ?) OR (Color = ?))", $sql);
474
		$this->assertEquals(array('Chimp', 'Brown'), $parameters);
475
	}
476
477 View Code Duplication
	public function testSelectFirst() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
478
		// Test first from sequence
479
		$query = new SQLSelect();
480
		$query->setFrom('"SQLSelectTest_DO"');
481
		$query->setOrderBy('"Name"');
482
		$result = $query->firstRow()->execute();
483
484
		$records = array();
485
		foreach($result as $row) {
486
			$records[] = $row;
487
		}
488
489
		$this->assertCount(1, $records);
490
		$this->assertEquals('Object 1', $records[0]['Name']);
491
492
		// Test first from empty sequence
493
		$query = new SQLSelect();
494
		$query->setFrom('"SQLSelectTest_DO"');
495
		$query->setOrderBy('"Name"');
496
		$query->setWhere(array('"Name"' => 'Nonexistent Object'));
497
		$result = $query->firstRow()->execute();
498
499
		$records = array();
500
		foreach($result as $row) {
501
			$records[] = $row;
502
		}
503
504
		$this->assertCount(0, $records);
505
506
		// Test that given the last item, the 'first' in this list matches the last
507
		$query = new SQLSelect();
508
		$query->setFrom('"SQLSelectTest_DO"');
509
		$query->setOrderBy('"Name"');
510
		$query->setLimit(1, 1);
511
		$result = $query->firstRow()->execute();
512
513
		$records = array();
514
		foreach($result as $row) {
515
			$records[] = $row;
516
		}
517
518
		$this->assertCount(1, $records);
519
		$this->assertEquals('Object 2', $records[0]['Name']);
520
	}
521
522 View Code Duplication
	public function testSelectLast() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
523
		// Test last in sequence
524
		$query = new SQLSelect();
525
		$query->setFrom('"SQLSelectTest_DO"');
526
		$query->setOrderBy('"Name"');
527
		$result = $query->lastRow()->execute();
528
529
		$records = array();
530
		foreach($result as $row) {
531
			$records[] = $row;
532
		}
533
534
		$this->assertCount(1, $records);
535
		$this->assertEquals('Object 2', $records[0]['Name']);
536
537
		// Test last from empty sequence
538
		$query = new SQLSelect();
539
		$query->setFrom('"SQLSelectTest_DO"');
540
		$query->setOrderBy('"Name"');
541
		$query->setWhere(array("\"Name\" = 'Nonexistent Object'"));
542
		$result = $query->lastRow()->execute();
543
544
		$records = array();
545
		foreach($result as $row) {
546
			$records[] = $row;
547
		}
548
549
		$this->assertCount(0, $records);
550
551
		// Test that given the first item, the 'last' in this list matches the first
552
		$query = new SQLSelect();
553
		$query->setFrom('"SQLSelectTest_DO"');
554
		$query->setOrderBy('"Name"');
555
		$query->setLimit(1);
556
		$result = $query->lastRow()->execute();
557
558
		$records = array();
559
		foreach($result as $row) {
560
			$records[] = $row;
561
		}
562
563
		$this->assertCount(1, $records);
564
		$this->assertEquals('Object 1', $records[0]['Name']);
565
	}
566
567
	/**
568
	 * Tests aggregate() function
569
	 */
570
	public function testAggregate() {
571
		$query = new SQLSelect('"Common"');
572
		$query->setFrom('"SQLSelectTest_DO"');
573
		$query->setGroupBy('"Common"');
574
575
		$queryClone = $query->aggregate('COUNT(*)', 'cnt');
576
		$result = $queryClone->execute();
577
		$this->assertEquals(array(2), $result->column('cnt'));
578
	}
579
580
	/**
581
	 * Tests that an ORDER BY is only added if a LIMIT is set.
582
	 */
583
	public function testAggregateNoOrderByIfNoLimit() {
584
		$query = new SQLSelect();
585
		$query->setFrom('"SQLSelectTest_DO"');
586
		$query->setOrderBy('Common');
587
		$query->setLimit(array());
588
589
		$aggregate = $query->aggregate('MAX("ID")');
590
		$limit = $aggregate->getLimit();
591
		$this->assertEquals(array(), $aggregate->getOrderBy());
592
		$this->assertEquals(array(), $limit);
593
594
		$query = new SQLSelect();
595
		$query->setFrom('"SQLSelectTest_DO"');
596
		$query->setOrderBy('Common');
597
		$query->setLimit(2);
598
599
		$aggregate = $query->aggregate('MAX("ID")');
600
		$limit = $aggregate->getLimit();
601
		$this->assertEquals(array('Common' => 'ASC'), $aggregate->getOrderBy());
602
		$this->assertEquals(array('start' => 0, 'limit' => 2), $limit);
603
	}
604
605
	/**
606
	 * Test that "_SortColumn0" is added for an aggregate in the ORDER BY
607
	 * clause, in combination with a LIMIT and GROUP BY clause.
608
	 * For some databases, like MSSQL, this is a complicated scenario
609
	 * because a subselect needs to be done to query paginated data.
610
	 */
611
	public function testOrderByContainingAggregateAndLimitOffset() {
612
		$query = new SQLSelect();
613
		$query->setSelect(array('"Name"', '"Meta"'));
614
		$query->setFrom('"SQLSelectTest_DO"');
615
		$query->setOrderBy(array('MAX("Date")'));
616
		$query->setGroupBy(array('"Name"', '"Meta"'));
617
		$query->setLimit('1', '1');
618
619
		$records = array();
620
		foreach($query->execute() as $record) {
621
			$records[] = $record;
622
		}
623
624
		$this->assertCount(1, $records);
625
626
		$this->assertEquals('Object 2', $records[0]['Name']);
627
		$this->assertEquals('2012-05-01 09:00:00', $records['0']['_SortColumn0']);
628
	}
629
630
	/**
631
	 * Test that multiple order elements are maintained in the given order
632
	 */
633
	public function testOrderByMultiple() {
634
		if(DB::get_conn() instanceof MySQLDatabase) {
635
			$query = new SQLSelect();
636
			$query->setSelect(array('"Name"', '"Meta"'));
637
			$query->setFrom('"SQLSelectTest_DO"');
638
			$query->setOrderBy(array('MID("Name", 8, 1) DESC', '"Name" ASC'));
639
640
			$records = array();
641
			foreach($query->execute() as $record) {
642
				$records[] = $record;
643
			}
644
645
			$this->assertCount(2, $records);
646
647
			$this->assertEquals('Object 2', $records[0]['Name']);
648
			$this->assertEquals('2', $records[0]['_SortColumn0']);
649
650
			$this->assertEquals('Object 1', $records[1]['Name']);
651
			$this->assertEquals('1', $records[1]['_SortColumn0']);
652
		}
653
	}
654
655
	public function testSelect() {
656
		$query = new SQLSelect('"Title"', '"MyTable"');
657
		$query->addSelect('"TestField"');
658
		$this->assertSQLEquals(
659
			'SELECT "Title", "TestField" FROM "MyTable"',
660
			$query->sql()
661
		);
662
663
		// Test replacement of select
664
		$query->setSelect(array(
665
			'Field' => '"Field"',
666
			'AnotherAlias' => '"AnotherField"'
667
		));
668
		$this->assertSQLEquals(
669
			'SELECT "Field", "AnotherField" AS "AnotherAlias" FROM "MyTable"',
670
			$query->sql()
671
		);
672
673
		// Check that ' as ' selects don't get mistaken as aliases
674
		$query->addSelect(array(
675
			'Relevance' => "MATCH (Title, MenuTitle) AGAINST ('Two as One')"
676
		));
677
		$this->assertSQLEquals(
678
			'SELECT "Field", "AnotherField" AS "AnotherAlias", MATCH (Title, MenuTitle) AGAINST (' .
679
			'\'Two as One\') AS "Relevance" FROM "MyTable"',
680
			$query->sql()
681
		);
682
	}
683
684
	/**
685
	 * Test passing in a LIMIT with OFFSET clause string.
686
	 */
687
	public function testLimitSetFromClauseString() {
688
		$query = new SQLSelect();
689
		$query->setSelect('*');
690
		$query->setFrom('"SQLSelectTest_DO"');
691
692
		$query->setLimit('20 OFFSET 10');
693
		$limit = $query->getLimit();
694
		$this->assertEquals(20, $limit['limit']);
695
		$this->assertEquals(10, $limit['start']);
696
	}
697
698 View Code Duplication
	public function testParameterisedInnerJoins() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
699
		$query = new SQLSelect();
700
		$query->setSelect(array('"SQLSelectTest_DO"."Name"', '"SubSelect"."Count"'));
701
		$query->setFrom('"SQLSelectTest_DO"');
702
		$query->addInnerJoin(
703
			'(SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase" GROUP BY "Title" HAVING "Title" NOT LIKE ?)',
704
			'"SQLSelectTest_DO"."Name" = "SubSelect"."Title"',
705
			'SubSelect',
706
			20,
707
			array('%MyName%')
708
		);
709
		$query->addWhere(array('"SQLSelectTest_DO"."Date" > ?' => '2012-08-08 12:00'));
710
711
		$this->assertSQLEquals('SELECT "SQLSelectTest_DO"."Name", "SubSelect"."Count"
712
			FROM "SQLSelectTest_DO" INNER JOIN (SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase"
713
		   GROUP BY "Title" HAVING "Title" NOT LIKE ?) AS "SubSelect" ON "SQLSelectTest_DO"."Name" =
714
		   "SubSelect"."Title"
715
			WHERE ("SQLSelectTest_DO"."Date" > ?)', $query->sql($parameters)
716
		);
717
		$this->assertEquals(array('%MyName%', '2012-08-08 12:00'), $parameters);
718
		$query->execute();
719
	}
720
721 View Code Duplication
	public function testParameterisedLeftJoins() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
722
		$query = new SQLSelect();
723
		$query->setSelect(array('"SQLSelectTest_DO"."Name"', '"SubSelect"."Count"'));
724
		$query->setFrom('"SQLSelectTest_DO"');
725
		$query->addLeftJoin(
726
			'(SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase" GROUP BY "Title" HAVING "Title" NOT LIKE ?)',
727
			'"SQLSelectTest_DO"."Name" = "SubSelect"."Title"',
728
			'SubSelect',
729
			20,
730
			array('%MyName%')
731
		);
732
		$query->addWhere(array('"SQLSelectTest_DO"."Date" > ?' => '2012-08-08 12:00'));
733
734
		$this->assertSQLEquals('SELECT "SQLSelectTest_DO"."Name", "SubSelect"."Count"
735
			FROM "SQLSelectTest_DO" LEFT JOIN (SELECT "Title", COUNT(*) AS "Count" FROM "SQLSelectTestBase"
736
		   GROUP BY "Title" HAVING "Title" NOT LIKE ?) AS "SubSelect" ON "SQLSelectTest_DO"."Name" =
737
		   "SubSelect"."Title"
738
			WHERE ("SQLSelectTest_DO"."Date" > ?)', $query->sql($parameters)
739
		);
740
		$this->assertEquals(array('%MyName%', '2012-08-08 12:00'), $parameters);
741
		$query->execute();
742
	}
743
}
744
745
class SQLSelectTest_DO extends DataObject implements TestOnly {
746
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
747
		"Name" => "Varchar",
748
		"Meta" => "Varchar",
749
		"Common" => "Varchar",
750
		"Date" => "SS_Datetime"
751
	);
752
}
753
754
class SQLSelectTestBase extends DataObject implements TestOnly {
755
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
756
		"Title" => "Varchar",
757
	);
758
}
759
760
class SQLSelectTestChild extends SQLSelectTestBase {
761
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
762
		"Name" => "Varchar",
763
	);
764
}
765