Passed
Push — master ( 3bbacb...7b7358 )
by Roeland
15:09 queued 10s
created

QueryBuilder::executeQuery()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 4
nop 0
dl 0
loc 16
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Daniel Kesselberg <[email protected]>
7
 * @author J0WI <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 * @author Vincent Petry <[email protected]>
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
31
namespace OC\DB\QueryBuilder;
32
33
use Doctrine\DBAL\Platforms\MySQLPlatform;
34
use Doctrine\DBAL\Platforms\OraclePlatform;
35
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
36
use Doctrine\DBAL\Platforms\SqlitePlatform;
37
use Doctrine\DBAL\Query\QueryException;
38
use OC\DB\ConnectionAdapter;
39
use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder;
40
use OC\DB\QueryBuilder\ExpressionBuilder\MySqlExpressionBuilder;
41
use OC\DB\QueryBuilder\ExpressionBuilder\OCIExpressionBuilder;
42
use OC\DB\QueryBuilder\ExpressionBuilder\PgSqlExpressionBuilder;
43
use OC\DB\QueryBuilder\ExpressionBuilder\SqliteExpressionBuilder;
44
use OC\DB\QueryBuilder\FunctionBuilder\FunctionBuilder;
45
use OC\DB\QueryBuilder\FunctionBuilder\OCIFunctionBuilder;
46
use OC\DB\QueryBuilder\FunctionBuilder\PgSqlFunctionBuilder;
47
use OC\DB\QueryBuilder\FunctionBuilder\SqliteFunctionBuilder;
48
use OC\DB\ResultAdapter;
49
use OC\SystemConfig;
50
use OCP\DB\IResult;
51
use OCP\DB\QueryBuilder\ICompositeExpression;
52
use OCP\DB\QueryBuilder\ILiteral;
53
use OCP\DB\QueryBuilder\IParameter;
54
use OCP\DB\QueryBuilder\IQueryBuilder;
55
use OCP\DB\QueryBuilder\IQueryFunction;
56
use OCP\ILogger;
57
58
class QueryBuilder implements IQueryBuilder {
59
60
	/** @var ConnectionAdapter */
61
	private $connection;
62
63
	/** @var SystemConfig */
64
	private $systemConfig;
65
66
	/** @var ILogger */
67
	private $logger;
68
69
	/** @var \Doctrine\DBAL\Query\QueryBuilder */
70
	private $queryBuilder;
71
72
	/** @var QuoteHelper */
73
	private $helper;
74
75
	/** @var bool */
76
	private $automaticTablePrefix = true;
77
78
	/** @var string */
79
	protected $lastInsertedTable;
80
81
	/**
82
	 * Initializes a new QueryBuilder.
83
	 *
84
	 * @param ConnectionAdapter $connection
85
	 * @param SystemConfig $systemConfig
86
	 * @param ILogger $logger
87
	 */
88
	public function __construct(ConnectionAdapter $connection, SystemConfig $systemConfig, ILogger $logger) {
89
		$this->connection = $connection;
90
		$this->systemConfig = $systemConfig;
91
		$this->logger = $logger;
92
		$this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection->getInner());
93
		$this->helper = new QuoteHelper();
94
	}
95
96
	/**
97
	 * Enable/disable automatic prefixing of table names with the oc_ prefix
98
	 *
99
	 * @param bool $enabled If set to true table names will be prefixed with the
100
	 * owncloud database prefix automatically.
101
	 * @since 8.2.0
102
	 */
103
	public function automaticTablePrefix($enabled) {
104
		$this->automaticTablePrefix = (bool) $enabled;
105
	}
106
107
	/**
108
	 * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
109
	 * This producer method is intended for convenient inline usage. Example:
110
	 *
111
	 * <code>
112
	 *     $qb = $conn->getQueryBuilder()
113
	 *         ->select('u')
114
	 *         ->from('users', 'u')
115
	 *         ->where($qb->expr()->eq('u.id', 1));
116
	 * </code>
117
	 *
118
	 * For more complex expression construction, consider storing the expression
119
	 * builder object in a local variable.
120
	 *
121
	 * @return \OCP\DB\QueryBuilder\IExpressionBuilder
122
	 */
123
	public function expr() {
124
		if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
125
			return new OCIExpressionBuilder($this->connection, $this);
126
		}
127
		if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) {
128
			return new PgSqlExpressionBuilder($this->connection, $this);
129
		}
130
		if ($this->connection->getDatabasePlatform() instanceof MySQLPlatform) {
131
			return new MySqlExpressionBuilder($this->connection, $this);
132
		}
133
		if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
134
			return new SqliteExpressionBuilder($this->connection, $this);
135
		}
136
137
		return new ExpressionBuilder($this->connection, $this);
138
	}
139
140
	/**
141
	 * Gets an FunctionBuilder used for object-oriented construction of query functions.
142
	 * This producer method is intended for convenient inline usage. Example:
143
	 *
144
	 * <code>
145
	 *     $qb = $conn->getQueryBuilder()
146
	 *         ->select('u')
147
	 *         ->from('users', 'u')
148
	 *         ->where($qb->fun()->md5('u.id'));
149
	 * </code>
150
	 *
151
	 * For more complex function construction, consider storing the function
152
	 * builder object in a local variable.
153
	 *
154
	 * @return \OCP\DB\QueryBuilder\IFunctionBuilder
155
	 */
156
	public function func() {
157
		if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
158
			return new OCIFunctionBuilder($this->helper);
159
		}
160
		if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
161
			return new SqliteFunctionBuilder($this->helper);
162
		}
163
		if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) {
164
			return new PgSqlFunctionBuilder($this->helper);
165
		}
