Upgraded drupal core with security updates
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Database / SelectSubqueryTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\Database;
4
5 /**
6  * Tests the Select query builder.
7  *
8  * @group Database
9  */
10 class SelectSubqueryTest extends DatabaseTestBase {
11
12   /**
13    * Tests that we can use a subquery in a FROM clause.
14    */
15   public function testFromSubquerySelect() {
16     // Create a subquery, which is just a normal query object.
17     $subquery = db_select('test_task', 'tt');
18     $subquery->addField('tt', 'pid', 'pid');
19     $subquery->addField('tt', 'task', 'task');
20     $subquery->condition('priority', 1);
21
22     for ($i = 0; $i < 2; $i++) {
23       // Create another query that joins against the virtual table resulting
24       // from the subquery.
25       $select = db_select($subquery, 'tt2');
26       $select->join('test', 't', 't.id=tt2.pid');
27       $select->addField('t', 'name');
28       if ($i) {
29         // Use a different number of conditions here to confuse the subquery
30         // placeholder counter, testing https://www.drupal.org/node/1112854.
31         $select->condition('name', 'John');
32       }
33       $select->condition('task', 'code');
34
35       // The resulting query should be equivalent to:
36       // SELECT t.name
37       // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt
38       //   INNER JOIN test t ON t.id=tt.pid
39       // WHERE tt.task = 'code'
40       $people = $select->execute()->fetchCol();
41
42       $this->assertCount(1, $people, 'Returned the correct number of rows.');
43     }
44   }
45
46   /**
47    * Tests that we can use a subquery in a FROM clause with a LIMIT.
48    */
49   public function testFromSubquerySelectWithLimit() {
50     // Create a subquery, which is just a normal query object.
51     $subquery = db_select('test_task', 'tt');
52     $subquery->addField('tt', 'pid', 'pid');
53     $subquery->addField('tt', 'task', 'task');
54     $subquery->orderBy('priority', 'DESC');
55     $subquery->range(0, 1);
56
57     // Create another query that joins against the virtual table resulting
58     // from the subquery.
59     $select = db_select($subquery, 'tt2');
60     $select->join('test', 't', 't.id=tt2.pid');
61     $select->addField('t', 'name');
62
63     // The resulting query should be equivalent to:
64     // SELECT t.name
65     // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt
66     //   INNER JOIN test t ON t.id=tt.pid
67     $people = $select->execute()->fetchCol();
68
69     $this->assertCount(1, $people, 'Returned the correct number of rows.');
70   }
71
72   /**
73    * Tests that we can use a subquery with an IN operator in a WHERE clause.
74    */
75   public function testConditionSubquerySelect() {
76     // Create a subquery, which is just a normal query object.
77     $subquery = db_select('test_task', 'tt');
78     $subquery->addField('tt', 'pid', 'pid');
79     $subquery->condition('tt.priority', 1);
80
81     // Create another query that joins against the virtual table resulting
82     // from the subquery.
83     $select = db_select('test_task', 'tt2');
84     $select->addField('tt2', 'task');
85     $select->condition('tt2.pid', $subquery, 'IN');
86
87     // The resulting query should be equivalent to:
88     // SELECT tt2.name
89     // FROM test tt2
90     // WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1)
91     $people = $select->execute()->fetchCol();
92     $this->assertCount(5, $people, 'Returned the correct number of rows.');
93   }
94
95   /**
96    * Test that we can use a subquery with a relational operator in a WHERE clause.
97    */
98   public function testConditionSubquerySelect2() {
99     // Create a subquery, which is just a normal query object.
100     $subquery = db_select('test', 't2');
101     $subquery->addExpression('AVG(t2.age)');
102
103     // Create another query that adds a clause using the subquery.
104     $select = db_select('test', 't');
105     $select->addField('t', 'name');
106     $select->condition('t.age', $subquery, '<');
107
108     // The resulting query should be equivalent to:
109     // SELECT t.name
110     // FROM test t
111     // WHERE t.age < (SELECT AVG(t2.age) FROM test t2)
112     $people = $select->execute()->fetchCol();
113     $this->assertEquals(['John', 'Paul'], $people, 'Returned Paul and John.', 0.0, 10, TRUE);
114   }
115
116   /**
117    * Test that we can use 2 subqueries with a relational operator in a WHERE clause.
118    */
119   public function testConditionSubquerySelect3() {
120     // Create subquery 1, which is just a normal query object.
121     $subquery1 = db_select('test_task', 'tt');
122     $subquery1->addExpression('AVG(tt.priority)');
123     $subquery1->where('tt.pid = t.id');
124
125     // Create subquery 2, which is just a normal query object.
126     $subquery2 = db_select('test_task', 'tt2');
127     $subquery2->addExpression('AVG(tt2.priority)');
128
129     // Create another query that adds a clause using the subqueries.
130     $select = db_select('test', 't');
131     $select->addField('t', 'name');
132     $select->condition($subquery1, $subquery2, '>');
133
134     // The resulting query should be equivalent to:
135     // SELECT t.name
136     // FROM test t
137     // WHERE (SELECT AVG(tt.priority) FROM test_task tt WHERE tt.pid = t.id) > (SELECT AVG(tt2.priority) FROM test_task tt2)
138     $people = $select->execute()->fetchCol();
139     $this->assertEquals(['John'], $people, 'Returned John.', 0.0, 10, TRUE);
140   }
141
142   /**
143    * Test that we can use multiple subqueries.
144    *
145    * This test uses a subquery at the left hand side and multiple subqueries at
146    * the right hand side. The test query may not be that logical but that's due
147    * to the limited amount of data and tables. 'Valid' use cases do exist :)
148    */
149   public function testConditionSubquerySelect4() {
150     // Create subquery 1, which is just a normal query object.
151     $subquery1 = db_select('test_task', 'tt');
152     $subquery1->addExpression('AVG(tt.priority)');
153     $subquery1->where('tt.pid = t.id');
154
155     // Create subquery 2, which is just a normal query object.
156     $subquery2 = db_select('test_task', 'tt2');
157     $subquery2->addExpression('MIN(tt2.priority)');
158     $subquery2->where('tt2.pid <> t.id');
159
160     // Create subquery 3, which is just a normal query object.
161     $subquery3 = db_select('test_task', 'tt3');
162     $subquery3->addExpression('AVG(tt3.priority)');
163     $subquery3->where('tt3.pid <> t.id');
164
165     // Create another query that adds a clause using the subqueries.
166     $select = db_select('test', 't');
167     $select->addField('t', 'name');
168     $select->condition($subquery1, [$subquery2, $subquery3], 'BETWEEN');
169
170     // The resulting query should be equivalent to:
171     // SELECT t.name AS name
172     // FROM {test} t
173     // WHERE (SELECT AVG(tt.priority) AS expression FROM {test_task} tt WHERE (tt.pid = t.id))
174     //   BETWEEN (SELECT MIN(tt2.priority) AS expression FROM {test_task} tt2 WHERE (tt2.pid <> t.id))
175     //       AND (SELECT AVG(tt3.priority) AS expression FROM {test_task} tt3 WHERE (tt3.pid <> t.id));
176     $people = $select->execute()->fetchCol();
177     $this->assertEquals(['George', 'Paul'], $people, 'Returned George and Paul.', 0.0, 10, TRUE);
178   }
179
180   /**
181    * Tests that we can use a subquery in a JOIN clause.
182    */
183   public function testJoinSubquerySelect() {
184     // Create a subquery, which is just a normal query object.
185     $subquery = db_select('test_task', 'tt');
186     $subquery->addField('tt', 'pid', 'pid');
187     $subquery->condition('priority', 1);
188
189     // Create another query that joins against the virtual table resulting
190     // from the subquery.
191     $select = db_select('test', 't');
192     $select->join($subquery, 'tt', 't.id=tt.pid');
193     $select->addField('t', 'name');
194
195     // The resulting query should be equivalent to:
196     // SELECT t.name
197     // FROM test t
198     //   INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid
199     $people = $select->execute()->fetchCol();
200
201     $this->assertCount(2, $people, 'Returned the correct number of rows.');
202   }
203
204   /**
205    * Tests EXISTS subquery conditionals on SELECT statements.
206    *
207    * We essentially select all rows from the {test} table that have matching
208    * rows in the {test_people} table based on the shared name column.
209    */
210   public function testExistsSubquerySelect() {
211     // Put George into {test_people}.
212     db_insert('test_people')
213       ->fields([
214         'name' => 'George',
215         'age' => 27,
216         'job' => 'Singer',
217       ])
218       ->execute();
219     // Base query to {test}.
220     $query = db_select('test', 't')
221       ->fields('t', ['name']);
222     // Subquery to {test_people}.
223     $subquery = db_select('test_people', 'tp')
224       ->fields('tp', ['name'])
225       ->where('tp.name = t.name');
226     $query->exists($subquery);
227     $result = $query->execute();
228
229     // Ensure that we got the right record.
230     $record = $result->fetch();
231     $this->assertEquals('George', $record->name, 'Fetched name is correct using EXISTS query.');
232   }
233
234   /**
235    * Tests NOT EXISTS subquery conditionals on SELECT statements.
236    *
237    * We essentially select all rows from the {test} table that don't have
238    * matching rows in the {test_people} table based on the shared name column.
239    */
240   public function testNotExistsSubquerySelect() {
241     // Put George into {test_people}.
242     db_insert('test_people')
243       ->fields([
244         'name' => 'George',
245         'age' => 27,
246         'job' => 'Singer',
247       ])
248       ->execute();
249
250     // Base query to {test}.
251     $query = db_select('test', 't')
252       ->fields('t', ['name']);
253     // Subquery to {test_people}.
254     $subquery = db_select('test_people', 'tp')
255       ->fields('tp', ['name'])
256       ->where('tp.name = t.name');
257     $query->notExists($subquery);
258
259     // Ensure that we got the right number of records.
260     $people = $query->execute()->fetchCol();
261     $this->assertCount(3, $people, 'NOT EXISTS query returned the correct results.');
262   }
263
264 }