ExpressionBuilderTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. <?php
  2. /**
  3. * @author Joas Schilling <nickvergessen@owncloud.com>
  4. *
  5. * @copyright Copyright (c) 2015, ownCloud, Inc.
  6. * @license AGPL-3.0
  7. *
  8. * This code is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License, version 3,
  10. * as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License, version 3,
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>
  19. *
  20. */
  21. namespace Test\DB\QueryBuilder;
  22. use Doctrine\DBAL\Query\Expression\ExpressionBuilder as DoctrineExpressionBuilder;
  23. use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder;
  24. use OCP\DB\QueryBuilder\IQueryBuilder;
  25. use Test\TestCase;
  26. /**
  27. * Class ExpressionBuilderTest
  28. *
  29. * @group DB
  30. *
  31. * @package Test\DB\QueryBuilder
  32. */
  33. class ExpressionBuilderTest extends TestCase {
  34. /** @var ExpressionBuilder */
  35. protected $expressionBuilder;
  36. /** @var DoctrineExpressionBuilder */
  37. protected $doctrineExpressionBuilder;
  38. /** @var \Doctrine\DBAL\Connection|\OCP\IDBConnection */
  39. protected $connection;
  40. protected function setUp() {
  41. parent::setUp();
  42. $this->connection = \OC::$server->getDatabaseConnection();
  43. $queryBuilder = $this->createMock(IQueryBuilder::class);
  44. $this->expressionBuilder = new ExpressionBuilder($this->connection, $queryBuilder);
  45. $this->doctrineExpressionBuilder = new DoctrineExpressionBuilder($this->connection);
  46. }
  47. public function dataComparison() {
  48. $valueSets = $this->dataComparisons();
  49. $comparisonOperators = ['=', '<>', '<', '>', '<=', '>='];
  50. $testSets = [];
  51. foreach ($comparisonOperators as $operator) {
  52. foreach ($valueSets as $values) {
  53. $testSets[] = array_merge([$operator], $values);
  54. }
  55. }
  56. return $testSets;
  57. }
  58. /**
  59. * @dataProvider dataComparison
  60. *
  61. * @param string $comparison
  62. * @param mixed $input1
  63. * @param bool $isInput1Literal
  64. * @param mixed $input2
  65. * @param bool $isInput2Literal
  66. */
  67. public function testComparison($comparison, $input1, $isInput1Literal, $input2, $isInput2Literal) {
  68. list($doctrineInput1, $ocInput1) = $this->helpWithLiteral($input1, $isInput1Literal);
  69. list($doctrineInput2, $ocInput2) = $this->helpWithLiteral($input2, $isInput2Literal);
  70. $this->assertEquals(
  71. $this->doctrineExpressionBuilder->comparison($doctrineInput1, $comparison, $doctrineInput2),
  72. $this->expressionBuilder->comparison($ocInput1, $comparison, $ocInput2)
  73. );
  74. }
  75. public function dataComparisons() {
  76. return [
  77. ['value', false, 'value', false],
  78. ['value', false, 'value', true],
  79. ['value', true, 'value', false],
  80. ['value', true, 'value', true],
  81. ];
  82. }
  83. /**
  84. * @dataProvider dataComparisons
  85. *
  86. * @param mixed $input1
  87. * @param bool $isInput1Literal
  88. * @param mixed $input2
  89. * @param bool $isInput2Literal
  90. */
  91. public function testEquals($input1, $isInput1Literal, $input2, $isInput2Literal) {
  92. list($doctrineInput1, $ocInput1) = $this->helpWithLiteral($input1, $isInput1Literal);
  93. list($doctrineInput2, $ocInput2) = $this->helpWithLiteral($input2, $isInput2Literal);
  94. $this->assertEquals(
  95. $this->doctrineExpressionBuilder->eq($doctrineInput1, $doctrineInput2),
  96. $this->expressionBuilder->eq($ocInput1, $ocInput2)
  97. );
  98. }
  99. /**
  100. * @dataProvider dataComparisons
  101. *
  102. * @param mixed $input1
  103. * @param bool $isInput1Literal
  104. * @param mixed $input2
  105. * @param bool $isInput2Literal
  106. */
  107. public function testNotEquals($input1, $isInput1Literal, $input2, $isInput2Literal) {
  108. list($doctrineInput1, $ocInput1) = $this->helpWithLiteral($input1, $isInput1Literal);
  109. list($doctrineInput2, $ocInput2) = $this->helpWithLiteral($input2, $isInput2Literal);
  110. $this->assertEquals(
  111. $this->doctrineExpressionBuilder->neq($doctrineInput1, $doctrineInput2),
  112. $this->expressionBuilder->neq($ocInput1, $ocInput2)
  113. );
  114. }
  115. /**
  116. * @dataProvider dataComparisons
  117. *
  118. * @param mixed $input1
  119. * @param bool $isInput1Literal
  120. * @param mixed $input2
  121. * @param bool $isInput2Literal
  122. */
  123. public function testLowerThan($input1, $isInput1Literal, $input2, $isInput2Literal) {
  124. list($doctrineInput1, $ocInput1) = $this->helpWithLiteral($input1, $isInput1Literal);
  125. list($doctrineInput2, $ocInput2) = $this->helpWithLiteral($input2, $isInput2Literal);
  126. $this->assertEquals(
  127. $this->doctrineExpressionBuilder->lt($doctrineInput1, $doctrineInput2),
  128. $this->expressionBuilder->lt($ocInput1, $ocInput2)
  129. );
  130. }
  131. /**
  132. * @dataProvider dataComparisons
  133. *
  134. * @param mixed $input1
  135. * @param bool $isInput1Literal
  136. * @param mixed $input2
  137. * @param bool $isInput2Literal
  138. */
  139. public function testLowerThanEquals($input1, $isInput1Literal, $input2, $isInput2Literal) {
  140. list($doctrineInput1, $ocInput1) = $this->helpWithLiteral($input1, $isInput1Literal);
  141. list($doctrineInput2, $ocInput2) = $this->helpWithLiteral($input2, $isInput2Literal);
  142. $this->assertEquals(
  143. $this->doctrineExpressionBuilder->lte($doctrineInput1, $doctrineInput2),
  144. $this->expressionBuilder->lte($ocInput1, $ocInput2)
  145. );
  146. }
  147. /**
  148. * @dataProvider dataComparisons
  149. *
  150. * @param mixed $input1
  151. * @param bool $isInput1Literal
  152. * @param mixed $input2
  153. * @param bool $isInput2Literal
  154. */
  155. public function testGreaterThan($input1, $isInput1Literal, $input2, $isInput2Literal) {
  156. list($doctrineInput1, $ocInput1) = $this->helpWithLiteral($input1, $isInput1Literal);
  157. list($doctrineInput2, $ocInput2) = $this->helpWithLiteral($input2, $isInput2Literal);
  158. $this->assertEquals(
  159. $this->doctrineExpressionBuilder->gt($doctrineInput1, $doctrineInput2),
  160. $this->expressionBuilder->gt($ocInput1, $ocInput2)
  161. );
  162. }
  163. /**
  164. * @dataProvider dataComparisons
  165. *
  166. * @param mixed $input1
  167. * @param bool $isInput1Literal
  168. * @param mixed $input2
  169. * @param bool $isInput2Literal
  170. */
  171. public function testGreaterThanEquals($input1, $isInput1Literal, $input2, $isInput2Literal) {
  172. list($doctrineInput1, $ocInput1) = $this->helpWithLiteral($input1, $isInput1Literal);
  173. list($doctrineInput2, $ocInput2) = $this->helpWithLiteral($input2, $isInput2Literal);
  174. $this->assertEquals(
  175. $this->doctrineExpressionBuilder->gte($doctrineInput1, $doctrineInput2),
  176. $this->expressionBuilder->gte($ocInput1, $ocInput2)
  177. );
  178. }
  179. public function testIsNull() {
  180. $this->assertEquals(
  181. $this->doctrineExpressionBuilder->isNull('`test`'),
  182. $this->expressionBuilder->isNull('test')
  183. );
  184. }
  185. public function testIsNotNull() {
  186. $this->assertEquals(
  187. $this->doctrineExpressionBuilder->isNotNull('`test`'),
  188. $this->expressionBuilder->isNotNull('test')
  189. );
  190. }
  191. public function dataLike() {
  192. return [
  193. ['value', false],
  194. ['value', true],
  195. ];
  196. }
  197. /**
  198. * @dataProvider dataLike
  199. *
  200. * @param mixed $input
  201. * @param bool $isLiteral
  202. */
  203. public function testLike($input, $isLiteral) {
  204. list($doctrineInput, $ocInput) = $this->helpWithLiteral($input, $isLiteral);
  205. $this->assertEquals(
  206. $this->doctrineExpressionBuilder->like('`test`', $doctrineInput),
  207. $this->expressionBuilder->like('test', $ocInput)
  208. );
  209. }
  210. /**
  211. * @dataProvider dataLike
  212. *
  213. * @param mixed $input
  214. * @param bool $isLiteral
  215. */
  216. public function testNotLike($input, $isLiteral) {
  217. list($doctrineInput, $ocInput) = $this->helpWithLiteral($input, $isLiteral);
  218. $this->assertEquals(
  219. $this->doctrineExpressionBuilder->notLike('`test`', $doctrineInput),
  220. $this->expressionBuilder->notLike('test', $ocInput)
  221. );
  222. }
  223. public function dataIn() {
  224. return [
  225. ['value', false],
  226. ['value', true],
  227. [['value'], false],
  228. [['value'], true],
  229. ];
  230. }
  231. /**
  232. * @dataProvider dataIn
  233. *
  234. * @param mixed $input
  235. * @param bool $isLiteral
  236. */
  237. public function testIn($input, $isLiteral) {
  238. list($doctrineInput, $ocInput) = $this->helpWithLiteral($input, $isLiteral);
  239. $this->assertEquals(
  240. $this->doctrineExpressionBuilder->in('`test`', $doctrineInput),
  241. $this->expressionBuilder->in('test', $ocInput)
  242. );
  243. }
  244. /**
  245. * @dataProvider dataIn
  246. *
  247. * @param mixed $input
  248. * @param bool $isLiteral
  249. */
  250. public function testNotIn($input, $isLiteral) {
  251. list($doctrineInput, $ocInput) = $this->helpWithLiteral($input, $isLiteral);
  252. $this->assertEquals(
  253. $this->doctrineExpressionBuilder->notIn('`test`', $doctrineInput),
  254. $this->expressionBuilder->notIn('test', $ocInput)
  255. );
  256. }
  257. protected function helpWithLiteral($input, $isLiteral) {
  258. if ($isLiteral) {
  259. if (is_array($input)) {
  260. $doctrineInput = array_map(function ($ident) {
  261. return $this->doctrineExpressionBuilder->literal($ident);
  262. }, $input);
  263. $ocInput = array_map(function ($ident) {
  264. return $this->expressionBuilder->literal($ident);
  265. }, $input);
  266. } else {
  267. $doctrineInput = $this->doctrineExpressionBuilder->literal($input);
  268. $ocInput = $this->expressionBuilder->literal($input);
  269. }
  270. } else {
  271. if (is_array($input)) {
  272. $doctrineInput = array_map(function ($input) {
  273. return '`' . $input . '`';
  274. }, $input);
  275. $ocInput = $input;
  276. } else {
  277. $doctrineInput = '`' . $input . '`';
  278. $ocInput = $input;
  279. }
  280. }
  281. return [$doctrineInput, $ocInput];
  282. }
  283. public function dataLiteral() {
  284. return [
  285. ['value', null],
  286. ['1', null],
  287. [1, null],
  288. [1, 'string'],
  289. [1, 'integer'],
  290. [1, IQueryBuilder::PARAM_INT],
  291. ];
  292. }
  293. /**
  294. * @dataProvider dataLiteral
  295. *
  296. * @param mixed $input
  297. * @param string|null $type
  298. */
  299. public function testLiteral($input, $type) {
  300. /** @var \OC\DB\QueryBuilder\Literal $actual */
  301. $actual = $this->expressionBuilder->literal($input, $type);
  302. $this->assertInstanceOf('\OC\DB\QueryBuilder\Literal', $actual);
  303. $this->assertEquals(
  304. $this->doctrineExpressionBuilder->literal($input, $type),
  305. $actual->__toString()
  306. );
  307. }
  308. public function dataClobComparisons() {
  309. return [
  310. ['eq', '5', IQueryBuilder::PARAM_STR, false, 3],
  311. ['eq', '5', IQueryBuilder::PARAM_STR, true, 1],
  312. ['neq', '5', IQueryBuilder::PARAM_STR, false, 8],
  313. ['neq', '5', IQueryBuilder::PARAM_STR, true, 6],
  314. ['lt', '5', IQueryBuilder::PARAM_STR, false, 3],
  315. ['lt', '5', IQueryBuilder::PARAM_STR, true, 1],
  316. ['lte', '5', IQueryBuilder::PARAM_STR, false, 6],
  317. ['lte', '5', IQueryBuilder::PARAM_STR, true, 4],
  318. ['gt', '5', IQueryBuilder::PARAM_STR, false, 5],
  319. ['gt', '5', IQueryBuilder::PARAM_STR, true, 1],
  320. ['gte', '5', IQueryBuilder::PARAM_STR, false, 8],
  321. ['gte', '5', IQueryBuilder::PARAM_STR, true, 4],
  322. ['like', '%5%', IQueryBuilder::PARAM_STR, false, 3],
  323. ['like', '%5%', IQueryBuilder::PARAM_STR, true, 1],
  324. ['like', 'under_%', IQueryBuilder::PARAM_STR, false, 2],
  325. ['like', 'under\_%', IQueryBuilder::PARAM_STR, false, 1],
  326. ['notLike', '%5%', IQueryBuilder::PARAM_STR, false, 8],
  327. ['notLike', '%5%', IQueryBuilder::PARAM_STR, true, 6],
  328. ['in', ['5'], IQueryBuilder::PARAM_STR_ARRAY, false, 3],
  329. ['in', ['5'], IQueryBuilder::PARAM_STR_ARRAY, true, 1],
  330. ['notIn', ['5'], IQueryBuilder::PARAM_STR_ARRAY, false, 8],
  331. ['notIn', ['5'], IQueryBuilder::PARAM_STR_ARRAY, true, 6],
  332. ];
  333. }
  334. /**
  335. * @dataProvider dataClobComparisons
  336. * @param string $function
  337. * @param mixed $value
  338. * @param mixed $type
  339. * @param bool $compareKeyToValue
  340. * @param int $expected
  341. */
  342. public function testClobComparisons($function, $value, $type, $compareKeyToValue, $expected) {
  343. $appId = $this->getUniqueID('testing');
  344. $this->createConfig($appId, 1, 4);
  345. $this->createConfig($appId, 2, 5);
  346. $this->createConfig($appId, 3, 6);
  347. $this->createConfig($appId, 4, 4);
  348. $this->createConfig($appId, 5, 5);
  349. $this->createConfig($appId, 6, 6);
  350. $this->createConfig($appId, 7, 4);
  351. $this->createConfig($appId, 8, 5);
  352. $this->createConfig($appId, 9, 6);
  353. $this->createConfig($appId, 10, 'under_score');
  354. $this->createConfig($appId, 11, 'underscore');
  355. $query = $this->connection->getQueryBuilder();
  356. $query->select($query->createFunction('COUNT(*) AS `count`'))
  357. ->from('appconfig')
  358. ->where($query->expr()->eq('appid', $query->createNamedParameter($appId)))
  359. ->andWhere(call_user_func([$query->expr(), $function], 'configvalue', $query->createNamedParameter($value, $type), IQueryBuilder::PARAM_STR));
  360. if ($compareKeyToValue) {
  361. $query->andWhere(call_user_func([$query->expr(), $function], 'configkey', 'configvalue', IQueryBuilder::PARAM_STR));
  362. }
  363. $result = $query->execute();
  364. $this->assertEquals(['count' => $expected], $result->fetch());
  365. $result->closeCursor();
  366. $query = $this->connection->getQueryBuilder();
  367. $query->delete('appconfig')
  368. ->where($query->expr()->eq('appid', $query->createNamedParameter($appId)))
  369. ->execute();
  370. }
  371. protected function createConfig($appId, $key, $value) {
  372. $query = $this->connection->getQueryBuilder();
  373. $query->insert('appconfig')
  374. ->values([
  375. 'appid' => $query->createNamedParameter($appId),
  376. 'configkey' => $query->createNamedParameter((string) $key),
  377. 'configvalue' => $query->createNamedParameter((string) $value),
  378. ])
  379. ->execute();
  380. }
  381. }