166
167
		return new FunctionBuilder($this->helper);
168
	}
169
170
	/**
171
	 * Gets the type of the currently built query.
172
	 *
173
	 * @return integer
174
	 */
175
	public function getType() {
176
		return $this->queryBuilder->getType();
177
	}
178
179
	/**
180
	 * Gets the associated DBAL Connection for this query builder.
181
	 *
182
	 * @return \OCP\IDBConnection
183
	 */
184
	public function getConnection() {
185
		return $this->connection;
186
	}
187
188
	/**
189
	 * Gets the state of this query builder instance.
190
	 *
191
	 * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
192
	 */
193
	public function getState() {
194
		return $this->queryBuilder->getState();
195
	}
196
197
	/**
198
	 * Executes this query using the bound parameters and their types.
199
	 *
200
	 * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
201
	 * for insert, update and delete statements.
202
	 *
203
	 * @return IResult|int
204
	 */
205
	public function execute() {
206
		if ($this->systemConfig->getValue('log_query', false)) {
207
			try {
208
				$params = [];
209
				foreach ($this->getParameters() as $placeholder => $value) {
210
					if ($value instanceof \DateTime) {
211
						$params[] = $placeholder . ' => DateTime:\'' . $value->format('c') . '\'';
212
					} elseif (is_array($value)) {
213
						$params[] = $placeholder . ' => (\'' . implode('\', \'', $value) . '\')';
214
					} else {
215
						$params[] = $placeholder . ' => \'' . $value . '\'';
216
					}
217
				}
218
				if (empty($params)) {
219
					$this->logger->debug('DB QueryBuilder: \'{query}\'', [
220
						'query' => $this->getSQL(),
221
						'app' => 'core',
222
					]);
223
				} else {
224
					$this->logger->debug('DB QueryBuilder: \'{query}\' with parameters: {params}', [
225
						'query' => $this->getSQL(),
226
						'params' => implode(', ', $params),
227
						'app' => 'core',
228
					]);
229
				}
230
			} catch (\Error $e) {
231
				// likely an error during conversion of $value to string
232
				$this->logger->debug('DB QueryBuilder: error trying to log SQL query');
233
				$this->logger->logException($e);
234
			}
235
		}
236
237
		if (!empty($this->getQueryPart('select'))) {
238
			$select = $this->getQueryPart('select');
239
			$hasSelectAll = array_filter($select, static function ($s) {
240
				return $s === '*';
241
			});
242
			$hasSelectSpecific = array_filter($select, static function ($s) {
243
				return $s !== '*';
244
			});
245
246
			if (empty($hasSelectAll) === empty($hasSelectSpecific)) {
247
				$exception = new QueryException('Query is selecting * and specific values in the same query. This is not supported in Oracle.');
248
				$this->logger->logException($exception, [
249
					'message' => 'Query is selecting * and specific values in the same query. This is not supported in Oracle.',
250
					'query' => $this->getSQL(),
251
					'level' => ILogger::ERROR,
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

251
					'level' => /** @scrutinizer ignore-deprecated */ ILogger::ERROR,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
252
					'app' => 'core',
253
				]);
254
			}
255
		}
256
257
		$numberOfParameters = 0;
258
		$hasTooLargeArrayParameter = false;
259
		foreach ($this->getParameters() as $parameter) {
260
			if (is_array($parameter)) {
261
				$count = count($parameter);
262
				$numberOfParameters += $count;
263
				$hasTooLargeArrayParameter = $hasTooLargeArrayParameter || ($count > 1000);
264
			}
265
		}
266
267
		if ($hasTooLargeArrayParameter) {
268
			$exception = new QueryException('More than 1000 expressions in a list are not allowed on Oracle.');
269
			$this->logger->logException($exception, [
270
				'message' => 'More than 1000 expressions in a list are not allowed on Oracle.',
271
				'query' => $this->getSQL(),
272
				'level' => ILogger::ERROR,
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

272
				'level' => /** @scrutinizer ignore-deprecated */ ILogger::ERROR,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
273
				'app' => 'core',
274
			]);
275
		}
276
277
		if ($numberOfParameters > 65535) {
278
			$exception = new QueryException('The number of parameters must not exceed 65535. Restriction by PostgreSQL.');
279
			$this->logger->logException($exception, [
280
				'message' => 'The number of parameters must not exceed 65535. Restriction by PostgreSQL.',
281
				'query' => $this->getSQL(),
282
				'level' => ILogger::ERROR,
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

282
				'level' => /** @scrutinizer ignore-deprecated */ ILogger::ERROR,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
283
				'app' => 'core',
284
			]);
285
		}
286
287
		$result = $this->queryBuilder->execute();
288
		if (is_int($result)) {
289
			return $result;
290
		}
291
		return new ResultAdapter($result);
292
	}
293
294
	public function executeQuery(): IResult {
295
		if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder::SELECT) {
296
			throw new \RuntimeException('Invalid query type, expected SELECT query');
297
		}
298
299
		try {
300
			$result = $this->execute();
301
		} catch (\Doctrine\DBAL\Exception $e) {
302
			throw \OC\DB\Exceptions\DbalException::wrap($e);
303
		}
304
305
		if ($result instanceof IResult) {
0 ignored issues
show
introduced by
$result is always a sub-type of OCP\DB\IResult.
Loading history...
306
			return $result;
307
		}
308
309
		throw new \RuntimeException('Invalid return type for query');
310
	}
311
312
	public function executeUpdate(): int {
313
		if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::SELECT) {
314
			throw new \RuntimeException('Invalid query type, expected INSERT, DELETE or UPDATE query');
315
		}
