Passed
Push — master ( 4d82a9...1089ad )
by Morris
15:08 queued 12s
created

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