QueryTest.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * Redistributions of files must retain the above copyright notice
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @since 3.0.0
  12. * @license https://opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Cake\Test\TestCase\Database;
  15. use Cake\Database\Connection;
  16. use Cake\Database\Expression\CommonTableExpression;
  17. use Cake\Database\Expression\IdentifierExpression;
  18. use Cake\Database\ExpressionInterface;
  19. use Cake\Database\Query;
  20. use Cake\Database\ValueBinder;
  21. use Cake\Datasource\ConnectionManager;
  22. use Cake\TestSuite\TestCase;
  23. use InvalidArgumentException;
  24. /**
  25. * Tests Query class
  26. */
  27. class QueryTest extends TestCase
  28. {
  29. use QueryAssertsTrait;
  30. protected array $fixtures = [
  31. 'core.Articles',
  32. 'core.Authors',
  33. 'core.Comments',
  34. 'core.Profiles',
  35. 'core.MenuLinkTrees',
  36. ];
  37. /**
  38. * @var \Cake\Database\Connection
  39. */
  40. protected $connection;
  41. /**
  42. * @var bool
  43. */
  44. protected $autoQuote;
  45. protected Query $query;
  46. public function setUp(): void
  47. {
  48. parent::setUp();
  49. $this->connection = ConnectionManager::get('test');
  50. $this->autoQuote = $this->connection->getDriver()->isAutoQuotingEnabled();
  51. $this->query = $this->newQuery();
  52. }
  53. public function tearDown(): void
  54. {
  55. parent::tearDown();
  56. $this->connection->getDriver()->enableAutoQuoting($this->autoQuote);
  57. unset($this->query);
  58. unset($this->connection);
  59. }
  60. public function testConnectionRoles(): void
  61. {
  62. // Defaults to write role
  63. $this->assertSame(Connection::ROLE_WRITE, $this->connection->insertQuery()->getConnectionRole());
  64. $selectQuery = $this->connection->selectQuery();
  65. $this->assertSame(Connection::ROLE_WRITE, $selectQuery->getConnectionRole());
  66. // Can set read role for select queries
  67. $this->assertSame(Connection::ROLE_READ, $selectQuery->setConnectionRole(Connection::ROLE_READ)->getConnectionRole());
  68. // Can set read role for select queries
  69. $this->assertSame(Connection::ROLE_READ, $selectQuery->useReadRole()->getConnectionRole());
  70. // Can set write role for select queries
  71. $this->assertSame(Connection::ROLE_WRITE, $selectQuery->useWriteRole()->getConnectionRole());
  72. }
  73. protected function newQuery(): Query
  74. {
  75. return new class ($this->connection) extends Query
  76. {
  77. };
  78. }
  79. /**
  80. * Tests that empty values don't set where clauses.
  81. */
  82. public function testWhereEmptyValues(): void
  83. {
  84. $this->query->from('comments')
  85. ->where('');
  86. $this->assertCount(0, $this->query->clause('where'));
  87. $this->query->where([]);
  88. $this->assertCount(0, $this->query->clause('where'));
  89. }
  90. /**
  91. * Tests that the identifier method creates an expression object.
  92. */
  93. public function testIdentifierExpression(): void
  94. {
  95. /** @var \Cake\Database\Expression\IdentifierExpression $identifier */
  96. $identifier = $this->query->identifier('foo');
  97. $this->assertInstanceOf(IdentifierExpression::class, $identifier);
  98. $this->assertSame('foo', $identifier->getIdentifier());
  99. }
  100. /**
  101. * Tests the interface contract of identifier
  102. */
  103. public function testIdentifierInterface(): void
  104. {
  105. $identifier = $this->query->identifier('description');
  106. $this->assertInstanceOf(ExpressionInterface::class, $identifier);
  107. $this->assertSame('description', $identifier->getIdentifier());
  108. $identifier->setIdentifier('title');
  109. $this->assertSame('title', $identifier->getIdentifier());
  110. }
  111. /**
  112. * Tests __debugInfo on incomplete query
  113. */
  114. public function testDebugInfoIncompleteQuery(): void
  115. {
  116. $this->query = $this->newQuery()
  117. ->from(['articles']);
  118. $result = $this->query->__debugInfo();
  119. $this->assertStringContainsString('incomplete', $result['sql']);
  120. $this->assertSame([], $result['params']);
  121. }
  122. public function testCloneWithExpression(): void
  123. {
  124. $this->query
  125. ->with(
  126. new CommonTableExpression(
  127. 'cte',
  128. $this->newQuery()
  129. )
  130. )
  131. ->with(function (CommonTableExpression $cte, Query $query) {
  132. return $cte
  133. ->name('cte2')
  134. ->query($query);
  135. });
  136. $clause = $this->query->clause('with');
  137. $clauseClone = (clone $this->query)->clause('with');
  138. $this->assertIsArray($clause);
  139. foreach ($clause as $key => $value) {
  140. $this->assertEquals($value, $clauseClone[$key]);
  141. $this->assertNotSame($value, $clauseClone[$key]);
  142. }
  143. }
  144. public function testCloneModifierExpression(): void
  145. {
  146. $this->query->modifier($this->query->newExpr('modifier'));
  147. $clause = $this->query->clause('modifier');
  148. $clauseClone = (clone $this->query)->clause('modifier');
  149. $this->assertIsArray($clause);
  150. foreach ($clause as $key => $value) {
  151. $this->assertEquals($value, $clauseClone[$key]);
  152. $this->assertNotSame($value, $clauseClone[$key]);
  153. }
  154. }
  155. public function testCloneFromExpression(): void
  156. {
  157. $this->query->from(['alias' => $this->newQuery()]);
  158. $clause = $this->query->clause('from');
  159. $clauseClone = (clone $this->query)->clause('from');
  160. $this->assertIsArray($clause);
  161. foreach ($clause as $key => $value) {
  162. $this->assertEquals($value, $clauseClone[$key]);
  163. $this->assertNotSame($value, $clauseClone[$key]);
  164. }
  165. }
  166. public function testCloneJoinExpression(): void
  167. {
  168. $this->query
  169. ->innerJoin(
  170. ['alias_inner' => $this->newQuery()],
  171. ['alias_inner.fk = parent.pk']
  172. )
  173. ->leftJoin(
  174. ['alias_left' => $this->newQuery()],
  175. ['alias_left.fk = parent.pk']
  176. )
  177. ->rightJoin(
  178. ['alias_right' => $this->newQuery()],
  179. ['alias_right.fk = parent.pk']
  180. );
  181. $clause = $this->query->clause('join');
  182. $clauseClone = (clone $this->query)->clause('join');
  183. $this->assertIsArray($clause);
  184. foreach ($clause as $key => $value) {
  185. $this->assertEquals($value['table'], $clauseClone[$key]['table']);
  186. $this->assertNotSame($value['table'], $clauseClone[$key]['table']);
  187. $this->assertEquals($value['conditions'], $clauseClone[$key]['conditions']);
  188. $this->assertNotSame($value['conditions'], $clauseClone[$key]['conditions']);
  189. }
  190. }
  191. public function testCloneWhereExpression(): void
  192. {
  193. $this->query
  194. ->where($this->query->newExpr('where'))
  195. ->where(['field' => $this->query->newExpr('where')]);
  196. $clause = $this->query->clause('where');
  197. $clauseClone = (clone $this->query)->clause('where');
  198. $this->assertInstanceOf(ExpressionInterface::class, $clause);
  199. $this->assertEquals($clause, $clauseClone);
  200. $this->assertNotSame($clause, $clauseClone);
  201. }
  202. public function testCloneOrderExpression(): void
  203. {
  204. $this->query
  205. ->orderBy($this->query->newExpr('order'))
  206. ->orderByAsc($this->query->newExpr('order_asc'))
  207. ->orderByDesc($this->query->newExpr('order_desc'));
  208. $clause = $this->query->clause('order');
  209. $clauseClone = (clone $this->query)->clause('order');
  210. $this->assertInstanceOf(ExpressionInterface::class, $clause);
  211. $this->assertEquals($clause, $clauseClone);
  212. $this->assertNotSame($clause, $clauseClone);
  213. }
  214. public function testCloneLimitExpression(): void
  215. {
  216. $this->query->limit($this->query->newExpr('1'));
  217. $clause = $this->query->clause('limit');
  218. $clauseClone = (clone $this->query)->clause('limit');
  219. $this->assertInstanceOf(ExpressionInterface::class, $clause);
  220. $this->assertEquals($clause, $clauseClone);
  221. $this->assertNotSame($clause, $clauseClone);
  222. }
  223. public function testCloneOffsetExpression(): void
  224. {
  225. $this->query->offset($this->query->newExpr('1'));
  226. $clause = $this->query->clause('offset');
  227. $clauseClone = (clone $this->query)->clause('offset');
  228. $this->assertInstanceOf(ExpressionInterface::class, $clause);
  229. $this->assertEquals($clause, $clauseClone);
  230. $this->assertNotSame($clause, $clauseClone);
  231. }
  232. public function testCloneEpilogExpression(): void
  233. {
  234. $this->query->epilog($this->query->newExpr('epilog'));
  235. $clause = $this->query->clause('epilog');
  236. $clauseClone = (clone $this->query)->clause('epilog');
  237. $this->assertInstanceOf(ExpressionInterface::class, $clause);
  238. $this->assertEquals($clause, $clauseClone);
  239. $this->assertNotSame($clause, $clauseClone);
  240. }
  241. /**
  242. * Test getValueBinder()
  243. */
  244. public function testGetValueBinder(): void
  245. {
  246. $this->assertInstanceOf(ValueBinder::class, $this->query->getValueBinder());
  247. }
  248. /**
  249. * Test that reading an undefined clause does not emit an error.
  250. */
  251. public function testClauseUndefined(): void
  252. {
  253. $this->expectException(InvalidArgumentException::class);
  254. $this->expectExceptionMessage('The `nope` clause is not defined. Valid clauses are: `comment`, `delete`, `update`');
  255. $this->assertEmpty($this->query->clause('where'));
  256. $this->query->clause('nope');
  257. }
  258. }