316
317
		try {
318
			$result = $this->execute();
319
		} catch (\Doctrine\DBAL\Exception $e) {
320
			throw \OC\DB\Exceptions\DbalException::wrap($e);
321
		}
322
323
		if (!is_int($result)) {
0 ignored issues
show
introduced by
The condition is_int($result) is always false.
Loading history...
324
			throw new \RuntimeException('Invalid return type for query');
325
		}
326
327
		return $result;
328
	}
329
330
331
	/**
332
	 * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
333
	 *
334
	 * <code>
335
	 *     $qb = $conn->getQueryBuilder()
336
	 *         ->select('u')
337
	 *         ->from('User', 'u')
338
	 *     echo $qb->getSQL(); // SELECT u FROM User u
339
	 * </code>
340
	 *
341
	 * @return string The SQL query string.
342
	 */
343
	public function getSQL() {
344
		return $this->queryBuilder->getSQL();
345
	}
346
347
	/**
348
	 * Sets a query parameter for the query being constructed.
349
	 *
350
	 * <code>
351
	 *     $qb = $conn->getQueryBuilder()
352
	 *         ->select('u')
353
	 *         ->from('users', 'u')
354
	 *         ->where('u.id = :user_id')
355
	 *         ->setParameter(':user_id', 1);
356
	 * </code>
357
	 *
358
	 * @param string|integer $key The parameter position or name.
359
	 * @param mixed $value The parameter value.
360
	 * @param string|null|int $type One of the IQueryBuilder::PARAM_* constants.
361
	 *
362
	 * @return $this This QueryBuilder instance.
363
	 */
364
	public function setParameter($key, $value, $type = null) {
365
		$this->queryBuilder->setParameter($key, $value, $type);
366
367
		return $this;
368
	}
369
370
	/**
371
	 * Sets a collection of query parameters for the query being constructed.
372
	 *
373
	 * <code>
374
	 *     $qb = $conn->getQueryBuilder()
375
	 *         ->select('u')
376
	 *         ->from('users', 'u')
377
	 *         ->where('u.id = :user_id1 OR u.id = :user_id2')
378
	 *         ->setParameters(array(
379
	 *             ':user_id1' => 1,
380
	 *             ':user_id2' => 2
381
	 *         ));
382
	 * </code>
383
	 *
384
	 * @param array $params The query parameters to set.
385
	 * @param array $types The query parameters types to set.
386
	 *
387
	 * @return $this This QueryBuilder instance.
388
	 */
389
	public function setParameters(array $params, array $types = []) {
390
		$this->queryBuilder->setParameters($params, $types);
391
392
		return $this;
393
	}
394
395
	/**
396
	 * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
397
	 *
398
	 * @return array The currently defined query parameters indexed by parameter index or name.
399
	 */
400
	public function getParameters() {
401
		return $this->queryBuilder->getParameters();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->queryBuilder->getParameters() returns the type Doctrine\DBAL\Query\list which is incompatible with the documented return type array.
Loading history...
402
	}
403
404
	/**
405
	 * Gets a (previously set) query parameter of the query being constructed.
406
	 *
407
	 * @param mixed $key The key (index or name) of the bound parameter.
408
	 *
409
	 * @return mixed The value of the bound parameter.
410
	 */
411
	public function getParameter($key) {
412
		return $this->queryBuilder->getParameter($key);
413
	}
414
415
	/**
416
	 * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
417
	 *
418
	 * @return array The currently defined query parameter types indexed by parameter index or name.
419
	 */
420
	public function getParameterTypes() {
421
		return $this->queryBuilder->getParameterTypes();
422
	}
423
424
	/**
425
	 * Gets a (previously set) query parameter type of the query being constructed.
426
	 *
427
	 * @param mixed $key The key (index or name) of the bound parameter type.
428
	 *
429
	 * @return mixed The value of the bound parameter type.
430
	 */
431
	public function getParameterType($key) {
432
		return $this->queryBuilder->getParameterType($key);
433
	}
434
435
	/**
436
	 * Sets the position of the first result to retrieve (the "offset").
437
	 *
438
	 * @param integer $firstResult The first result to return.
439
	 *
440
	 * @return $this This QueryBuilder instance.
441
	 */
442
	public function setFirstResult($firstResult) {
443
		$this->queryBuilder->setFirstResult($firstResult);
444
445
		return $this;
446
	}
447
448
	/**
449
	 * Gets the position of the first result the query object was set to retrieve (the "offset").
450
	 * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
451
	 *
452
	 * @return integer The position of the first result.
453
	 */
454
	public function getFirstResult() {
455
		return $this->queryBuilder->getFirstResult();
456
	}
457
458
	/**
459
	 * Sets the maximum number of results to retrieve (the "limit").
460
	 *
461
	 * NOTE: Setting max results to "0" will cause mixed behaviour. While most
462
	 * of the databases will just return an empty result set, Oracle will return
463
	 * all entries.
464
	 *
465
	 * @param integer $maxResults The maximum number of results to retrieve.
466
	 *
467
	 * @return $this This QueryBuilder instance.
468
	 */
469
	public function setMaxResults($maxResults) {
470
		$this->queryBuilder->setMaxResults($maxResults);
471
472
		return $this;
473
	}
474
475
	/**
476
	 * Gets the maximum number of results the query object was set to retrieve (the "limit").
477
	 * Returns NULL if {@link setMaxResults} was not applied to this query builder.
478
	 *
479
	 * @return int|null The maximum number of results.
480
	 */
481
	public function getMaxResults() {
482
		return $this->queryBuilder->getMaxResults();
483
	}
