You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

QueryBuilder.php 34KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  6. * @author Daniel Kesselberg <mail@danielkesselberg.de>
  7. * @author J0WI <J0WI@users.noreply.github.com>
  8. * @author Joas Schilling <coding@schilljs.com>
  9. * @author Lukas Reschke <lukas@statuscode.ch>
  10. * @author Robin Appelman <robin@icewind.nl>
  11. * @author Roeland Jago Douma <roeland@famdouma.nl>
  12. * @author Thomas Müller <thomas.mueller@tmit.eu>
  13. * @author Vincent Petry <vincent@nextcloud.com>
  14. *
  15. * @license AGPL-3.0
  16. *
  17. * This code is free software: you can redistribute it and/or modify
  18. * it under the terms of the GNU Affero General Public License, version 3,
  19. * as published by the Free Software Foundation.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU Affero General Public License for more details.
  25. *
  26. * You should have received a copy of the GNU Affero General Public License, version 3,
  27. * along with this program. If not, see <http://www.gnu.org/licenses/>
  28. *
  29. */
  30. namespace OC\DB\QueryBuilder;
  31. use Doctrine\DBAL\Platforms\MySqlPlatform;
  32. use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
  33. use Doctrine\DBAL\Platforms\SqlitePlatform;
  34. use Doctrine\DBAL\Query\QueryException;
  35. use OC\DB\OracleConnection;
  36. use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder;
  37. use OC\DB\QueryBuilder\ExpressionBuilder\MySqlExpressionBuilder;
  38. use OC\DB\QueryBuilder\ExpressionBuilder\OCIExpressionBuilder;
  39. use OC\DB\QueryBuilder\ExpressionBuilder\PgSqlExpressionBuilder;
  40. use OC\DB\QueryBuilder\ExpressionBuilder\SqliteExpressionBuilder;
  41. use OC\DB\QueryBuilder\FunctionBuilder\FunctionBuilder;
  42. use OC\DB\QueryBuilder\FunctionBuilder\OCIFunctionBuilder;
  43. use OC\DB\QueryBuilder\FunctionBuilder\PgSqlFunctionBuilder;
  44. use OC\DB\QueryBuilder\FunctionBuilder\SqliteFunctionBuilder;
  45. use OC\SystemConfig;
  46. use OCP\DB\QueryBuilder\ILiteral;
  47. use OCP\DB\QueryBuilder\IParameter;
  48. use OCP\DB\QueryBuilder\IQueryBuilder;
  49. use OCP\DB\QueryBuilder\IQueryFunction;
  50. use OCP\IDBConnection;
  51. use OCP\ILogger;
  52. class QueryBuilder implements IQueryBuilder {
  53. /** @var \OCP\IDBConnection */
  54. private $connection;
  55. /** @var SystemConfig */
  56. private $systemConfig;
  57. /** @var ILogger */
  58. private $logger;
  59. /** @var \Doctrine\DBAL\Query\QueryBuilder */
  60. private $queryBuilder;
  61. /** @var QuoteHelper */
  62. private $helper;
  63. /** @var bool */
  64. private $automaticTablePrefix = true;
  65. /** @var string */
  66. protected $lastInsertedTable;
  67. /**
  68. * Initializes a new QueryBuilder.
  69. *
  70. * @param IDBConnection $connection
  71. * @param SystemConfig $systemConfig
  72. * @param ILogger $logger
  73. */
  74. public function __construct(IDBConnection $connection, SystemConfig $systemConfig, ILogger $logger) {
  75. $this->connection = $connection;
  76. $this->systemConfig = $systemConfig;
  77. $this->logger = $logger;
  78. $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection);
  79. $this->helper = new QuoteHelper();
  80. }
  81. /**
  82. * Enable/disable automatic prefixing of table names with the oc_ prefix
  83. *
  84. * @param bool $enabled If set to true table names will be prefixed with the
  85. * owncloud database prefix automatically.
  86. * @since 8.2.0
  87. */
  88. public function automaticTablePrefix($enabled) {
  89. $this->automaticTablePrefix = (bool) $enabled;
  90. }
  91. /**
  92. * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
  93. * This producer method is intended for convenient inline usage. Example:
  94. *
  95. * <code>
  96. * $qb = $conn->getQueryBuilder()
  97. * ->select('u')
  98. * ->from('users', 'u')
  99. * ->where($qb->expr()->eq('u.id', 1));
  100. * </code>
  101. *
  102. * For more complex expression construction, consider storing the expression
  103. * builder object in a local variable.
  104. *
  105. * @return \OCP\DB\QueryBuilder\IExpressionBuilder
  106. */
  107. public function expr() {
  108. if ($this->connection instanceof OracleConnection) {
  109. return new OCIExpressionBuilder($this->connection, $this);
  110. } elseif ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) {
  111. return new PgSqlExpressionBuilder($this->connection, $this);
  112. } elseif ($this->connection->getDatabasePlatform() instanceof MySqlPlatform) {
  113. return new MySqlExpressionBuilder($this->connection, $this);
  114. } elseif ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
  115. return new SqliteExpressionBuilder($this->connection, $this);
  116. } else {
  117. return new ExpressionBuilder($this->connection, $this);
  118. }
  119. }
  120. /**
  121. * Gets an FunctionBuilder used for object-oriented construction of query functions.
  122. * This producer method is intended for convenient inline usage. Example:
  123. *
  124. * <code>
  125. * $qb = $conn->getQueryBuilder()
  126. * ->select('u')
  127. * ->from('users', 'u')
  128. * ->where($qb->fun()->md5('u.id'));
  129. * </code>
  130. *
  131. * For more complex function construction, consider storing the function
  132. * builder object in a local variable.
  133. *
  134. * @return \OCP\DB\QueryBuilder\IFunctionBuilder
  135. */
  136. public function func() {
  137. if ($this->connection instanceof OracleConnection) {
  138. return new OCIFunctionBuilder($this->helper);
  139. } elseif ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
  140. return new SqliteFunctionBuilder($this->helper);
  141. } elseif ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) {
  142. return new PgSqlFunctionBuilder($this->helper);
  143. } else {
  144. return new FunctionBuilder($this->helper);
  145. }
  146. }
  147. /**
  148. * Gets the type of the currently built query.
  149. *
  150. * @return integer
  151. */
  152. public function getType() {
  153. return $this->queryBuilder->getType();
  154. }
  155. /**
  156. * Gets the associated DBAL Connection for this query builder.
  157. *
  158. * @return \OCP\IDBConnection
  159. */
  160. public function getConnection() {
  161. return $this->connection;
  162. }
  163. /**
  164. * Gets the state of this query builder instance.
  165. *
  166. * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
  167. */
  168. public function getState() {
  169. return $this->queryBuilder->getState();
  170. }
  171. /**
  172. * Executes this query using the bound parameters and their types.
  173. *
  174. * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
  175. * for insert, update and delete statements.
  176. *
  177. * @return \Doctrine\DBAL\Driver\Statement|int
  178. */
  179. public function execute() {
  180. if ($this->systemConfig->getValue('log_query', false)) {
  181. try {
  182. $params = [];
  183. foreach ($this->getParameters() as $placeholder => $value) {
  184. if ($value instanceof \DateTime) {
  185. $params[] = $placeholder . ' => DateTime:\'' . $value->format('c') . '\'';
  186. } elseif (is_array($value)) {
  187. $params[] = $placeholder . ' => (\'' . implode('\', \'', $value) . '\')';
  188. } else {
  189. $params[] = $placeholder . ' => \'' . $value . '\'';
  190. }
  191. }
  192. if (empty($params)) {
  193. $this->logger->debug('DB QueryBuilder: \'{query}\'', [
  194. 'query' => $this->getSQL(),
  195. 'app' => 'core',
  196. ]);
  197. } else {
  198. $this->logger->debug('DB QueryBuilder: \'{query}\' with parameters: {params}', [
  199. 'query' => $this->getSQL(),
  200. 'params' => implode(', ', $params),
  201. 'app' => 'core',
  202. ]);
  203. }
  204. } catch (\Error $e) {
  205. // likely an error during conversion of $value to string
  206. $this->logger->debug('DB QueryBuilder: error trying to log SQL query');
  207. $this->logger->logException($e);
  208. }
  209. }
  210. if (!empty($this->getQueryPart('select'))) {
  211. $select = $this->getQueryPart('select');
  212. $hasSelectAll = array_filter($select, static function ($s) {
  213. return $s === '*';
  214. });
  215. $hasSelectSpecific = array_filter($select, static function ($s) {
  216. return $s !== '*';
  217. });
  218. if (empty($hasSelectAll) === empty($hasSelectSpecific)) {
  219. $exception = new QueryException('Query is selecting * and specific values in the same query. This is not supported in Oracle.');
  220. $this->logger->logException($exception, [
  221. 'message' => 'Query is selecting * and specific values in the same query. This is not supported in Oracle.',
  222. 'query' => $this->getSQL(),
  223. 'level' => ILogger::ERROR,
  224. 'app' => 'core',
  225. ]);
  226. }
  227. }
  228. return $this->queryBuilder->execute();
  229. }
  230. /**
  231. * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
  232. *
  233. * <code>
  234. * $qb = $conn->getQueryBuilder()
  235. * ->select('u')
  236. * ->from('User', 'u')
  237. * echo $qb->getSQL(); // SELECT u FROM User u
  238. * </code>
  239. *
  240. * @return string The SQL query string.
  241. */
  242. public function getSQL() {
  243. return $this->queryBuilder->getSQL();
  244. }
  245. /**
  246. * Sets a query parameter for the query being constructed.
  247. *
  248. * <code>
  249. * $qb = $conn->getQueryBuilder()
  250. * ->select('u')
  251. * ->from('users', 'u')
  252. * ->where('u.id = :user_id')
  253. * ->setParameter(':user_id', 1);
  254. * </code>
  255. *
  256. * @param string|integer $key The parameter position or name.
  257. * @param mixed $value The parameter value.
  258. * @param string|null|int $type One of the IQueryBuilder::PARAM_* constants.
  259. *
  260. * @return $this This QueryBuilder instance.
  261. */
  262. public function setParameter($key, $value, $type = null) {
  263. $this->queryBuilder->setParameter($key, $value, $type);
  264. return $this;
  265. }
  266. /**
  267. * Sets a collection of query parameters for the query being constructed.
  268. *
  269. * <code>
  270. * $qb = $conn->getQueryBuilder()
  271. * ->select('u')
  272. * ->from('users', 'u')
  273. * ->where('u.id = :user_id1 OR u.id = :user_id2')
  274. * ->setParameters(array(
  275. * ':user_id1' => 1,
  276. * ':user_id2' => 2
  277. * ));
  278. * </code>
  279. *
  280. * @param array $params The query parameters to set.
  281. * @param array $types The query parameters types to set.
  282. *
  283. * @return $this This QueryBuilder instance.
  284. */
  285. public function setParameters(array $params, array $types = []) {
  286. $this->queryBuilder->setParameters($params, $types);
  287. return $this;
  288. }
  289. /**
  290. * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
  291. *
  292. * @return array The currently defined query parameters indexed by parameter index or name.
  293. */
  294. public function getParameters() {
  295. return $this->queryBuilder->getParameters();
  296. }
  297. /**
  298. * Gets a (previously set) query parameter of the query being constructed.
  299. *
  300. * @param mixed $key The key (index or name) of the bound parameter.
  301. *
  302. * @return mixed The value of the bound parameter.
  303. */
  304. public function getParameter($key) {
  305. return $this->queryBuilder->getParameter($key);
  306. }
  307. /**
  308. * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
  309. *
  310. * @return array The currently defined query parameter types indexed by parameter index or name.
  311. */
  312. public function getParameterTypes() {
  313. return $this->queryBuilder->getParameterTypes();
  314. }
  315. /**
  316. * Gets a (previously set) query parameter type of the query being constructed.
  317. *
  318. * @param mixed $key The key (index or name) of the bound parameter type.
  319. *
  320. * @return mixed The value of the bound parameter type.
  321. */
  322. public function getParameterType($key) {
  323. return $this->queryBuilder->getParameterType($key);
  324. }
  325. /**
  326. * Sets the position of the first result to retrieve (the "offset").
  327. *
  328. * @param integer $firstResult The first result to return.
  329. *
  330. * @return $this This QueryBuilder instance.
  331. */
  332. public function setFirstResult($firstResult) {
  333. $this->queryBuilder->setFirstResult($firstResult);
  334. return $this;
  335. }
  336. /**
  337. * Gets the position of the first result the query object was set to retrieve (the "offset").
  338. * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
  339. *
  340. * @return integer The position of the first result.
  341. */
  342. public function getFirstResult() {
  343. return $this->queryBuilder->getFirstResult();
  344. }
  345. /**
  346. * Sets the maximum number of results to retrieve (the "limit").
  347. *
  348. * NOTE: Setting max results to "0" will cause mixed behaviour. While most
  349. * of the databases will just return an empty result set, Oracle will return
  350. * all entries.
  351. *
  352. * @param integer $maxResults The maximum number of results to retrieve.
  353. *
  354. * @return $this This QueryBuilder instance.
  355. */
  356. public function setMaxResults($maxResults) {
  357. $this->queryBuilder->setMaxResults($maxResults);
  358. return $this;
  359. }
  360. /**
  361. * Gets the maximum number of results the query object was set to retrieve (the "limit").
  362. * Returns NULL if {@link setMaxResults} was not applied to this query builder.
  363. *
  364. * @return int|null The maximum number of results.
  365. */
  366. public function getMaxResults() {
  367. return $this->queryBuilder->getMaxResults();
  368. }
  369. /**
  370. * Specifies an item that is to be returned in the query result.
  371. * Replaces any previously specified selections, if any.
  372. *
  373. * <code>
  374. * $qb = $conn->getQueryBuilder()
  375. * ->select('u.id', 'p.id')
  376. * ->from('users', 'u')
  377. * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
  378. * </code>
  379. *
  380. * @param mixed ...$selects The selection expressions.
  381. *
  382. * '@return $this This QueryBuilder instance.
  383. */
  384. public function select(...$selects) {
  385. if (count($selects) === 1 && is_array($selects[0])) {
  386. $selects = $selects[0];
  387. }
  388. $this->queryBuilder->select(
  389. $this->helper->quoteColumnNames($selects)
  390. );
  391. return $this;
  392. }
  393. /**
  394. * Specifies an item that is to be returned with a different name in the query result.
  395. *
  396. * <code>
  397. * $qb = $conn->getQueryBuilder()
  398. * ->selectAlias('u.id', 'user_id')
  399. * ->from('users', 'u')
  400. * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
  401. * </code>
  402. *
  403. * @param mixed $select The selection expressions.
  404. * @param string $alias The column alias used in the constructed query.
  405. *
  406. * @return $this This QueryBuilder instance.
  407. */
  408. public function selectAlias($select, $alias) {
  409. $this->queryBuilder->addSelect(
  410. $this->helper->quoteColumnName($select) . ' AS ' . $this->helper->quoteColumnName($alias)
  411. );
  412. return $this;
  413. }
  414. /**
  415. * Specifies an item that is to be returned uniquely in the query result.
  416. *
  417. * <code>
  418. * $qb = $conn->getQueryBuilder()
  419. * ->selectDistinct('type')
  420. * ->from('users');
  421. * </code>
  422. *
  423. * @param mixed $select The selection expressions.
  424. *
  425. * @return $this This QueryBuilder instance.
  426. */
  427. public function selectDistinct($select) {
  428. if (!is_array($select)) {
  429. $select = [$select];
  430. }
  431. $quotedSelect = $this->helper->quoteColumnNames($select);
  432. $this->queryBuilder->addSelect(
  433. 'DISTINCT ' . implode(', ', $quotedSelect)
  434. );
  435. return $this;
  436. }
  437. /**
  438. * Adds an item that is to be returned in the query result.
  439. *
  440. * <code>
  441. * $qb = $conn->getQueryBuilder()
  442. * ->select('u.id')
  443. * ->addSelect('p.id')
  444. * ->from('users', 'u')
  445. * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
  446. * </code>
  447. *
  448. * @param mixed ...$selects The selection expression.
  449. *
  450. * @return $this This QueryBuilder instance.
  451. */
  452. public function addSelect(...$selects) {
  453. if (count($selects) === 1 && is_array($selects[0])) {
  454. $selects = $selects[0];
  455. }
  456. $this->queryBuilder->addSelect(
  457. $this->helper->quoteColumnNames($selects)
  458. );
  459. return $this;
  460. }
  461. /**
  462. * Turns the query being built into a bulk delete query that ranges over
  463. * a certain table.
  464. *
  465. * <code>
  466. * $qb = $conn->getQueryBuilder()
  467. * ->delete('users', 'u')
  468. * ->where('u.id = :user_id');
  469. * ->setParameter(':user_id', 1);
  470. * </code>
  471. *
  472. * @param string $delete The table whose rows are subject to the deletion.
  473. * @param string $alias The table alias used in the constructed query.
  474. *
  475. * @return $this This QueryBuilder instance.
  476. */
  477. public function delete($delete = null, $alias = null) {
  478. $this->queryBuilder->delete(
  479. $this->getTableName($delete),
  480. $alias
  481. );
  482. return $this;
  483. }
  484. /**
  485. * Turns the query being built into a bulk update query that ranges over
  486. * a certain table
  487. *
  488. * <code>
  489. * $qb = $conn->getQueryBuilder()
  490. * ->update('users', 'u')
  491. * ->set('u.password', md5('password'))
  492. * ->where('u.id = ?');
  493. * </code>
  494. *
  495. * @param string $update The table whose rows are subject to the update.
  496. * @param string $alias The table alias used in the constructed query.
  497. *
  498. * @return $this This QueryBuilder instance.
  499. */
  500. public function update($update = null, $alias = null) {
  501. $this->queryBuilder->update(
  502. $this->getTableName($update),
  503. $alias
  504. );
  505. return $this;
  506. }
  507. /**
  508. * Turns the query being built into an insert query that inserts into
  509. * a certain table
  510. *
  511. * <code>
  512. * $qb = $conn->getQueryBuilder()
  513. * ->insert('users')
  514. * ->values(
  515. * array(
  516. * 'name' => '?',
  517. * 'password' => '?'
  518. * )
  519. * );
  520. * </code>
  521. *
  522. * @param string $insert The table into which the rows should be inserted.
  523. *
  524. * @return $this This QueryBuilder instance.
  525. */
  526. public function insert($insert = null) {
  527. $this->queryBuilder->insert(
  528. $this->getTableName($insert)
  529. );
  530. $this->lastInsertedTable = $insert;
  531. return $this;
  532. }
  533. /**
  534. * Creates and adds a query root corresponding to the table identified by the
  535. * given alias, forming a cartesian product with any existing query roots.
  536. *
  537. * <code>
  538. * $qb = $conn->getQueryBuilder()
  539. * ->select('u.id')
  540. * ->from('users', 'u')
  541. * </code>
  542. *
  543. * @param string $from The table.
  544. * @param string|null $alias The alias of the table.
  545. *
  546. * @return $this This QueryBuilder instance.
  547. */
  548. public function from($from, $alias = null) {
  549. $this->queryBuilder->from(
  550. $this->getTableName($from),
  551. $this->quoteAlias($alias)
  552. );
  553. return $this;
  554. }
  555. /**
  556. * Creates and adds a join to the query.
  557. *
  558. * <code>
  559. * $qb = $conn->getQueryBuilder()
  560. * ->select('u.name')
  561. * ->from('users', 'u')
  562. * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  563. * </code>
  564. *
  565. * @param string $fromAlias The alias that points to a from clause.
  566. * @param string $join The table name to join.
  567. * @param string $alias The alias of the join table.
  568. * @param string $condition The condition for the join.
  569. *
  570. * @return $this This QueryBuilder instance.
  571. */
  572. public function join($fromAlias, $join, $alias, $condition = null) {
  573. $this->queryBuilder->join(
  574. $this->quoteAlias($fromAlias),
  575. $this->getTableName($join),
  576. $this->quoteAlias($alias),
  577. $condition
  578. );
  579. return $this;
  580. }
  581. /**
  582. * Creates and adds a join to the query.
  583. *
  584. * <code>
  585. * $qb = $conn->getQueryBuilder()
  586. * ->select('u.name')
  587. * ->from('users', 'u')
  588. * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  589. * </code>
  590. *
  591. * @param string $fromAlias The alias that points to a from clause.
  592. * @param string $join The table name to join.
  593. * @param string $alias The alias of the join table.
  594. * @param string $condition The condition for the join.
  595. *
  596. * @return $this This QueryBuilder instance.
  597. */
  598. public function innerJoin($fromAlias, $join, $alias, $condition = null) {
  599. $this->queryBuilder->innerJoin(
  600. $this->quoteAlias($fromAlias),
  601. $this->getTableName($join),
  602. $this->quoteAlias($alias),
  603. $condition
  604. );
  605. return $this;
  606. }
  607. /**
  608. * Creates and adds a left join to the query.
  609. *
  610. * <code>
  611. * $qb = $conn->getQueryBuilder()
  612. * ->select('u.name')
  613. * ->from('users', 'u')
  614. * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  615. * </code>
  616. *
  617. * @param string $fromAlias The alias that points to a from clause.
  618. * @param string $join The table name to join.
  619. * @param string $alias The alias of the join table.
  620. * @param string $condition The condition for the join.
  621. *
  622. * @return $this This QueryBuilder instance.
  623. */
  624. public function leftJoin($fromAlias, $join, $alias, $condition = null) {
  625. $this->queryBuilder->leftJoin(
  626. $this->quoteAlias($fromAlias),
  627. $this->getTableName($join),
  628. $this->quoteAlias($alias),
  629. $condition
  630. );
  631. return $this;
  632. }
  633. /**
  634. * Creates and adds a right join to the query.
  635. *
  636. * <code>
  637. * $qb = $conn->getQueryBuilder()
  638. * ->select('u.name')
  639. * ->from('users', 'u')
  640. * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  641. * </code>
  642. *
  643. * @param string $fromAlias The alias that points to a from clause.
  644. * @param string $join The table name to join.
  645. * @param string $alias The alias of the join table.
  646. * @param string $condition The condition for the join.
  647. *
  648. * @return $this This QueryBuilder instance.
  649. */
  650. public function rightJoin($fromAlias, $join, $alias, $condition = null) {
  651. $this->queryBuilder->rightJoin(
  652. $this->quoteAlias($fromAlias),
  653. $this->getTableName($join),
  654. $this->quoteAlias($alias),
  655. $condition
  656. );
  657. return $this;
  658. }
  659. /**
  660. * Sets a new value for a column in a bulk update query.
  661. *
  662. * <code>
  663. * $qb = $conn->getQueryBuilder()
  664. * ->update('users', 'u')
  665. * ->set('u.password', md5('password'))
  666. * ->where('u.id = ?');
  667. * </code>
  668. *
  669. * @param string $key The column to set.
  670. * @param ILiteral|IParameter|IQueryFunction|string $value The value, expression, placeholder, etc.
  671. *
  672. * @return $this This QueryBuilder instance.
  673. */
  674. public function set($key, $value) {
  675. $this->queryBuilder->set(
  676. $this->helper->quoteColumnName($key),
  677. $this->helper->quoteColumnName($value)
  678. );
  679. return $this;
  680. }
  681. /**
  682. * Specifies one or more restrictions to the query result.
  683. * Replaces any previously specified restrictions, if any.
  684. *
  685. * <code>
  686. * $qb = $conn->getQueryBuilder()
  687. * ->select('u.name')
  688. * ->from('users', 'u')
  689. * ->where('u.id = ?');
  690. *
  691. * // You can optionally programatically build and/or expressions
  692. * $qb = $conn->getQueryBuilder();
  693. *
  694. * $or = $qb->expr()->orx();
  695. * $or->add($qb->expr()->eq('u.id', 1));
  696. * $or->add($qb->expr()->eq('u.id', 2));
  697. *
  698. * $qb->update('users', 'u')
  699. * ->set('u.password', md5('password'))
  700. * ->where($or);
  701. * </code>
  702. *
  703. * @param mixed ...$predicates The restriction predicates.
  704. *
  705. * @return $this This QueryBuilder instance.
  706. */
  707. public function where(...$predicates) {
  708. call_user_func_array(
  709. [$this->queryBuilder, 'where'],
  710. $predicates
  711. );
  712. return $this;
  713. }
  714. /**
  715. * Adds one or more restrictions to the query results, forming a logical
  716. * conjunction with any previously specified restrictions.
  717. *
  718. * <code>
  719. * $qb = $conn->getQueryBuilder()
  720. * ->select('u')
  721. * ->from('users', 'u')
  722. * ->where('u.username LIKE ?')
  723. * ->andWhere('u.is_active = 1');
  724. * </code>
  725. *
  726. * @param mixed ...$where The query restrictions.
  727. *
  728. * @return $this This QueryBuilder instance.
  729. *
  730. * @see where()
  731. */
  732. public function andWhere(...$where) {
  733. call_user_func_array(
  734. [$this->queryBuilder, 'andWhere'],
  735. $where
  736. );
  737. return $this;
  738. }
  739. /**
  740. * Adds one or more restrictions to the query results, forming a logical
  741. * disjunction with any previously specified restrictions.
  742. *
  743. * <code>
  744. * $qb = $conn->getQueryBuilder()
  745. * ->select('u.name')
  746. * ->from('users', 'u')
  747. * ->where('u.id = 1')
  748. * ->orWhere('u.id = 2');
  749. * </code>
  750. *
  751. * @param mixed ...$where The WHERE statement.
  752. *
  753. * @return $this This QueryBuilder instance.
  754. *
  755. * @see where()
  756. */
  757. public function orWhere(...$where) {
  758. call_user_func_array(
  759. [$this->queryBuilder, 'orWhere'],
  760. $where
  761. );
  762. return $this;
  763. }
  764. /**
  765. * Specifies a grouping over the results of the query.
  766. * Replaces any previously specified groupings, if any.
  767. *
  768. * <code>
  769. * $qb = $conn->getQueryBuilder()
  770. * ->select('u.name')
  771. * ->from('users', 'u')
  772. * ->groupBy('u.id');
  773. * </code>
  774. *
  775. * @param mixed ...$groupBys The grouping expression.
  776. *
  777. * @return $this This QueryBuilder instance.
  778. */
  779. public function groupBy(...$groupBys) {
  780. if (count($groupBys) === 1 && is_array($groupBys[0])) {
  781. $groupBys = $groupBys[0];
  782. }
  783. call_user_func_array(
  784. [$this->queryBuilder, 'groupBy'],
  785. $this->helper->quoteColumnNames($groupBys)
  786. );
  787. return $this;
  788. }
  789. /**
  790. * Adds a grouping expression to the query.
  791. *
  792. * <code>
  793. * $qb = $conn->getQueryBuilder()
  794. * ->select('u.name')
  795. * ->from('users', 'u')
  796. * ->groupBy('u.lastLogin');
  797. * ->addGroupBy('u.createdAt')
  798. * </code>
  799. *
  800. * @param mixed ...$groupBy The grouping expression.
  801. *
  802. * @return $this This QueryBuilder instance.
  803. */
  804. public function addGroupBy(...$groupBys) {
  805. if (count($groupBys) === 1 && is_array($groupBys[0])) {
  806. $$groupBys = $groupBys[0];
  807. }
  808. call_user_func_array(
  809. [$this->queryBuilder, 'addGroupBy'],
  810. $this->helper->quoteColumnNames($groupBys)
  811. );
  812. return $this;
  813. }
  814. /**
  815. * Sets a value for a column in an insert query.
  816. *
  817. * <code>
  818. * $qb = $conn->getQueryBuilder()
  819. * ->insert('users')
  820. * ->values(
  821. * array(
  822. * 'name' => '?'
  823. * )
  824. * )
  825. * ->setValue('password', '?');
  826. * </code>
  827. *
  828. * @param string $column The column into which the value should be inserted.
  829. * @param IParameter|string $value The value that should be inserted into the column.
  830. *
  831. * @return $this This QueryBuilder instance.
  832. */
  833. public function setValue($column, $value) {
  834. $this->queryBuilder->setValue(
  835. $this->helper->quoteColumnName($column),
  836. (string) $value
  837. );
  838. return $this;
  839. }
  840. /**
  841. * Specifies values for an insert query indexed by column names.
  842. * Replaces any previous values, if any.
  843. *
  844. * <code>
  845. * $qb = $conn->getQueryBuilder()
  846. * ->insert('users')
  847. * ->values(
  848. * array(
  849. * 'name' => '?',
  850. * 'password' => '?'
  851. * )
  852. * );
  853. * </code>
  854. *
  855. * @param array $values The values to specify for the insert query indexed by column names.
  856. *
  857. * @return $this This QueryBuilder instance.
  858. */
  859. public function values(array $values) {
  860. $quotedValues = [];
  861. foreach ($values as $key => $value) {
  862. $quotedValues[$this->helper->quoteColumnName($key)] = $value;
  863. }
  864. $this->queryBuilder->values($quotedValues);
  865. return $this;
  866. }
  867. /**
  868. * Specifies a restriction over the groups of the query.
  869. * Replaces any previous having restrictions, if any.
  870. *
  871. * @param mixed ...$having The restriction over the groups.
  872. *
  873. * @return $this This QueryBuilder instance.
  874. */
  875. public function having(...$having) {
  876. call_user_func_array(
  877. [$this->queryBuilder, 'having'],
  878. $having
  879. );
  880. return $this;
  881. }
  882. /**
  883. * Adds a restriction over the groups of the query, forming a logical
  884. * conjunction with any existing having restrictions.
  885. *
  886. * @param mixed ...$having The restriction to append.
  887. *
  888. * @return $this This QueryBuilder instance.
  889. */
  890. public function andHaving(...$having) {
  891. call_user_func_array(
  892. [$this->queryBuilder, 'andHaving'],
  893. $having
  894. );
  895. return $this;
  896. }
  897. /**
  898. * Adds a restriction over the groups of the query, forming a logical
  899. * disjunction with any existing having restrictions.
  900. *
  901. * @param mixed ...$having The restriction to add.
  902. *
  903. * @return $this This QueryBuilder instance.
  904. */
  905. public function orHaving(...$having) {
  906. call_user_func_array(
  907. [$this->queryBuilder, 'orHaving'],
  908. $having
  909. );
  910. return $this;
  911. }
  912. /**
  913. * Specifies an ordering for the query results.
  914. * Replaces any previously specified orderings, if any.
  915. *
  916. * @param string $sort The ordering expression.
  917. * @param string $order The ordering direction.
  918. *
  919. * @return $this This QueryBuilder instance.
  920. */
  921. public function orderBy($sort, $order = null) {
  922. $this->queryBuilder->orderBy(
  923. $this->helper->quoteColumnName($sort),
  924. $order
  925. );
  926. return $this;
  927. }
  928. /**
  929. * Adds an ordering to the query results.
  930. *
  931. * @param string $sort The ordering expression.
  932. * @param string $order The ordering direction.
  933. *
  934. * @return $this This QueryBuilder instance.
  935. */
  936. public function addOrderBy($sort, $order = null) {
  937. $this->queryBuilder->addOrderBy(
  938. $this->helper->quoteColumnName($sort),
  939. $order
  940. );
  941. return $this;
  942. }
  943. /**
  944. * Gets a query part by its name.
  945. *
  946. * @param string $queryPartName
  947. *
  948. * @return mixed
  949. */
  950. public function getQueryPart($queryPartName) {
  951. return $this->queryBuilder->getQueryPart($queryPartName);
  952. }
  953. /**
  954. * Gets all query parts.
  955. *
  956. * @return array
  957. */
  958. public function getQueryParts() {
  959. return $this->queryBuilder->getQueryParts();
  960. }
  961. /**
  962. * Resets SQL parts.
  963. *
  964. * @param array|null $queryPartNames
  965. *
  966. * @return $this This QueryBuilder instance.
  967. */
  968. public function resetQueryParts($queryPartNames = null) {
  969. $this->queryBuilder->resetQueryParts($queryPartNames);
  970. return $this;
  971. }
  972. /**
  973. * Resets a single SQL part.
  974. *
  975. * @param string $queryPartName
  976. *
  977. * @return $this This QueryBuilder instance.
  978. */
  979. public function resetQueryPart($queryPartName) {
  980. $this->queryBuilder->resetQueryPart($queryPartName);
  981. return $this;
  982. }
  983. /**
  984. * Creates a new named parameter and bind the value $value to it.
  985. *
  986. * This method provides a shortcut for PDOStatement::bindValue
  987. * when using prepared statements.
  988. *
  989. * The parameter $value specifies the value that you want to bind. If
  990. * $placeholder is not provided bindValue() will automatically create a
  991. * placeholder for you. An automatic placeholder will be of the name
  992. * ':dcValue1', ':dcValue2' etc.
  993. *
  994. * For more information see {@link https://www.php.net/pdostatement-bindparam}
  995. *
  996. * Example:
  997. * <code>
  998. * $value = 2;
  999. * $q->eq( 'id', $q->bindValue( $value ) );
  1000. * $stmt = $q->executeQuery(); // executed with 'id = 2'
  1001. * </code>
  1002. *
  1003. * @license New BSD License
  1004. * @link http://www.zetacomponents.org
  1005. *
  1006. * @param mixed $value
  1007. * @param mixed $type
  1008. * @param string $placeHolder The name to bind with. The string must start with a colon ':'.
  1009. *
  1010. * @return IParameter the placeholder name used.
  1011. */
  1012. public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $placeHolder = null) {
  1013. return new Parameter($this->queryBuilder->createNamedParameter($value, $type, $placeHolder));
  1014. }
  1015. /**
  1016. * Creates a new positional parameter and bind the given value to it.
  1017. *
  1018. * Attention: If you are using positional parameters with the query builder you have
  1019. * to be very careful to bind all parameters in the order they appear in the SQL
  1020. * statement , otherwise they get bound in the wrong order which can lead to serious
  1021. * bugs in your code.
  1022. *
  1023. * Example:
  1024. * <code>
  1025. * $qb = $conn->getQueryBuilder();
  1026. * $qb->select('u.*')
  1027. * ->from('users', 'u')
  1028. * ->where('u.username = ' . $qb->createPositionalParameter('Foo', IQueryBuilder::PARAM_STR))
  1029. * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', IQueryBuilder::PARAM_STR))
  1030. * </code>
  1031. *
  1032. * @param mixed $value
  1033. * @param integer $type
  1034. *
  1035. * @return IParameter
  1036. */
  1037. public function createPositionalParameter($value, $type = IQueryBuilder::PARAM_STR) {
  1038. return new Parameter($this->queryBuilder->createPositionalParameter($value, $type));
  1039. }
  1040. /**
  1041. * Creates a new parameter
  1042. *
  1043. * Example:
  1044. * <code>
  1045. * $qb = $conn->getQueryBuilder();
  1046. * $qb->select('u.*')
  1047. * ->from('users', 'u')
  1048. * ->where('u.username = ' . $qb->createParameter('name'))
  1049. * ->setParameter('name', 'Bar', IQueryBuilder::PARAM_STR))
  1050. * </code>
  1051. *
  1052. * @param string $name
  1053. *
  1054. * @return IParameter
  1055. */
  1056. public function createParameter($name) {
  1057. return new Parameter(':' . $name);
  1058. }
  1059. /**
  1060. * Creates a new function
  1061. *
  1062. * Attention: Column names inside the call have to be quoted before hand
  1063. *
  1064. * Example:
  1065. * <code>
  1066. * $qb = $conn->getQueryBuilder();
  1067. * $qb->select($qb->createFunction('COUNT(*)'))
  1068. * ->from('users', 'u')
  1069. * echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u
  1070. * </code>
  1071. * <code>
  1072. * $qb = $conn->getQueryBuilder();
  1073. * $qb->select($qb->createFunction('COUNT(`column`)'))
  1074. * ->from('users', 'u')
  1075. * echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u
  1076. * </code>
  1077. *
  1078. * @param string $call
  1079. *
  1080. * @return IQueryFunction
  1081. */
  1082. public function createFunction($call) {
  1083. return new QueryFunction($call);
  1084. }
  1085. /**
  1086. * Used to get the id of the last inserted element
  1087. * @return int
  1088. * @throws \BadMethodCallException When being called before an insert query has been run.
  1089. */
  1090. public function getLastInsertId() {
  1091. if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable) {
  1092. // lastInsertId() needs the prefix but no quotes
  1093. $table = $this->prefixTableName($this->lastInsertedTable);
  1094. return (int) $this->connection->lastInsertId($table);
  1095. }
  1096. throw new \BadMethodCallException('Invalid call to getLastInsertId without using insert() before.');
  1097. }
  1098. /**
  1099. * Returns the table name quoted and with database prefix as needed by the implementation
  1100. *
  1101. * @param string $table
  1102. * @return string
  1103. */
  1104. public function getTableName($table) {
  1105. if ($table instanceof IQueryFunction) {
  1106. return (string) $table;
  1107. }
  1108. $table = $this->prefixTableName($table);
  1109. return $this->helper->quoteColumnName($table);
  1110. }
  1111. /**
  1112. * Returns the table name with database prefix as needed by the implementation
  1113. *
  1114. * @param string $table
  1115. * @return string
  1116. */
  1117. protected function prefixTableName($table) {
  1118. if ($this->automaticTablePrefix === false || strpos($table, '*PREFIX*') === 0) {
  1119. return $table;
  1120. }
  1121. return '*PREFIX*' . $table;
  1122. }
  1123. /**
  1124. * Returns the column name quoted and with table alias prefix as needed by the implementation
  1125. *
  1126. * @param string $column
  1127. * @param string $tableAlias
  1128. * @return string
  1129. */
  1130. public function getColumnName($column, $tableAlias = '') {
  1131. if ($tableAlias !== '') {
  1132. $tableAlias .= '.';
  1133. }
  1134. return $this->helper->quoteColumnName($tableAlias . $column);
  1135. }
  1136. /**
  1137. * Returns the column name quoted and with table alias prefix as needed by the implementation
  1138. *
  1139. * @param string $alias
  1140. * @return string
  1141. */
  1142. public function quoteAlias($alias) {
  1143. if ($alias === '' || $alias === null) {
  1144. return $alias;
  1145. }
  1146. return $this->helper->quoteColumnName($alias);
  1147. }
  1148. }