SelectTest.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. <?php
  2. namespace Drupal\KernelTests\Core\Database;
  3. use Drupal\Core\Database\InvalidQueryException;
  4. use Drupal\Core\Database\Database;
  5. use Drupal\Core\Database\DatabaseExceptionWrapper;
  6. /**
  7. * Tests the Select query builder.
  8. *
  9. * @group Database
  10. */
  11. class SelectTest extends DatabaseTestBase {
  12. /**
  13. * Tests rudimentary SELECT statements.
  14. */
  15. public function testSimpleSelect() {
  16. $query = $this->connection->select('test');
  17. $query->addField('test', 'name');
  18. $query->addField('test', 'age', 'age');
  19. $num_records = $query->countQuery()->execute()->fetchField();
  20. $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
  21. }
  22. /**
  23. * Tests rudimentary SELECT statement with a COMMENT.
  24. */
  25. public function testSimpleComment() {
  26. $query = $this->connection->select('test')->comment('Testing query comments');
  27. $query->addField('test', 'name');
  28. $query->addField('test', 'age', 'age');
  29. $result = $query->execute();
  30. $records = $result->fetchAll();
  31. $query = (string) $query;
  32. $expected = "/* Testing query comments */";
  33. $this->assertCount(4, $records, 'Returned the correct number of rows.');
  34. $this->assertStringContainsString($expected, $query, 'The flattened query contains the comment string.');
  35. }
  36. /**
  37. * Tests query COMMENT system against vulnerabilities.
  38. */
  39. public function testVulnerableComment() {
  40. $query = $this->connection->select('test')->comment('Testing query comments */ SELECT nid FROM {node}; --');
  41. $query->addField('test', 'name');
  42. $query->addField('test', 'age', 'age');
  43. $result = $query->execute();
  44. $records = $result->fetchAll();
  45. $query = (string) $query;
  46. $expected = "/* Testing query comments * / SELECT nid FROM {node}. -- */";
  47. // Check the returned number of rows.
  48. $this->assertCount(4, $records);
  49. // Check that the flattened query contains the sanitized comment string.
  50. $this->assertStringContainsString($expected, $query);
  51. $connection = Database::getConnection();
  52. foreach ($this->makeCommentsProvider() as $test_set) {
  53. list($expected, $comments) = $test_set;
  54. $this->assertEquals($expected, $connection->makeComment($comments));
  55. }
  56. }
  57. /**
  58. * Provides expected and input values for testVulnerableComment().
  59. */
  60. public function makeCommentsProvider() {
  61. return [
  62. [
  63. '/* */ ',
  64. [''],
  65. ],
  66. // Try and close the comment early.
  67. [
  68. '/* Exploit * / DROP TABLE node. -- */ ',
  69. ['Exploit */ DROP TABLE node; --'],
  70. ],
  71. // Variations on comment closing.
  72. [
  73. '/* Exploit * / * / DROP TABLE node. -- */ ',
  74. ['Exploit */*/ DROP TABLE node; --'],
  75. ],
  76. [
  77. '/* Exploit * * // DROP TABLE node. -- */ ',
  78. ['Exploit **// DROP TABLE node; --'],
  79. ],
  80. // Try closing the comment in the second string which is appended.
  81. [
  82. '/* Exploit * / DROP TABLE node. --. Another try * / DROP TABLE node. -- */ ',
  83. ['Exploit */ DROP TABLE node; --', 'Another try */ DROP TABLE node; --'],
  84. ],
  85. ];
  86. }
  87. /**
  88. * Tests basic conditionals on SELECT statements.
  89. */
  90. public function testSimpleSelectConditional() {
  91. $query = $this->connection->select('test');
  92. $name_field = $query->addField('test', 'name');
  93. $age_field = $query->addField('test', 'age', 'age');
  94. $query->condition('age', 27);
  95. $result = $query->execute();
  96. // Check that the aliases are being created the way we want.
  97. $this->assertEqual($name_field, 'name', 'Name field alias is correct.');
  98. $this->assertEqual($age_field, 'age', 'Age field alias is correct.');
  99. // Ensure that we got the right record.
  100. $record = $result->fetch();
  101. $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
  102. $this->assertEqual($record->$age_field, 27, 'Fetched age is correct.');
  103. }
  104. /**
  105. * Tests SELECT statements with expressions.
  106. */
  107. public function testSimpleSelectExpression() {
  108. $query = $this->connection->select('test');
  109. $name_field = $query->addField('test', 'name');
  110. $age_field = $query->addExpression("age*2", 'double_age');
  111. $query->condition('age', 27);
  112. $result = $query->execute();
  113. // Check that the aliases are being created the way we want.
  114. $this->assertEqual($name_field, 'name', 'Name field alias is correct.');
  115. $this->assertEqual($age_field, 'double_age', 'Age field alias is correct.');
  116. // Ensure that we got the right record.
  117. $record = $result->fetch();
  118. $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
  119. $this->assertEqual($record->$age_field, 27 * 2, 'Fetched age expression is correct.');
  120. }
  121. /**
  122. * Tests SELECT statements with multiple expressions.
  123. */
  124. public function testSimpleSelectExpressionMultiple() {
  125. $query = $this->connection->select('test');
  126. $name_field = $query->addField('test', 'name');
  127. $age_double_field = $query->addExpression("age*2");
  128. $age_triple_field = $query->addExpression("age*3");
  129. $query->condition('age', 27);
  130. $result = $query->execute();
  131. // Check that the aliases are being created the way we want.
  132. $this->assertEqual($age_double_field, 'expression', 'Double age field alias is correct.');
  133. $this->assertEqual($age_triple_field, 'expression_2', 'Triple age field alias is correct.');
  134. // Ensure that we got the right record.
  135. $record = $result->fetch();
  136. $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
  137. $this->assertEqual($record->$age_double_field, 27 * 2, 'Fetched double age expression is correct.');
  138. $this->assertEqual($record->$age_triple_field, 27 * 3, 'Fetched triple age expression is correct.');
  139. }
  140. /**
  141. * Tests adding multiple fields to a SELECT statement at the same time.
  142. */
  143. public function testSimpleSelectMultipleFields() {
  144. $record = $this->connection->select('test')
  145. ->fields('test', ['id', 'name', 'age', 'job'])
  146. ->condition('age', 27)
  147. ->execute()->fetchObject();
  148. // Check that all fields we asked for are present.
  149. $this->assertNotNull($record->id, 'ID field is present.');
  150. $this->assertNotNull($record->name, 'Name field is present.');
  151. $this->assertNotNull($record->age, 'Age field is present.');
  152. $this->assertNotNull($record->job, 'Job field is present.');
  153. // Ensure that we got the right record.
  154. // Check that all fields we asked for are present.
  155. $this->assertEqual($record->id, 2, 'ID field has the correct value.');
  156. $this->assertEqual($record->name, 'George', 'Name field has the correct value.');
  157. $this->assertEqual($record->age, 27, 'Age field has the correct value.');
  158. $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.');
  159. }
  160. /**
  161. * Tests adding all fields from a given table to a SELECT statement.
  162. */
  163. public function testSimpleSelectAllFields() {
  164. $record = $this->connection->select('test')
  165. ->fields('test')
  166. ->condition('age', 27)
  167. ->execute()->fetchObject();
  168. // Check that all fields we asked for are present.
  169. $this->assertNotNull($record->id, 'ID field is present.');
  170. $this->assertNotNull($record->name, 'Name field is present.');
  171. $this->assertNotNull($record->age, 'Age field is present.');
  172. $this->assertNotNull($record->job, 'Job field is present.');
  173. // Ensure that we got the right record.
  174. // Check that all fields we asked for are present.
  175. $this->assertEqual($record->id, 2, 'ID field has the correct value.');
  176. $this->assertEqual($record->name, 'George', 'Name field has the correct value.');
  177. $this->assertEqual($record->age, 27, 'Age field has the correct value.');
  178. $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.');
  179. }
  180. /**
  181. * Tests that a comparison with NULL is always FALSE.
  182. */
  183. public function testNullCondition() {
  184. $this->ensureSampleDataNull();
  185. $names = $this->connection->select('test_null', 'tn')
  186. ->fields('tn', ['name'])
  187. ->condition('age', NULL)
  188. ->execute()->fetchCol();
  189. $this->assertCount(0, $names, 'No records found when comparing to NULL.');
  190. }
  191. /**
  192. * Tests that we can find a record with a NULL value.
  193. */
  194. public function testIsNullCondition() {
  195. $this->ensureSampleDataNull();
  196. $names = $this->connection->select('test_null', 'tn')
  197. ->fields('tn', ['name'])
  198. ->isNull('age')
  199. ->execute()->fetchCol();
  200. $this->assertCount(1, $names, 'Correct number of records found with NULL age.');
  201. $this->assertEqual($names[0], 'Fozzie', 'Correct record returned for NULL age.');
  202. }
  203. /**
  204. * Tests that we can find a record without a NULL value.
  205. */
  206. public function testIsNotNullCondition() {
  207. $this->ensureSampleDataNull();
  208. $names = $this->connection->select('test_null', 'tn')
  209. ->fields('tn', ['name'])
  210. ->isNotNull('tn.age')
  211. ->orderBy('name')
  212. ->execute()->fetchCol();
  213. $this->assertCount(2, $names, 'Correct number of records found withNOT NULL age.');
  214. $this->assertEqual($names[0], 'Gonzo', 'Correct record returned for NOT NULL age.');
  215. $this->assertEqual($names[1], 'Kermit', 'Correct record returned for NOT NULL age.');
  216. }
  217. /**
  218. * Tests that we can force a query to return an empty result.
  219. */
  220. public function testAlwaysFalseCondition() {
  221. $names = $this->connection->select('test', 'test')
  222. ->fields('test', ['name'])
  223. ->condition('age', 27)
  224. ->execute()->fetchCol();
  225. $this->assertCount(1, $names);
  226. $this->assertSame($names[0], 'George');
  227. $names = $this->connection->select('test', 'test')
  228. ->fields('test', ['name'])
  229. ->condition('age', 27)
  230. ->alwaysFalse()
  231. ->execute()->fetchCol();
  232. $this->assertCount(0, $names);
  233. }
  234. /**
  235. * Tests that we can force an extended query to return an empty result.
  236. */
  237. public function testExtenderAlwaysFalseCondition() {
  238. $names = $this->connection->select('test', 'test')
  239. ->extend('Drupal\Core\Database\Query\SelectExtender')
  240. ->fields('test', ['name'])
  241. ->condition('age', 27)
  242. ->execute()->fetchCol();
  243. $this->assertCount(1, $names);
  244. $this->assertSame($names[0], 'George');
  245. $names = $this->connection->select('test', 'test')
  246. ->extend('Drupal\Core\Database\Query\SelectExtender')
  247. ->fields('test', ['name'])
  248. ->condition('age', 27)
  249. ->alwaysFalse()
  250. ->execute()->fetchCol();
  251. $this->assertCount(0, $names);
  252. }
  253. /**
  254. * Tests that we can UNION multiple Select queries together.
  255. *
  256. * This is semantically equal to UNION DISTINCT, so we don't explicitly test
  257. * that.
  258. */
  259. public function testUnion() {
  260. $query_1 = $this->connection->select('test', 't')
  261. ->fields('t', ['name'])
  262. ->condition('age', [27, 28], 'IN');
  263. $query_2 = $this->connection->select('test', 't')
  264. ->fields('t', ['name'])
  265. ->condition('age', 28);
  266. $query_1->union($query_2);
  267. $names = $query_1->execute()->fetchCol();
  268. // Ensure we only get 2 records.
  269. $this->assertCount(2, $names, 'UNION correctly discarded duplicates.');
  270. sort($names);
  271. $this->assertEquals(['George', 'Ringo'], $names);
  272. }
  273. /**
  274. * Tests that we can UNION ALL multiple SELECT queries together.
  275. */
  276. public function testUnionAll() {
  277. $query_1 = $this->connection->select('test', 't')
  278. ->fields('t', ['name'])
  279. ->condition('age', [27, 28], 'IN');
  280. $query_2 = $this->connection->select('test', 't')
  281. ->fields('t', ['name'])
  282. ->condition('age', 28);
  283. $query_1->union($query_2, 'ALL');
  284. $names = $query_1->execute()->fetchCol();
  285. // Ensure we get all 3 records.
  286. $this->assertCount(3, $names, 'UNION ALL correctly preserved duplicates.');
  287. $this->assertEqual($names[0], 'George', 'First query returned correct first name.');
  288. $this->assertEqual($names[1], 'Ringo', 'Second query returned correct second name.');
  289. $this->assertEqual($names[2], 'Ringo', 'Third query returned correct name.');
  290. }
  291. /**
  292. * Tests that we can get a count query for a UNION Select query.
  293. */
  294. public function testUnionCount() {
  295. $query_1 = $this->connection->select('test', 't')
  296. ->fields('t', ['name', 'age'])
  297. ->condition('age', [27, 28], 'IN');
  298. $query_2 = $this->connection->select('test', 't')
  299. ->fields('t', ['name', 'age'])
  300. ->condition('age', 28);
  301. $query_1->union($query_2, 'ALL');
  302. $names = $query_1->execute()->fetchCol();
  303. $query_3 = $query_1->countQuery();
  304. $count = $query_3->execute()->fetchField();
  305. // Ensure the counts match.
  306. $this->assertEqual(count($names), $count, "The count query's result matched the number of rows in the UNION query.");
  307. }
  308. /**
  309. * Tests that we can UNION multiple Select queries together and set the ORDER.
  310. */
  311. public function testUnionOrder() {
  312. // This gives George and Ringo.
  313. $query_1 = $this->connection->select('test', 't')
  314. ->fields('t', ['name'])
  315. ->condition('age', [27, 28], 'IN');
  316. // This gives Paul.
  317. $query_2 = $this->connection->select('test', 't')
  318. ->fields('t', ['name'])
  319. ->condition('age', 26);
  320. $query_1->union($query_2);
  321. $query_1->orderBy('name', 'DESC');
  322. $names = $query_1->execute()->fetchCol();
  323. // Ensure we get all 3 records.
  324. $this->assertCount(3, $names, 'UNION returned rows from both queries.');
  325. // Ensure that the names are in the correct reverse alphabetical order,
  326. // regardless of which query they came from.
  327. $this->assertEqual($names[0], 'Ringo', 'First query returned correct name.');
  328. $this->assertEqual($names[1], 'Paul', 'Second query returned correct name.');
  329. $this->assertEqual($names[2], 'George', 'Third query returned correct name.');
  330. }
  331. /**
  332. * Tests that we can UNION multiple Select queries together with and a LIMIT.
  333. */
  334. public function testUnionOrderLimit() {
  335. // This gives George and Ringo.
  336. $query_1 = $this->connection->select('test', 't')
  337. ->fields('t', ['name'])
  338. ->condition('age', [27, 28], 'IN');
  339. // This gives Paul.
  340. $query_2 = $this->connection->select('test', 't')
  341. ->fields('t', ['name'])
  342. ->condition('age', 26);
  343. $query_1->union($query_2);
  344. $query_1->orderBy('name', 'DESC');
  345. $query_1->range(0, 2);
  346. $names = $query_1->execute()->fetchCol();
  347. // Ensure we get all only 2 of the 3 records.
  348. $this->assertCount(2, $names, 'UNION with a limit returned rows from both queries.');
  349. // Ensure that the names are in the correct reverse alphabetical order,
  350. // regardless of which query they came from.
  351. $this->assertEqual($names[0], 'Ringo', 'First query returned correct name.');
  352. $this->assertEqual($names[1], 'Paul', 'Second query returned correct name.');
  353. }
  354. /**
  355. * Tests that random ordering of queries works.
  356. *
  357. * We take the approach of testing the Drupal layer only, rather than trying
  358. * to test that the database's random number generator actually produces
  359. * random queries (which is very difficult to do without an unacceptable risk
  360. * of the test failing by accident).
  361. *
  362. * Therefore, in this test we simply run the same query twice and assert that
  363. * the two results are reordered versions of each other (as well as of the
  364. * same query without the random ordering). It is reasonable to assume that
  365. * if we run the same select query twice and the results are in a different
  366. * order each time, the only way this could happen is if we have successfully
  367. * triggered the database's random ordering functionality.
  368. */
  369. public function testRandomOrder() {
  370. // Use 52 items, so the chance that this test fails by accident will be the
  371. // same as the chance that a deck of cards will come out in the same order
  372. // after shuffling it (in other words, nearly impossible).
  373. $number_of_items = 52;
  374. while ($this->connection->query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) {
  375. $this->connection->insert('test')->fields(['name' => $this->randomMachineName()])->execute();
  376. }
  377. // First select the items in order and make sure we get an ordered list.
  378. $expected_ids = range(1, $number_of_items);
  379. $ordered_ids = $this->connection->select('test', 't')
  380. ->fields('t', ['id'])
  381. ->range(0, $number_of_items)
  382. ->orderBy('id')
  383. ->execute()
  384. ->fetchCol();
  385. $this->assertEqual($ordered_ids, $expected_ids, 'A query without random ordering returns IDs in the correct order.');
  386. // Now perform the same query, but instead choose a random ordering. We
  387. // expect this to contain a differently ordered version of the original
  388. // result.
  389. $randomized_ids = $this->connection->select('test', 't')
  390. ->fields('t', ['id'])
  391. ->range(0, $number_of_items)
  392. ->orderRandom()
  393. ->execute()
  394. ->fetchCol();
  395. $this->assertNotEqual($randomized_ids, $ordered_ids, 'A query with random ordering returns an unordered set of IDs.');
  396. $sorted_ids = $randomized_ids;
  397. sort($sorted_ids);
  398. $this->assertEqual($sorted_ids, $ordered_ids, 'After sorting the random list, the result matches the original query.');
  399. // Now perform the exact same query again, and make sure the order is
  400. // different.
  401. $randomized_ids_second_set = $this->connection->select('test', 't')
  402. ->fields('t', ['id'])
  403. ->range(0, $number_of_items)
  404. ->orderRandom()
  405. ->execute()
  406. ->fetchCol();
  407. $this->assertNotEqual($randomized_ids_second_set, $randomized_ids, 'Performing the query with random ordering a second time returns IDs in a different order.');
  408. $sorted_ids_second_set = $randomized_ids_second_set;
  409. sort($sorted_ids_second_set);
  410. $this->assertEqual($sorted_ids_second_set, $sorted_ids, 'After sorting the second random list, the result matches the sorted version of the first random list.');
  411. }
  412. /**
  413. * Data provider for testRegularExpressionCondition().
  414. *
  415. * @return array[]
  416. * Returns data-set elements with:
  417. * - the expected result of the query
  418. * - the table column to do the search on.
  419. * - the regular expression pattern to search for.
  420. * - the regular expression operator 'REGEXP' or 'NOT REGEXP'.
  421. */
  422. public function providerRegularExpressionCondition() {
  423. return [
  424. [['John'], 'name', 'hn$', 'REGEXP'],
  425. [['Paul'], 'name', '^Pau', 'REGEXP'],
  426. [['George', 'Ringo'], 'name', 'Ringo|George', 'REGEXP'],
  427. [['Pete'], 'job', '#Drummer', 'REGEXP'],
  428. [[], 'job', '#Singer', 'REGEXP'],
  429. [['Paul', 'Pete'], 'age', '2[6]', 'REGEXP'],
  430. [['George', 'Paul', 'Pete', 'Ringo'], 'name', 'hn$', 'NOT REGEXP'],
  431. [['George', 'John', 'Pete', 'Ringo'], 'name', '^Pau', 'NOT REGEXP'],
  432. [['John', 'Paul', 'Pete'], 'name', 'Ringo|George', 'NOT REGEXP'],
  433. [['George', 'John', 'Paul', 'Ringo'], 'job', '#Drummer', 'NOT REGEXP'],
  434. [['George', 'John', 'Paul', 'Pete', 'Ringo'], 'job', '#Singer', 'NOT REGEXP'],
  435. [['George', 'John', 'Ringo'], 'age', '2[6]', 'NOT REGEXP'],
  436. ];
  437. }
  438. /**
  439. * Tests that filter by 'REGEXP' and 'NOT REGEXP' works as expected.
  440. *
  441. * @dataProvider providerRegularExpressionCondition
  442. */
  443. public function testRegularExpressionCondition($expected, $column, $pattern, $operator) {
  444. $database = $this->container->get('database');
  445. $database->insert('test')
  446. ->fields([
  447. 'name' => 'Pete',
  448. 'age' => 26,
  449. 'job' => '#Drummer',
  450. ])
  451. ->execute();
  452. $query = $database->select('test', 't');
  453. $query->addField('t', 'name');
  454. $query->condition("t.$column", $pattern, $operator);
  455. $result = $query->execute()->fetchCol();
  456. sort($result);
  457. $this->assertEquals($expected, $result);
  458. }
  459. /**
  460. * Tests that aliases are renamed when they are duplicates.
  461. */
  462. public function testSelectDuplicateAlias() {
  463. $query = $this->connection->select('test', 't');
  464. $alias1 = $query->addField('t', 'name', 'the_alias');
  465. $alias2 = $query->addField('t', 'age', 'the_alias');
  466. $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.');
  467. }
  468. /**
  469. * Tests that an invalid count query throws an exception.
  470. */
  471. public function testInvalidSelectCount() {
  472. try {
  473. // This query will fail because the table does not exist.
  474. // Normally it would throw an exception but we are suppressing
  475. // it with the throw_exception option.
  476. $options['throw_exception'] = FALSE;
  477. $this->connection->select('some_table_that_does_not_exist', 't', $options)
  478. ->fields('t')
  479. ->countQuery()
  480. ->execute();
  481. }
  482. catch (\Exception $e) {
  483. $this->fail('$options[\'throw_exception\'] is FALSE, but Exception thrown for invalid query.');
  484. }
  485. try {
  486. // This query will fail because the table does not exist.
  487. $this->connection->select('some_table_that_does_not_exist', 't')
  488. ->fields('t')
  489. ->countQuery()
  490. ->execute();
  491. $this->fail('No Exception thrown.');
  492. }
  493. catch (\Exception $e) {
  494. $this->assertInstanceOf(DatabaseExceptionWrapper::class, $e);
  495. }
  496. }
  497. /**
  498. * Tests thrown exception for IN query conditions with an empty array.
  499. */
  500. public function testEmptyInCondition() {
  501. try {
  502. $this->connection->select('test', 't')
  503. ->fields('t')
  504. ->condition('age', [], 'IN')
  505. ->execute();
  506. $this->fail('Expected exception not thrown');
  507. }
  508. catch (InvalidQueryException $e) {
  509. $this->assertEqual("Query condition 'age IN ()' cannot be empty.", $e->getMessage());
  510. }
  511. try {
  512. $this->connection->select('test', 't')
  513. ->fields('t')
  514. ->condition('age', [], 'NOT IN')
  515. ->execute();
  516. $this->fail('Expected exception not thrown');
  517. }
  518. catch (InvalidQueryException $e) {
  519. $this->assertEqual("Query condition 'age NOT IN ()' cannot be empty.", $e->getMessage());
  520. }
  521. }
  522. }