484
485
	/**
486
	 * Specifies an item that is to be returned in the query result.
487
	 * Replaces any previously specified selections, if any.
488
	 *
489
	 * <code>
490
	 *     $qb = $conn->getQueryBuilder()
491
	 *         ->select('u.id', 'p.id')
492
	 *         ->from('users', 'u')
493
	 *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
494
	 * </code>
495
	 *
496
	 * @param mixed ...$selects The selection expressions.
497
	 *
498
	 * '@return $this This QueryBuilder instance.
499
	 */
500
	public function select(...$selects) {
501
		if (count($selects) === 1 && is_array($selects[0])) {
502
			$selects = $selects[0];
503
		}
504
505
		$this->queryBuilder->select(
506
			$this->helper->quoteColumnNames($selects)
507
		);
508
509
		return $this;
510
	}
511
512
	/**
513
	 * Specifies an item that is to be returned with a different name in the query result.
514
	 *
515
	 * <code>
516
	 *     $qb = $conn->getQueryBuilder()
517
	 *         ->selectAlias('u.id', 'user_id')
518
	 *         ->from('users', 'u')
519
	 *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
520
	 * </code>
521
	 *
522
	 * @param mixed $select The selection expressions.
523
	 * @param string $alias The column alias used in the constructed query.
524
	 *
525
	 * @return $this This QueryBuilder instance.
526
	 */
527
	public function selectAlias($select, $alias) {
528
		$this->queryBuilder->addSelect(
529
			$this->helper->quoteColumnName($select) . ' AS ' . $this->helper->quoteColumnName($alias)
530
		);
531
532
		return $this;
533
	}
534
535
	/**
536
	 * Specifies an item that is to be returned uniquely in the query result.
537
	 *
538
	 * <code>
539
	 *     $qb = $conn->getQueryBuilder()
540
	 *         ->selectDistinct('type')
541
	 *         ->from('users');
542
	 * </code>
543
	 *
544
	 * @param mixed $select The selection expressions.
545
	 *
546
	 * @return $this This QueryBuilder instance.
547
	 */
548
	public function selectDistinct($select) {
549
		if (!is_array($select)) {
550
			$select = [$select];
551
		}
552
553
		$quotedSelect = $this->helper->quoteColumnNames($select);
554
555
		$this->queryBuilder->addSelect(
556
			'DISTINCT ' . implode(', ', $quotedSelect)
557
		);
558
559
		return $this;
560
	}
561
562
	/**
563
	 * Adds an item that is to be returned in the query result.
564
	 *
565
	 * <code>
566
	 *     $qb = $conn->getQueryBuilder()
567
	 *         ->select('u.id')
568
	 *         ->addSelect('p.id')
569
	 *         ->from('users', 'u')
570
	 *         ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
571
	 * </code>
572
	 *
573
	 * @param mixed ...$selects The selection expression.
574
	 *
575
	 * @return $this This QueryBuilder instance.
576
	 */
577
	public function addSelect(...$selects) {
578
		if (count($selects) === 1 && is_array($selects[0])) {
579
			$selects = $selects[0];
580
		}
581
582
		$this->queryBuilder->addSelect(
583
			$this->helper->quoteColumnNames($selects)
584
		);
585
586
		return $this;
587
	}
588
589
	/**
590
	 * Turns the query being built into a bulk delete query that ranges over
591
	 * a certain table.
592
	 *
593
	 * <code>
594
	 *     $qb = $conn->getQueryBuilder()
595
	 *         ->delete('users', 'u')
596
	 *         ->where('u.id = :user_id');
597
	 *         ->setParameter(':user_id', 1);
598
	 * </code>
599
	 *
600
	 * @param string $delete The table whose rows are subject to the deletion.
601
	 * @param string $alias The table alias used in the constructed query.
602
	 *
603
	 * @return $this This QueryBuilder instance.
604
	 */
605
	public function delete($delete = null, $alias = null) {
606
		$this->queryBuilder->delete(
607
			$this->getTableName($delete),
608
			$alias
609
		);
610
611
		return $this;
612
	}
613
614
	/**
615
	 * Turns the query being built into a bulk update query that ranges over
616
	 * a certain table
617
	 *
618
	 * <code>
619
	 *     $qb = $conn->getQueryBuilder()
620
	 *         ->update('users', 'u')
621
	 *         ->set('u.password', md5('password'))
622
	 *         ->where('u.id = ?');
623
	 * </code>
624
	 *
625
	 * @param string $update The table whose rows are subject to the update.
626
	 * @param string $alias The table alias used in the constructed query.
627
	 *
628
	 * @return $this This QueryBuilder instance.
629
	 */
630
	public function update($update = null, $alias = null) {
631
		$this->queryBuilder->update(
632
			$this->getTableName($update),
633
			$alias
634
		);
635
636
		return $this;
637
	}
638
639
	/**
640
	 * Turns the query being built into an insert query that inserts into
641
	 * a certain table
642
	 *
643
	 * <code>
644
	 *     $qb = $conn->getQueryBuilder()
645
	 *         ->insert('users')
646
	 *         ->values(
647
	 *             array(
648
	 *                 'name' => '?',
649
	 *                 'password' => '?'
650
	 *             )
651
	 *         );
652
	 * </code>
653
	 *
654
	 * @param string $insert The table into which the rows should be inserted.
655
	 *
656
	 * @return $this This QueryBuilder instance.
657
	 */
658
	public function insert($insert = null) {
659
		$this->queryBuilder->insert(
660
			$this->getTableName($insert)
661
		);
662
663
		$this->lastInsertedTable = $insert;
664
665
		return $this;
666
	}
667
668
	/**
669
	 * Creates and adds a query root corresponding to the table identified by the
670
	 * given alias, forming a cartesian product with any existing query roots.
671
	 *
672
	 * <code>
673
	 *     $qb = $conn->getQueryBuilder()
674
	 *         ->select('u.id')
675
	 *         ->from('users', 'u')
676
	 * </code>
677
	 *
678
	 * @param string $from The table.
679
	 * @param string|null $alias The alias of the table.
680
	 *
681
	 * @return $this This QueryBuilder instance.
682
	 */
683
	public function from($from, $alias = null) {
684
		$this->queryBuilder->from(
685
			$this->getTableName($from),
686
			$this->quoteAlias($alias)
687
		);
688
689
		return $this;
690
	}
691
692
	/**
693
	 * Creates and adds a join to the query.
694
	 *
695
	 * <code>
696
	 *     $qb = $conn->getQueryBuilder()
697
	 *         ->select('u.name')
698
	 *         ->from('users', 'u')
699
	 *         ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
700
	 * </code>
701
	 *
702
	 * @param string $fromAlias The alias that points to a from clause.
703
	 * @param string $join The table name to join.
704
	 * @param string $alias The alias of the join table.
705
	 * @param string|ICompositeExpression|null $condition The condition for the join.
706
	 *
707
	 * @return $this This QueryBuilder instance.
708
	 */
709
	public function join($fromAlias, $join, $alias, $condition = null) {
710
		$this->queryBuilder->join(
711
			$this->quoteAlias($fromAlias),
712
			$this->getTableName($join),
713
			$this->quoteAlias($alias),
714
			$condition
715
		);
716
717
		return $this;
718
	}
719
720
	/**
721
	 * Creates and adds a join to the query.
722
	 *
723
	 * <code>
724
	 *     $qb = $conn->getQueryBuilder()
725
	 *         ->select('u.name')
726
	 *         ->from('users', 'u')
727
	 *         ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
728
	 * </code>
729
	 *
730
	 * @param string $fromAlias The alias that points to a from clause.
731
	 * @param string $join The table name to join.
732
	 * @param string $alias The alias of the join table.
733
	 * @param string|ICompositeExpression|null $condition The condition for the join.
734
	 *
735
	 * @return $this This QueryBuilder instance.
736
	 */
737
	public function innerJoin($fromAlias, $join, $alias, $condition = null) {
738
		$this->queryBuilder->innerJoin(
739
			$this->quoteAlias($fromAlias),
740
			$this->getTableName($join),
741
			$this->quoteAlias($alias),
742
			$condition
743
		);
744
745
		return $this;
746
	}
747
748
	/**
749
	 * Creates and adds a left join to the query.
750
	 *
751
	 * <code>
752
	 *     $qb = $conn->getQueryBuilder()
753
	 *         ->select('u.name')
754
	 *         ->from('users', 'u')
755
	 *         ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
756
	 * </code>
757
	 *
758
	 * @param string $fromAlias The alias that points to a from clause.
759
	 * @param string $join The table name to join.
760
	 * @param string $alias The alias of the join table.
761
	 * @param string|ICompositeExpression|null $condition The condition for the join.
762
	 *
763
	 * @return $this This QueryBuilder instance.
764
	 */
765
	public function leftJoin($fromAlias, $join, $alias, $condition = null) {
766
		$this->queryBuilder->leftJoin(
767
			$this->quoteAlias($fromAlias),
768
			$this->getTableName($join),
769
			$this->quoteAlias($alias),
770
			$condition
771
		);
772
773
		return $this;
774
	}
775
776
	/**
777
	 * Creates and adds a right join to the query.
778
	 *
779
	 * <code>
780
	 *     $qb = $conn->getQueryBuilder()
781
	 *         ->select('u.name')
782
	 *         ->from('users', 'u')
783
	 *         ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
784
	 * </code>
785
	 *
786
	 * @param string $fromAlias The alias that points to a from clause.
787
	 * @param string $join The table name to join.
788
	 * @param string $alias The alias of the join table.
789
	 * @param string|ICompositeExpression|null $condition The condition for the join.
790
	 *
791
	 * @return $this This QueryBuilder instance.
792
	 */
793
	public function rightJoin($fromAlias, $join, $alias, $condition = null) {
794
		$this->queryBuilder->rightJoin(
795
			$this->quoteAlias($fromAlias),
796
			$this->getTableName($join),
797
			$this->quoteAlias($alias),
798
			$condition
799
		);
800
801
		return $this;
802
	}
803
804
	/**
805
	 * Sets a new value for a column in a bulk update query.
806
	 *
807
	 * <code>
808
	 *     $qb = $conn->getQueryBuilder()
809
	 *         ->update('users', 'u')
810
	 *         ->set('u.password', md5('password'))
811
	 *         ->where('u.id = ?');
812
	 * </code>
813
	 *
814
	 * @param string $key The column to set.
815
	 * @param ILiteral|IParameter|IQueryFunction|string $value The value, expression, placeholder, etc.
816
	 *
817
	 * @return $this This QueryBuilder instance.
818
	 */
819
	public function set($key, $value) {
820
		$this->queryBuilder->set(
821
			$this->helper->quoteColumnName($key),
822
			$this->helper->quoteColumnName($value)
823
		);
824
825
		return $this;
826
	}
827
828
	/**
829
	 * Specifies one or more restrictions to the query result.
830
	 * Replaces any previously specified restrictions, if any.
831
	 *
832
	 * <code>
833
	 *     $qb = $conn->getQueryBuilder()
834
	 *         ->select('u.name')
835
	 *         ->from('users', 'u')
836
	 *         ->where('u.id = ?');
837
	 *
838
	 *     // You can optionally programatically build and/or expressions
839
	 *     $qb = $conn->getQueryBuilder();
840
	 *
841
	 *     $or = $qb->expr()->orx();
842
	 *     $or->add($qb->expr()->eq('u.id', 1));
843
	 *     $or->add($qb->expr()->eq('u.id', 2));
844
	 *
845
	 *     $qb->update('users', 'u')
846
	 *         ->set('u.password', md5('password'))
847
	 *         ->where($or);
848
	 * </code>
849
	 *
850
	 * @param mixed ...$predicates The restriction predicates.
851
	 *
852
	 * @return $this This QueryBuilder instance.
853
	 */
854
	public function where(...$predicates) {
855
		call_user_func_array(
856
			[$this->queryBuilder, 'where'],
857
			$predicates
858
		);
859
860
		return $this;
861
	}
862
863
	/**
864
	 * Adds one or more restrictions to the query results, forming a logical
865
	 * conjunction with any previously specified restrictions.
866
	 *
867
	 * <code>
868
	 *     $qb = $conn->getQueryBuilder()
869
	 *         ->select('u')
870
	 *         ->from('users', 'u')
871
	 *         ->where('u.username LIKE ?')
872
	 *         ->andWhere('u.is_active = 1');
873
	 * </code>
874
	 *
875
	 * @param mixed ...$where The query restrictions.
876
	 *
877
	 * @return $this This QueryBuilder instance.
878
	 *
879
	 * @see where()
880
	 */
881
	public function andWhere(...$where) {
882
		call_user_func_array(
883
			[$this->queryBuilder, 'andWhere'],
884
			$where
885
		);
886
887
		return $this;
888
	}
889
890
	/**
891
	 * Adds one or more restrictions to the query results, forming a logical
892
	 * disjunction with any previously specified restrictions.
893
	 *
894
	 * <code>
895
	 *     $qb = $conn->getQueryBuilder()
896
	 *         ->select('u.name')
897
	 *         ->from('users', 'u')
898
	 *         ->where('u.id = 1')
899
	 *         ->orWhere('u.id = 2');
900
	 * </code>
901
	 *
902
	 * @param mixed ...$where The WHERE statement.
903
	 *
904
	 * @return $this This QueryBuilder instance.
905
	 *
906
	 * @see where()
907
	 */
908
	public function orWhere(...$where) {
909
		call_user_func_array(
910
			[$this->queryBuilder, 'orWhere'],
911
			$where
912
		);
913
914
		return $this;
915
	}
916
917
	/**
918
	 * Specifies a grouping over the results of the query.
919
	 * Replaces any previously specified groupings, if any.
920
	 *
921
	 * <code>
922
	 *     $qb = $conn->getQueryBuilder()
923
	 *         ->select('u.name')
924
	 *         ->from('users', 'u')
925
	 *         ->groupBy('u.id');
926
	 * </code>
927
	 *
928
	 * @param mixed ...$groupBys The grouping expression.
929
	 *
930
	 * @return $this This QueryBuilder instance.
931
	 */
932
	public function groupBy(...$groupBys) {
933
		if (count($groupBys) === 1 && is_array($groupBys[0])) {
934
			$groupBys = $groupBys[0];
935
		}
936
937
		call_user_func_array(
938
			[$this->queryBuilder, 'groupBy'],
939
			$this->helper->quoteColumnNames($groupBys)
940
		);
941
942
		return $this;
943
	}
944
945
	/**
946
	 * Adds a grouping expression to the query.
947
	 *
948
	 * <code>
949
	 *     $qb = $conn->getQueryBuilder()
950
	 *         ->select('u.name')
951
	 *         ->from('users', 'u')
952
	 *         ->groupBy('u.lastLogin');
953
	 *         ->addGroupBy('u.createdAt')
954
	 * </code>
955
	 *
956
	 * @param mixed ...$groupBy The grouping expression.
957
	 *
958
	 * @return $this This QueryBuilder instance.
959
	 */
960
	public function addGroupBy(...$groupBys) {
961
		if (count($groupBys) === 1 && is_array($groupBys[0])) {
962
			$$groupBys = $groupBys[0];
963
		}
964
965
		call_user_func_array(
966
			[$this->queryBuilder, 'addGroupBy'],
967
			$this->helper->quoteColumnNames($groupBys)
968
		);
969
970
		return $this;
971
	}
972
973
	/**
974
	 * Sets a value for a column in an insert query.
975
	 *
976
	 * <code>
977
	 *     $qb = $conn->getQueryBuilder()
978
	 *         ->insert('users')
979
	 *         ->values(
980
	 *             array(
981
	 *                 'name' => '?'
982
	 *             )
983
	 *         )
984
	 *         ->setValue('password', '?');
985
	 * </code>
986
	 *
987
	 * @param string $column The column into which the value should be inserted.
988
	 * @param IParameter|string $value The value that should be inserted into the column.
989
	 *
990
	 * @return $this This QueryBuilder instance.
991
	 */
992
	public function setValue($column, $value) {
993
		$this->queryBuilder->setValue(
994
			$this->helper->quoteColumnName($column),
995
			(string) $value
996
		);
997
998
		return $this;
999
	}
1000
1001
	/**
1002
	 * Specifies values for an insert query indexed by column names.
1003
	 * Replaces any previous values, if any.
1004
	 *
1005
	 * <code>
1006
	 *     $qb = $conn->getQueryBuilder()
1007
	 *         ->insert('users')
1008
	 *         ->values(
1009
	 *             array(
1010
	 *                 'name' => '?',
1011
	 *                 'password' => '?'
1012
	 *             )
1013
	 *         );
1014
	 * </code>
1015
	 *
1016
	 * @param array $values The values to specify for the insert query indexed by column names.
1017
	 *
1018
	 * @return $this This QueryBuilder instance.
1019
	 */
1020
	public function values(array $values) {
1021
		$quotedValues = [];
1022
		foreach ($values as $key => $value) {
1023
			$quotedValues[$this->helper->quoteColumnName($key)] = $value;
1024
		}
1025
1026
		$this->queryBuilder->values($quotedValues);
1027
1028
		return $this;
1029
	}
1030
1031
	/**
1032
	 * Specifies a restriction over the groups of the query.
1033
	 * Replaces any previous having restrictions, if any.
1034
	 *
1035
	 * @param mixed ...$having The restriction over the groups.
1036
	 *
1037
	 * @return $this This QueryBuilder instance.
1038
	 */
1039
	public function having(...$having) {
1040
		call_user_func_array(
1041
			[$this->queryBuilder, 'having'],
1042
			$having
1043
		);
1044
1045
		return $this;
1046
	}
1047
1048
	/**
1049
	 * Adds a restriction over the groups of the query, forming a logical
1050
	 * conjunction with any existing having restrictions.
1051
	 *
1052
	 * @param mixed ...$having The restriction to append.
1053
	 *
1054
	 * @return $this This QueryBuilder instance.
1055
	 */
1056
	public function andHaving(...$having) {
1057
		call_user_func_array(
1058
			[$this->queryBuilder, 'andHaving'],
1059
			$having
1060
		);
1061
1062
		return $this;
1063
	}
1064
1065
	/**
1066
	 * Adds a restriction over the groups of the query, forming a logical
1067
	 * disjunction with any existing having restrictions.
1068
	 *
1069
	 * @param mixed ...$having The restriction to add.
1070
	 *
1071
	 * @return $this This QueryBuilder instance.
1072
	 */
1073
	public function orHaving(...$having) {
1074
		call_user_func_array(
1075
			[$this->queryBuilder, 'orHaving'],
1076
			$having
1077
		);
1078
1079
		return $this;
1080
	}
1081
1082
	/**
1083
	 * Specifies an ordering for the query results.
1084
	 * Replaces any previously specified orderings, if any.
1085
	 *
1086
	 * @param string $sort The ordering expression.
1087
	 * @param string $order The ordering direction.
1088
	 *
1089
	 * @return $this This QueryBuilder instance.
1090
	 */
1091
	public function orderBy($sort, $order = null) {
1092
		$this->queryBuilder->orderBy(
1093
			$this->helper->quoteColumnName($sort),
1094
			$order
1095
		);
1096
1097
		return $this;
1098
	}
1099
1100
	/**
1101
	 * Adds an ordering to the query results.
1102
	 *
1103
	 * @param string $sort The ordering expression.
1104
	 * @param string $order The ordering direction.
1105
	 *
1106
	 * @return $this This QueryBuilder instance.
1107
	 */
1108
	public function addOrderBy($sort, $order = null) {
1109
		$this->queryBuilder->addOrderBy(
1110
			$this->helper->quoteColumnName($sort),
1111
			$order
1112
		);
1113
1114
		return $this;
1115
	}
1116
1117
	/**
1118
	 * Gets a query part by its name.
1119
	 *
1120
	 * @param string $queryPartName
1121
	 *
1122
	 * @return mixed
1123
	 */
1124
	public function getQueryPart($queryPartName) {
1125
		return $this->queryBuilder->getQueryPart($queryPartName);
1126
	}
1127
1128
	/**
1129
	 * Gets all query parts.
1130
	 *
1131
	 * @return array
1132
	 */
1133
	public function getQueryParts() {
1134
		return $this->queryBuilder->getQueryParts();
1135
	}
1136
1137
	/**
1138
	 * Resets SQL parts.
1139
	 *
1140
	 * @param array|null $queryPartNames
1141
	 *
1142
	 * @return $this This QueryBuilder instance.
1143
	 */
1144
	public function resetQueryParts($queryPartNames = null) {
1145
		$this->queryBuilder->resetQueryParts($queryPartNames);
1146
1147
		return $this;
1148
	}
1149
1150
	/**
1151
	 * Resets a single SQL part.
1152
	 *
1153
	 * @param string $queryPartName
1154
	 *
1155
	 * @return $this This QueryBuilder instance.
1156
	 */
1157
	public function resetQueryPart($queryPartName) {
1158
		$this->queryBuilder->resetQueryPart($queryPartName);
1159
1160
		return $this;
1161
	}
1162
1163
	/**
1164
	 * Creates a new named parameter and bind the value $value to it.
1165
	 *
1166
	 * This method provides a shortcut for PDOStatement::bindValue
1167
	 * when using prepared statements.
1168
	 *
1169
	 * The parameter $value specifies the value that you want to bind. If
1170
	 * $placeholder is not provided bindValue() will automatically create a
1171
	 * placeholder for you. An automatic placeholder will be of the name
1172
	 * ':dcValue1', ':dcValue2' etc.
1173
	 *
1174
	 * For more information see {@link https://www.php.net/pdostatement-bindparam}
1175
	 *
1176
	 * Example:
1177
	 * <code>
1178
	 * $value = 2;
1179
	 * $q->eq( 'id', $q->bindValue( $value ) );
1180
	 * $stmt = $q->executeQuery(); // executed with 'id = 2'
1181
	 * </code>
1182
	 *
1183
	 * @license New BSD License
1184
	 * @link http://www.zetacomponents.org
1185
	 *
1186
	 * @param mixed $value
1187
	 * @param mixed $type
1188
	 * @param string $placeHolder The name to bind with. The string must start with a colon ':'.
1189
	 *
1190
	 * @return IParameter the placeholder name used.
1191
	 */
1192
	public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $placeHolder = null) {
1193
		return new Parameter($this->queryBuilder->createNamedParameter($value, $type, $placeHolder));
1194
	}
1195
1196
	/**
1197
	 * Creates a new positional parameter and bind the given value to it.
1198
	 *
1199
	 * Attention: If you are using positional parameters with the query builder you have
1200
	 * to be very careful to bind all parameters in the order they appear in the SQL
1201
	 * statement , otherwise they get bound in the wrong order which can lead to serious
1202
	 * bugs in your code.
1203
	 *
1204
	 * Example:
1205
	 * <code>
1206
	 *  $qb = $conn->getQueryBuilder();
1207
	 *  $qb->select('u.*')
1208
	 *     ->from('users', 'u')
1209
	 *     ->where('u.username = ' . $qb->createPositionalParameter('Foo', IQueryBuilder::PARAM_STR))
1210
	 *     ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', IQueryBuilder::PARAM_STR))
1211
	 * </code>
1212
	 *
1213
	 * @param mixed $value
1214
	 * @param integer $type
1215
	 *
1216
	 * @return IParameter
1217
	 */
1218
	public function createPositionalParameter($value, $type = IQueryBuilder::PARAM_STR) {
1219
		return new Parameter($this->queryBuilder->createPositionalParameter($value, $type));
1220
	}
1221
1222
	/**
1223
	 * Creates a new parameter
1224
	 *
1225
	 * Example:
1226
	 * <code>
1227
	 *  $qb = $conn->getQueryBuilder();
1228
	 *  $qb->select('u.*')
1229
	 *     ->from('users', 'u')
1230
	 *     ->where('u.username = ' . $qb->createParameter('name'))
1231
	 *     ->setParameter('name', 'Bar', IQueryBuilder::PARAM_STR))
1232
	 * </code>
1233
	 *
1234
	 * @param string $name
1235
	 *
1236
	 * @return IParameter
1237
	 */
1238
	public function createParameter($name) {
1239
		return new Parameter(':' . $name);
1240
	}
1241
1242
	/**
1243
	 * Creates a new function
1244
	 *
1245
	 * Attention: Column names inside the call have to be quoted before hand
1246
	 *
1247
	 * Example:
1248
	 * <code>
1249
	 *  $qb = $conn->getQueryBuilder();
1250
	 *  $qb->select($qb->createFunction('COUNT(*)'))
1251
	 *     ->from('users', 'u')
1252
	 *  echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u
1253
	 * </code>
1254
	 * <code>
1255
	 *  $qb = $conn->getQueryBuilder();
1256
	 *  $qb->select($qb->createFunction('COUNT(`column`)'))
1257
	 *     ->from('users', 'u')
1258
	 *  echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u
1259
	 * </code>
1260
	 *
1261
	 * @param string $call
1262
	 *
1263
	 * @return IQueryFunction
1264
	 */
1265
	public function createFunction($call) {
1266
		return new QueryFunction($call);
1267
	}
1268
1269
	/**
1270
	 * Used to get the id of the last inserted element
1271
	 * @return int
1272
	 * @throws \BadMethodCallException When being called before an insert query has been run.
1273
	 */
1274
	public function getLastInsertId() {
1275
		if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable) {
1276
			// lastInsertId() needs the prefix but no quotes
1277
			$table = $this->prefixTableName($this->lastInsertedTable);
1278
			return (int) $this->connection->lastInsertId($table);
1279
		}
1280
1281
		throw new \BadMethodCallException('Invalid call to getLastInsertId without using insert() before.');
1282
	}
1283
1284
	/**
1285
	 * Returns the table name quoted and with database prefix as needed by the implementation
1286
	 *
1287
	 * @param string $table
1288
	 * @return string
1289
	 */
1290
	public function getTableName($table) {
1291
		if ($table instanceof IQueryFunction) {
0 ignored issues
show
introduced by
$table is never a sub-type of OCP\DB\QueryBuilder\IQueryFunction.
Loading history...
1292
			return (string) $table;
1293
		}
1294
1295
		$table = $this->prefixTableName($table);
1296
		return $this->helper->quoteColumnName($table);
1297
	}
1298
1299
	/**
1300
	 * Returns the table name with database prefix as needed by the implementation
1301
	 *
1302
	 * @param string $table
1303
	 * @return string
1304
	 */
1305
	protected function prefixTableName($table) {
1306
		if ($this->automaticTablePrefix === false || strpos($table, '*PREFIX*') === 0) {
1307
			return $table;
1308
		}
1309
1310
		return '*PREFIX*' . $table;
1311
	}
1312
1313
	/**
1314
	 * Returns the column name quoted and with table alias prefix as needed by the implementation
1315
	 *
1316
	 * @param string $column
1317
	 * @param string $tableAlias
1318
	 * @return string
1319
	 */
1320
	public function getColumnName($column, $tableAlias = '') {
1321
		if ($tableAlias !== '') {
1322
			$tableAlias .= '.';
1323
		}
1324
1325
		return $this->helper->quoteColumnName($tableAlias . $column);
1326
	}
1327
1328
	/**
1329
	 * Returns the column name quoted and with table alias prefix as needed by the implementation
1330
	 *
1331
	 * @param string $alias
1332
	 * @return string
1333
	 */
1334
	public function quoteAlias($alias) {
1335
		if ($alias === '' || $alias === null) {
1336
			return $alias;
1337
		}
1338
1339
		return $this->helper->quoteColumnName($alias);
1340
	}
1341
}
1342