Passed
Push — master ( c81798...ecad09 )
by Joas
27:22 queued 13:12
created
lib/private/DB/QueryBuilder/QueryBuilder.php 1 patch
Indentation   +1300 added lines, -1300 removed lines patch added patch discarded remove patch
@@ -56,1304 +56,1304 @@
 block discarded – undo
56 56
 use Psr\Log\LoggerInterface;
57 57
 
58 58
 class QueryBuilder implements IQueryBuilder {
59
-	/** @var ConnectionAdapter */
60
-	private $connection;
61
-
62
-	/** @var SystemConfig */
63
-	private $systemConfig;
64
-
65
-	private LoggerInterface $logger;
66
-
67
-	/** @var \Doctrine\DBAL\Query\QueryBuilder */
68
-	private $queryBuilder;
69
-
70
-	/** @var QuoteHelper */
71
-	private $helper;
72
-
73
-	/** @var bool */
74
-	private $automaticTablePrefix = true;
75
-
76
-	/** @var string */
77
-	protected $lastInsertedTable;
78
-
79
-	/**
80
-	 * Initializes a new QueryBuilder.
81
-	 *
82
-	 * @param ConnectionAdapter $connection
83
-	 * @param SystemConfig $systemConfig
84
-	 */
85
-	public function __construct(ConnectionAdapter $connection, SystemConfig $systemConfig, LoggerInterface $logger) {
86
-		$this->connection = $connection;
87
-		$this->systemConfig = $systemConfig;
88
-		$this->logger = $logger;
89
-		$this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection->getInner());
90
-		$this->helper = new QuoteHelper();
91
-	}
92
-
93
-	/**
94
-	 * Enable/disable automatic prefixing of table names with the oc_ prefix
95
-	 *
96
-	 * @param bool $enabled If set to true table names will be prefixed with the
97
-	 * owncloud database prefix automatically.
98
-	 * @since 8.2.0
99
-	 */
100
-	public function automaticTablePrefix($enabled) {
101
-		$this->automaticTablePrefix = (bool) $enabled;
102
-	}
103
-
104
-	/**
105
-	 * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
106
-	 * This producer method is intended for convenient inline usage. Example:
107
-	 *
108
-	 * <code>
109
-	 *     $qb = $conn->getQueryBuilder()
110
-	 *         ->select('u')
111
-	 *         ->from('users', 'u')
112
-	 *         ->where($qb->expr()->eq('u.id', 1));
113
-	 * </code>
114
-	 *
115
-	 * For more complex expression construction, consider storing the expression
116
-	 * builder object in a local variable.
117
-	 *
118
-	 * @return \OCP\DB\QueryBuilder\IExpressionBuilder
119
-	 */
120
-	public function expr() {
121
-		if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
122
-			return new OCIExpressionBuilder($this->connection, $this);
123
-		}
124
-		if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) {
125
-			return new PgSqlExpressionBuilder($this->connection, $this);
126
-		}
127
-		if ($this->connection->getDatabasePlatform() instanceof MySQLPlatform) {
128
-			return new MySqlExpressionBuilder($this->connection, $this);
129
-		}
130
-		if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
131
-			return new SqliteExpressionBuilder($this->connection, $this);
132
-		}
133
-
134
-		return new ExpressionBuilder($this->connection, $this);
135
-	}
136
-
137
-	/**
138
-	 * Gets an FunctionBuilder used for object-oriented construction of query functions.
139
-	 * This producer method is intended for convenient inline usage. Example:
140
-	 *
141
-	 * <code>
142
-	 *     $qb = $conn->getQueryBuilder()
143
-	 *         ->select('u')
144
-	 *         ->from('users', 'u')
145
-	 *         ->where($qb->fun()->md5('u.id'));
146
-	 * </code>
147
-	 *
148
-	 * For more complex function construction, consider storing the function
149
-	 * builder object in a local variable.
150
-	 *
151
-	 * @return \OCP\DB\QueryBuilder\IFunctionBuilder
152
-	 */
153
-	public function func() {
154
-		if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
155
-			return new OCIFunctionBuilder($this->connection, $this, $this->helper);
156
-		}
157
-		if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
158
-			return new SqliteFunctionBuilder($this->connection, $this, $this->helper);
159
-		}
160
-		if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) {
161
-			return new PgSqlFunctionBuilder($this->connection, $this, $this->helper);
162
-		}
163
-
164
-		return new FunctionBuilder($this->connection, $this, $this->helper);
165
-	}
166
-
167
-	/**
168
-	 * Gets the type of the currently built query.
169
-	 *
170
-	 * @return integer
171
-	 */
172
-	public function getType() {
173
-		return $this->queryBuilder->getType();
174
-	}
175
-
176
-	/**
177
-	 * Gets the associated DBAL Connection for this query builder.
178
-	 *
179
-	 * @return \OCP\IDBConnection
180
-	 */
181
-	public function getConnection() {
182
-		return $this->connection;
183
-	}
184
-
185
-	/**
186
-	 * Gets the state of this query builder instance.
187
-	 *
188
-	 * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
189
-	 */
190
-	public function getState() {
191
-		return $this->queryBuilder->getState();
192
-	}
193
-
194
-	/**
195
-	 * Executes this query using the bound parameters and their types.
196
-	 *
197
-	 * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
198
-	 * for insert, update and delete statements.
199
-	 *
200
-	 * @return IResult|int
201
-	 */
202
-	public function execute() {
203
-		if ($this->systemConfig->getValue('log_query', false)) {
204
-			try {
205
-				$params = [];
206
-				foreach ($this->getParameters() as $placeholder => $value) {
207
-					if ($value instanceof \DateTime) {
208
-						$params[] = $placeholder . ' => DateTime:\'' . $value->format('c') . '\'';
209
-					} elseif (is_array($value)) {
210
-						$params[] = $placeholder . ' => (\'' . implode('\', \'', $value) . '\')';
211
-					} else {
212
-						$params[] = $placeholder . ' => \'' . $value . '\'';
213
-					}
214
-				}
215
-				if (empty($params)) {
216
-					$this->logger->debug('DB QueryBuilder: \'{query}\'', [
217
-						'query' => $this->getSQL(),
218
-						'app' => 'core',
219
-					]);
220
-				} else {
221
-					$this->logger->debug('DB QueryBuilder: \'{query}\' with parameters: {params}', [
222
-						'query' => $this->getSQL(),
223
-						'params' => implode(', ', $params),
224
-						'app' => 'core',
225
-					]);
226
-				}
227
-			} catch (\Error $e) {
228
-				// likely an error during conversion of $value to string
229
-				$this->logger->error('DB QueryBuilder: error trying to log SQL query', ['exception' => $e]);
230
-			}
231
-		}
232
-
233
-		if (!empty($this->getQueryPart('select'))) {
234
-			$select = $this->getQueryPart('select');
235
-			$hasSelectAll = array_filter($select, static function ($s) {
236
-				return $s === '*';
237
-			});
238
-			$hasSelectSpecific = array_filter($select, static function ($s) {
239
-				return $s !== '*';
240
-			});
241
-
242
-			if (empty($hasSelectAll) === empty($hasSelectSpecific)) {
243
-				$exception = new QueryException('Query is selecting * and specific values in the same query. This is not supported in Oracle.');
244
-				$this->logger->error($exception->getMessage(), [
245
-					'query' => $this->getSQL(),
246
-					'app' => 'core',
247
-					'exception' => $exception,
248
-				]);
249
-			}
250
-		}
251
-
252
-		$numberOfParameters = 0;
253
-		$hasTooLargeArrayParameter = false;
254
-		foreach ($this->getParameters() as $parameter) {
255
-			if (is_array($parameter)) {
256
-				$count = count($parameter);
257
-				$numberOfParameters += $count;
258
-				$hasTooLargeArrayParameter = $hasTooLargeArrayParameter || ($count > 1000);
259
-			}
260
-		}
261
-
262
-		if ($hasTooLargeArrayParameter) {
263
-			$exception = new QueryException('More than 1000 expressions in a list are not allowed on Oracle.');
264
-			$this->logger->error($exception->getMessage(), [
265
-				'query' => $this->getSQL(),
266
-				'app' => 'core',
267
-				'exception' => $exception,
268
-			]);
269
-		}
270
-
271
-		if ($numberOfParameters > 65535) {
272
-			$exception = new QueryException('The number of parameters must not exceed 65535. Restriction by PostgreSQL.');
273
-			$this->logger->error($exception->getMessage(), [
274
-				'query' => $this->getSQL(),
275
-				'app' => 'core',
276
-				'exception' => $exception,
277
-			]);
278
-		}
279
-
280
-		$result = $this->queryBuilder->execute();
281
-		if (is_int($result)) {
282
-			return $result;
283
-		}
284
-		return new ResultAdapter($result);
285
-	}
286
-
287
-	public function executeQuery(): IResult {
288
-		if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder::SELECT) {
289
-			throw new \RuntimeException('Invalid query type, expected SELECT query');
290
-		}
291
-
292
-		try {
293
-			$result = $this->execute();
294
-		} catch (\Doctrine\DBAL\Exception $e) {
295
-			throw \OC\DB\Exceptions\DbalException::wrap($e);
296
-		}
297
-
298
-		if ($result instanceof IResult) {
299
-			return $result;
300
-		}
301
-
302
-		throw new \RuntimeException('Invalid return type for query');
303
-	}
304
-
305
-	/**
306
-	 * Monkey-patched compatibility layer for apps that were adapted for Nextcloud 22 before
307
-	 * the first beta, where executeStatement was named executeUpdate.
308
-	 *
309
-	 * Static analysis should catch those misuses, but until then let's try to keep things
310
-	 * running.
311
-	 *
312
-	 * @internal
313
-	 * @deprecated
314
-	 * @todo drop ASAP
315
-	 */
316
-	public function executeUpdate(): int {
317
-		return $this->executeStatement();
318
-	}
319
-
320
-	public function executeStatement(): int {
321
-		if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::SELECT) {
322
-			throw new \RuntimeException('Invalid query type, expected INSERT, DELETE or UPDATE statement');
323
-		}
324
-
325
-		try {
326
-			$result = $this->execute();
327
-		} catch (\Doctrine\DBAL\Exception $e) {
328
-			throw \OC\DB\Exceptions\DbalException::wrap($e);
329
-		}
330
-
331
-		if (!is_int($result)) {
332
-			throw new \RuntimeException('Invalid return type for statement');
333
-		}
334
-
335
-		return $result;
336
-	}
337
-
338
-
339
-	/**
340
-	 * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
341
-	 *
342
-	 * <code>
343
-	 *     $qb = $conn->getQueryBuilder()
344
-	 *         ->select('u')
345
-	 *         ->from('User', 'u')
346
-	 *     echo $qb->getSQL(); // SELECT u FROM User u
347
-	 * </code>
348
-	 *
349
-	 * @return string The SQL query string.
350
-	 */
351
-	public function getSQL() {
352
-		return $this->queryBuilder->getSQL();
353
-	}
354
-
355
-	/**
356
-	 * Sets a query parameter for the query being constructed.
357
-	 *
358
-	 * <code>
359
-	 *     $qb = $conn->getQueryBuilder()
360
-	 *         ->select('u')
361
-	 *         ->from('users', 'u')
362
-	 *         ->where('u.id = :user_id')
363
-	 *         ->setParameter(':user_id', 1);
364
-	 * </code>
365
-	 *
366
-	 * @param string|integer $key The parameter position or name.
367
-	 * @param mixed $value The parameter value.
368
-	 * @param string|null|int $type One of the IQueryBuilder::PARAM_* constants.
369
-	 *
370
-	 * @return $this This QueryBuilder instance.
371
-	 */
372
-	public function setParameter($key, $value, $type = null) {
373
-		$this->queryBuilder->setParameter($key, $value, $type);
374
-
375
-		return $this;
376
-	}
377
-
378
-	/**
379
-	 * Sets a collection of query parameters for the query being constructed.
380
-	 *
381
-	 * <code>
382
-	 *     $qb = $conn->getQueryBuilder()
383
-	 *         ->select('u')
384
-	 *         ->from('users', 'u')
385
-	 *         ->where('u.id = :user_id1 OR u.id = :user_id2')
386
-	 *         ->setParameters(array(
387
-	 *             ':user_id1' => 1,
388
-	 *             ':user_id2' => 2
389
-	 *         ));
390
-	 * </code>
391
-	 *
392
-	 * @param array $params The query parameters to set.
393
-	 * @param array $types The query parameters types to set.
394
-	 *
395
-	 * @return $this This QueryBuilder instance.
396
-	 */
397
-	public function setParameters(array $params, array $types = []) {
398
-		$this->queryBuilder->setParameters($params, $types);
399
-
400
-		return $this;
401
-	}
402
-
403
-	/**
404
-	 * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
405
-	 *
406
-	 * @return array The currently defined query parameters indexed by parameter index or name.
407
-	 */
408
-	public function getParameters() {
409
-		return $this->queryBuilder->getParameters();
410
-	}
411
-
412
-	/**
413
-	 * Gets a (previously set) query parameter of the query being constructed.
414
-	 *
415
-	 * @param mixed $key The key (index or name) of the bound parameter.
416
-	 *
417
-	 * @return mixed The value of the bound parameter.
418
-	 */
419
-	public function getParameter($key) {
420
-		return $this->queryBuilder->getParameter($key);
421
-	}
422
-
423
-	/**
424
-	 * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
425
-	 *
426
-	 * @return array The currently defined query parameter types indexed by parameter index or name.
427
-	 */
428
-	public function getParameterTypes() {
429
-		return $this->queryBuilder->getParameterTypes();
430
-	}
431
-
432
-	/**
433
-	 * Gets a (previously set) query parameter type of the query being constructed.
434
-	 *
435
-	 * @param mixed $key The key (index or name) of the bound parameter type.
436
-	 *
437
-	 * @return mixed The value of the bound parameter type.
438
-	 */
439
-	public function getParameterType($key) {
440
-		return $this->queryBuilder->getParameterType($key);
441
-	}
442
-
443
-	/**
444
-	 * Sets the position of the first result to retrieve (the "offset").
445
-	 *
446
-	 * @param int $firstResult The first result to return.
447
-	 *
448
-	 * @return $this This QueryBuilder instance.
449
-	 */
450
-	public function setFirstResult($firstResult) {
451
-		$this->queryBuilder->setFirstResult((int) $firstResult);
452
-
453
-		return $this;
454
-	}
455
-
456
-	/**
457
-	 * Gets the position of the first result the query object was set to retrieve (the "offset").
458
-	 * Returns 0 if {@link setFirstResult} was not applied to this QueryBuilder.
459
-	 *
460
-	 * @return int The position of the first result.
461
-	 */
462
-	public function getFirstResult() {
463
-		return $this->queryBuilder->getFirstResult();
464
-	}
465
-
466
-	/**
467
-	 * Sets the maximum number of results to retrieve (the "limit").
468
-	 *
469
-	 * NOTE: Setting max results to "0" will cause mixed behaviour. While most
470
-	 * of the databases will just return an empty result set, Oracle will return
471
-	 * all entries.
472
-	 *
473
-	 * @param int|null $maxResults The maximum number of results to retrieve.
474
-	 *
475
-	 * @return $this This QueryBuilder instance.
476
-	 */
477
-	public function setMaxResults($maxResults) {
478
-		if ($maxResults === null) {
479
-			$this->queryBuilder->setMaxResults($maxResults);
480
-		} else {
481
-			$this->queryBuilder->setMaxResults((int) $maxResults);
482
-		}
483
-
484
-		return $this;
485
-	}
486
-
487
-	/**
488
-	 * Gets the maximum number of results the query object was set to retrieve (the "limit").
489
-	 * Returns NULL if {@link setMaxResults} was not applied to this query builder.
490
-	 *
491
-	 * @return int|null The maximum number of results.
492
-	 */
493
-	public function getMaxResults() {
494
-		return $this->queryBuilder->getMaxResults();
495
-	}
496
-
497
-	/**
498
-	 * Specifies an item that is to be returned in the query result.
499
-	 * Replaces any previously specified selections, if any.
500
-	 *
501
-	 * <code>
502
-	 *     $qb = $conn->getQueryBuilder()
503
-	 *         ->select('u.id', 'p.id')
504
-	 *         ->from('users', 'u')
505
-	 *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
506
-	 * </code>
507
-	 *
508
-	 * @param mixed ...$selects The selection expressions.
509
-	 *
510
-	 * '@return $this This QueryBuilder instance.
511
-	 */
512
-	public function select(...$selects) {
513
-		if (count($selects) === 1 && is_array($selects[0])) {
514
-			$selects = $selects[0];
515
-		}
516
-
517
-		$this->queryBuilder->select(
518
-			$this->helper->quoteColumnNames($selects)
519
-		);
520
-
521
-		return $this;
522
-	}
523
-
524
-	/**
525
-	 * Specifies an item that is to be returned with a different name in the query result.
526
-	 *
527
-	 * <code>
528
-	 *     $qb = $conn->getQueryBuilder()
529
-	 *         ->selectAlias('u.id', 'user_id')
530
-	 *         ->from('users', 'u')
531
-	 *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
532
-	 * </code>
533
-	 *
534
-	 * @param mixed $select The selection expressions.
535
-	 * @param string $alias The column alias used in the constructed query.
536
-	 *
537
-	 * @return $this This QueryBuilder instance.
538
-	 */
539
-	public function selectAlias($select, $alias) {
540
-		$this->queryBuilder->addSelect(
541
-			$this->helper->quoteColumnName($select) . ' AS ' . $this->helper->quoteColumnName($alias)
542
-		);
543
-
544
-		return $this;
545
-	}
546
-
547
-	/**
548
-	 * Specifies an item that is to be returned uniquely in the query result.
549
-	 *
550
-	 * <code>
551
-	 *     $qb = $conn->getQueryBuilder()
552
-	 *         ->selectDistinct('type')
553
-	 *         ->from('users');
554
-	 * </code>
555
-	 *
556
-	 * @param mixed $select The selection expressions.
557
-	 *
558
-	 * @return $this This QueryBuilder instance.
559
-	 */
560
-	public function selectDistinct($select) {
561
-		if (!is_array($select)) {
562
-			$select = [$select];
563
-		}
564
-
565
-		$quotedSelect = $this->helper->quoteColumnNames($select);
566
-
567
-		$this->queryBuilder->addSelect(
568
-			'DISTINCT ' . implode(', ', $quotedSelect)
569
-		);
570
-
571
-		return $this;
572
-	}
573
-
574
-	/**
575
-	 * Adds an item that is to be returned in the query result.
576
-	 *
577
-	 * <code>
578
-	 *     $qb = $conn->getQueryBuilder()
579
-	 *         ->select('u.id')
580
-	 *         ->addSelect('p.id')
581
-	 *         ->from('users', 'u')
582
-	 *         ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
583
-	 * </code>
584
-	 *
585
-	 * @param mixed ...$selects The selection expression.
586
-	 *
587
-	 * @return $this This QueryBuilder instance.
588
-	 */
589
-	public function addSelect(...$selects) {
590
-		if (count($selects) === 1 && is_array($selects[0])) {
591
-			$selects = $selects[0];
592
-		}
593
-
594
-		$this->queryBuilder->addSelect(
595
-			$this->helper->quoteColumnNames($selects)
596
-		);
597
-
598
-		return $this;
599
-	}
600
-
601
-	/**
602
-	 * Turns the query being built into a bulk delete query that ranges over
603
-	 * a certain table.
604
-	 *
605
-	 * <code>
606
-	 *     $qb = $conn->getQueryBuilder()
607
-	 *         ->delete('users', 'u')
608
-	 *         ->where('u.id = :user_id');
609
-	 *         ->setParameter(':user_id', 1);
610
-	 * </code>
611
-	 *
612
-	 * @param string $delete The table whose rows are subject to the deletion.
613
-	 * @param string $alias The table alias used in the constructed query.
614
-	 *
615
-	 * @return $this This QueryBuilder instance.
616
-	 */
617
-	public function delete($delete = null, $alias = null) {
618
-		$this->queryBuilder->delete(
619
-			$this->getTableName($delete),
620
-			$alias
621
-		);
622
-
623
-		return $this;
624
-	}
625
-
626
-	/**
627
-	 * Turns the query being built into a bulk update query that ranges over
628
-	 * a certain table
629
-	 *
630
-	 * <code>
631
-	 *     $qb = $conn->getQueryBuilder()
632
-	 *         ->update('users', 'u')
633
-	 *         ->set('u.password', md5('password'))
634
-	 *         ->where('u.id = ?');
635
-	 * </code>
636
-	 *
637
-	 * @param string $update The table whose rows are subject to the update.
638
-	 * @param string $alias The table alias used in the constructed query.
639
-	 *
640
-	 * @return $this This QueryBuilder instance.
641
-	 */
642
-	public function update($update = null, $alias = null) {
643
-		$this->queryBuilder->update(
644
-			$this->getTableName($update),
645
-			$alias
646
-		);
647
-
648
-		return $this;
649
-	}
650
-
651
-	/**
652
-	 * Turns the query being built into an insert query that inserts into
653
-	 * a certain table
654
-	 *
655
-	 * <code>
656
-	 *     $qb = $conn->getQueryBuilder()
657
-	 *         ->insert('users')
658
-	 *         ->values(
659
-	 *             array(
660
-	 *                 'name' => '?',
661
-	 *                 'password' => '?'
662
-	 *             )
663
-	 *         );
664
-	 * </code>
665
-	 *
666
-	 * @param string $insert The table into which the rows should be inserted.
667
-	 *
668
-	 * @return $this This QueryBuilder instance.
669
-	 */
670
-	public function insert($insert = null) {
671
-		$this->queryBuilder->insert(
672
-			$this->getTableName($insert)
673
-		);
674
-
675
-		$this->lastInsertedTable = $insert;
676
-
677
-		return $this;
678
-	}
679
-
680
-	/**
681
-	 * Creates and adds a query root corresponding to the table identified by the
682
-	 * given alias, forming a cartesian product with any existing query roots.
683
-	 *
684
-	 * <code>
685
-	 *     $qb = $conn->getQueryBuilder()
686
-	 *         ->select('u.id')
687
-	 *         ->from('users', 'u')
688
-	 * </code>
689
-	 *
690
-	 * @param string|IQueryFunction $from The table.
691
-	 * @param string|null $alias The alias of the table.
692
-	 *
693
-	 * @return $this This QueryBuilder instance.
694
-	 */
695
-	public function from($from, $alias = null) {
696
-		$this->queryBuilder->from(
697
-			$this->getTableName($from),
698
-			$this->quoteAlias($alias)
699
-		);
700
-
701
-		return $this;
702
-	}
703
-
704
-	/**
705
-	 * Creates and adds a join to the query.
706
-	 *
707
-	 * <code>
708
-	 *     $qb = $conn->getQueryBuilder()
709
-	 *         ->select('u.name')
710
-	 *         ->from('users', 'u')
711
-	 *         ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
712
-	 * </code>
713
-	 *
714
-	 * @param string $fromAlias The alias that points to a from clause.
715
-	 * @param string $join The table name to join.
716
-	 * @param string $alias The alias of the join table.
717
-	 * @param string|ICompositeExpression|null $condition The condition for the join.
718
-	 *
719
-	 * @return $this This QueryBuilder instance.
720
-	 */
721
-	public function join($fromAlias, $join, $alias, $condition = null) {
722
-		$this->queryBuilder->join(
723
-			$this->quoteAlias($fromAlias),
724
-			$this->getTableName($join),
725
-			$this->quoteAlias($alias),
726
-			$condition
727
-		);
728
-
729
-		return $this;
730
-	}
731
-
732
-	/**
733
-	 * Creates and adds a join to the query.
734
-	 *
735
-	 * <code>
736
-	 *     $qb = $conn->getQueryBuilder()
737
-	 *         ->select('u.name')
738
-	 *         ->from('users', 'u')
739
-	 *         ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
740
-	 * </code>
741
-	 *
742
-	 * @param string $fromAlias The alias that points to a from clause.
743
-	 * @param string $join The table name to join.
744
-	 * @param string $alias The alias of the join table.
745
-	 * @param string|ICompositeExpression|null $condition The condition for the join.
746
-	 *
747
-	 * @return $this This QueryBuilder instance.
748
-	 */
749
-	public function innerJoin($fromAlias, $join, $alias, $condition = null) {
750
-		$this->queryBuilder->innerJoin(
751
-			$this->quoteAlias($fromAlias),
752
-			$this->getTableName($join),
753
-			$this->quoteAlias($alias),
754
-			$condition
755
-		);
756
-
757
-		return $this;
758
-	}
759
-
760
-	/**
761
-	 * Creates and adds a left join to the query.
762
-	 *
763
-	 * <code>
764
-	 *     $qb = $conn->getQueryBuilder()
765
-	 *         ->select('u.name')
766
-	 *         ->from('users', 'u')
767
-	 *         ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
768
-	 * </code>
769
-	 *
770
-	 * @param string $fromAlias The alias that points to a from clause.
771
-	 * @param string $join The table name to join.
772
-	 * @param string $alias The alias of the join table.
773
-	 * @param string|ICompositeExpression|null $condition The condition for the join.
774
-	 *
775
-	 * @return $this This QueryBuilder instance.
776
-	 */
777
-	public function leftJoin($fromAlias, $join, $alias, $condition = null) {
778
-		$this->queryBuilder->leftJoin(
779
-			$this->quoteAlias($fromAlias),
780
-			$this->getTableName($join),
781
-			$this->quoteAlias($alias),
782
-			$condition
783
-		);
784
-
785
-		return $this;
786
-	}
787
-
788
-	/**
789
-	 * Creates and adds a right join to the query.
790
-	 *
791
-	 * <code>
792
-	 *     $qb = $conn->getQueryBuilder()
793
-	 *         ->select('u.name')
794
-	 *         ->from('users', 'u')
795
-	 *         ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
796
-	 * </code>
797
-	 *
798
-	 * @param string $fromAlias The alias that points to a from clause.
799
-	 * @param string $join The table name to join.
800
-	 * @param string $alias The alias of the join table.
801
-	 * @param string|ICompositeExpression|null $condition The condition for the join.
802
-	 *
803
-	 * @return $this This QueryBuilder instance.
804
-	 */
805
-	public function rightJoin($fromAlias, $join, $alias, $condition = null) {
806
-		$this->queryBuilder->rightJoin(
807
-			$this->quoteAlias($fromAlias),
808
-			$this->getTableName($join),
809
-			$this->quoteAlias($alias),
810
-			$condition
811
-		);
812
-
813
-		return $this;
814
-	}
815
-
816
-	/**
817
-	 * Sets a new value for a column in a bulk update query.
818
-	 *
819
-	 * <code>
820
-	 *     $qb = $conn->getQueryBuilder()
821
-	 *         ->update('users', 'u')
822
-	 *         ->set('u.password', md5('password'))
823
-	 *         ->where('u.id = ?');
824
-	 * </code>
825
-	 *
826
-	 * @param string $key The column to set.
827
-	 * @param ILiteral|IParameter|IQueryFunction|string $value The value, expression, placeholder, etc.
828
-	 *
829
-	 * @return $this This QueryBuilder instance.
830
-	 */
831
-	public function set($key, $value) {
832
-		$this->queryBuilder->set(
833
-			$this->helper->quoteColumnName($key),
834
-			$this->helper->quoteColumnName($value)
835
-		);
836
-
837
-		return $this;
838
-	}
839
-
840
-	/**
841
-	 * Specifies one or more restrictions to the query result.
842
-	 * Replaces any previously specified restrictions, if any.
843
-	 *
844
-	 * <code>
845
-	 *     $qb = $conn->getQueryBuilder()
846
-	 *         ->select('u.name')
847
-	 *         ->from('users', 'u')
848
-	 *         ->where('u.id = ?');
849
-	 *
850
-	 *     // You can optionally programmatically build and/or expressions
851
-	 *     $qb = $conn->getQueryBuilder();
852
-	 *
853
-	 *     $or = $qb->expr()->orx();
854
-	 *     $or->add($qb->expr()->eq('u.id', 1));
855
-	 *     $or->add($qb->expr()->eq('u.id', 2));
856
-	 *
857
-	 *     $qb->update('users', 'u')
858
-	 *         ->set('u.password', md5('password'))
859
-	 *         ->where($or);
860
-	 * </code>
861
-	 *
862
-	 * @param mixed ...$predicates The restriction predicates.
863
-	 *
864
-	 * @return $this This QueryBuilder instance.
865
-	 */
866
-	public function where(...$predicates) {
867
-		if ($this->getQueryPart('where') !== null && $this->systemConfig->getValue('debug', false)) {
868
-			// Only logging a warning, not throwing for now.
869
-			$e = new QueryException('Using where() on non-empty WHERE part, please verify it is intentional to not call whereAnd() or whereOr() instead. Otherwise consider creating a new query builder object or call resetQueryPart(\'where\') first.');
870
-			$this->logger->warning($e->getMessage(), ['exception' => $e]);
871
-		}
872
-
873
-		call_user_func_array(
874
-			[$this->queryBuilder, 'where'],
875
-			$predicates
876
-		);
877
-
878
-		return $this;
879
-	}
880
-
881
-	/**
882
-	 * Adds one or more restrictions to the query results, forming a logical
883
-	 * conjunction with any previously specified restrictions.
884
-	 *
885
-	 * <code>
886
-	 *     $qb = $conn->getQueryBuilder()
887
-	 *         ->select('u')
888
-	 *         ->from('users', 'u')
889
-	 *         ->where('u.username LIKE ?')
890
-	 *         ->andWhere('u.is_active = 1');
891
-	 * </code>
892
-	 *
893
-	 * @param mixed ...$where The query restrictions.
894
-	 *
895
-	 * @return $this This QueryBuilder instance.
896
-	 *
897
-	 * @see where()
898
-	 */
899
-	public function andWhere(...$where) {
900
-		call_user_func_array(
901
-			[$this->queryBuilder, 'andWhere'],
902
-			$where
903
-		);
904
-
905
-		return $this;
906
-	}
907
-
908
-	/**
909
-	 * Adds one or more restrictions to the query results, forming a logical
910
-	 * disjunction with any previously specified restrictions.
911
-	 *
912
-	 * <code>
913
-	 *     $qb = $conn->getQueryBuilder()
914
-	 *         ->select('u.name')
915
-	 *         ->from('users', 'u')
916
-	 *         ->where('u.id = 1')
917
-	 *         ->orWhere('u.id = 2');
918
-	 * </code>
919
-	 *
920
-	 * @param mixed ...$where The WHERE statement.
921
-	 *
922
-	 * @return $this This QueryBuilder instance.
923
-	 *
924
-	 * @see where()
925
-	 */
926
-	public function orWhere(...$where) {
927
-		call_user_func_array(
928
-			[$this->queryBuilder, 'orWhere'],
929
-			$where
930
-		);
931
-
932
-		return $this;
933
-	}
934
-
935
-	/**
936
-	 * Specifies a grouping over the results of the query.
937
-	 * Replaces any previously specified groupings, if any.
938
-	 *
939
-	 * <code>
940
-	 *     $qb = $conn->getQueryBuilder()
941
-	 *         ->select('u.name')
942
-	 *         ->from('users', 'u')
943
-	 *         ->groupBy('u.id');
944
-	 * </code>
945
-	 *
946
-	 * @param mixed ...$groupBys The grouping expression.
947
-	 *
948
-	 * @return $this This QueryBuilder instance.
949
-	 */
950
-	public function groupBy(...$groupBys) {
951
-		if (count($groupBys) === 1 && is_array($groupBys[0])) {
952
-			$groupBys = $groupBys[0];
953
-		}
954
-
955
-		call_user_func_array(
956
-			[$this->queryBuilder, 'groupBy'],
957
-			$this->helper->quoteColumnNames($groupBys)
958
-		);
959
-
960
-		return $this;
961
-	}
962
-
963
-	/**
964
-	 * Adds a grouping expression to the query.
965
-	 *
966
-	 * <code>
967
-	 *     $qb = $conn->getQueryBuilder()
968
-	 *         ->select('u.name')
969
-	 *         ->from('users', 'u')
970
-	 *         ->groupBy('u.lastLogin');
971
-	 *         ->addGroupBy('u.createdAt')
972
-	 * </code>
973
-	 *
974
-	 * @param mixed ...$groupBy The grouping expression.
975
-	 *
976
-	 * @return $this This QueryBuilder instance.
977
-	 */
978
-	public function addGroupBy(...$groupBys) {
979
-		if (count($groupBys) === 1 && is_array($groupBys[0])) {
980
-			$$groupBys = $groupBys[0];
981
-		}
982
-
983
-		call_user_func_array(
984
-			[$this->queryBuilder, 'addGroupBy'],
985
-			$this->helper->quoteColumnNames($groupBys)
986
-		);
987
-
988
-		return $this;
989
-	}
990
-
991
-	/**
992
-	 * Sets a value for a column in an insert query.
993
-	 *
994
-	 * <code>
995
-	 *     $qb = $conn->getQueryBuilder()
996
-	 *         ->insert('users')
997
-	 *         ->values(
998
-	 *             array(
999
-	 *                 'name' => '?'
1000
-	 *             )
1001
-	 *         )
1002
-	 *         ->setValue('password', '?');
1003
-	 * </code>
1004
-	 *
1005
-	 * @param string $column The column into which the value should be inserted.
1006
-	 * @param IParameter|string $value The value that should be inserted into the column.
1007
-	 *
1008
-	 * @return $this This QueryBuilder instance.
1009
-	 */
1010
-	public function setValue($column, $value) {
1011
-		$this->queryBuilder->setValue(
1012
-			$this->helper->quoteColumnName($column),
1013
-			(string) $value
1014
-		);
1015
-
1016
-		return $this;
1017
-	}
1018
-
1019
-	/**
1020
-	 * Specifies values for an insert query indexed by column names.
1021
-	 * Replaces any previous values, if any.
1022
-	 *
1023
-	 * <code>
1024
-	 *     $qb = $conn->getQueryBuilder()
1025
-	 *         ->insert('users')
1026
-	 *         ->values(
1027
-	 *             array(
1028
-	 *                 'name' => '?',
1029
-	 *                 'password' => '?'
1030
-	 *             )
1031
-	 *         );
1032
-	 * </code>
1033
-	 *
1034
-	 * @param array $values The values to specify for the insert query indexed by column names.
1035
-	 *
1036
-	 * @return $this This QueryBuilder instance.
1037
-	 */
1038
-	public function values(array $values) {
1039
-		$quotedValues = [];
1040
-		foreach ($values as $key => $value) {
1041
-			$quotedValues[$this->helper->quoteColumnName($key)] = $value;
1042
-		}
1043
-
1044
-		$this->queryBuilder->values($quotedValues);
1045
-
1046
-		return $this;
1047
-	}
1048
-
1049
-	/**
1050
-	 * Specifies a restriction over the groups of the query.
1051
-	 * Replaces any previous having restrictions, if any.
1052
-	 *
1053
-	 * @param mixed ...$having The restriction over the groups.
1054
-	 *
1055
-	 * @return $this This QueryBuilder instance.
1056
-	 */
1057
-	public function having(...$having) {
1058
-		call_user_func_array(
1059
-			[$this->queryBuilder, 'having'],
1060
-			$having
1061
-		);
1062
-
1063
-		return $this;
1064
-	}
1065
-
1066
-	/**
1067
-	 * Adds a restriction over the groups of the query, forming a logical
1068
-	 * conjunction with any existing having restrictions.
1069
-	 *
1070
-	 * @param mixed ...$having The restriction to append.
1071
-	 *
1072
-	 * @return $this This QueryBuilder instance.
1073
-	 */
1074
-	public function andHaving(...$having) {
1075
-		call_user_func_array(
1076
-			[$this->queryBuilder, 'andHaving'],
1077
-			$having
1078
-		);
1079
-
1080
-		return $this;
1081
-	}
1082
-
1083
-	/**
1084
-	 * Adds a restriction over the groups of the query, forming a logical
1085
-	 * disjunction with any existing having restrictions.
1086
-	 *
1087
-	 * @param mixed ...$having The restriction to add.
1088
-	 *
1089
-	 * @return $this This QueryBuilder instance.
1090
-	 */
1091
-	public function orHaving(...$having) {
1092
-		call_user_func_array(
1093
-			[$this->queryBuilder, 'orHaving'],
1094
-			$having
1095
-		);
1096
-
1097
-		return $this;
1098
-	}
1099
-
1100
-	/**
1101
-	 * Specifies an ordering for the query results.
1102
-	 * Replaces any previously specified orderings, if any.
1103
-	 *
1104
-	 * @param string|IQueryFunction|ILiteral|IParameter $sort The ordering expression.
1105
-	 * @param string $order The ordering direction.
1106
-	 *
1107
-	 * @return $this This QueryBuilder instance.
1108
-	 */
1109
-	public function orderBy($sort, $order = null) {
1110
-		$this->queryBuilder->orderBy(
1111
-			$this->helper->quoteColumnName($sort),
1112
-			$order
1113
-		);
1114
-
1115
-		return $this;
1116
-	}
1117
-
1118
-	/**
1119
-	 * Adds an ordering to the query results.
1120
-	 *
1121
-	 * @param string|ILiteral|IParameter|IQueryFunction $sort The ordering expression.
1122
-	 * @param string $order The ordering direction.
1123
-	 *
1124
-	 * @return $this This QueryBuilder instance.
1125
-	 */
1126
-	public function addOrderBy($sort, $order = null) {
1127
-		$this->queryBuilder->addOrderBy(
1128
-			$this->helper->quoteColumnName($sort),
1129
-			$order
1130
-		);
1131
-
1132
-		return $this;
1133
-	}
1134
-
1135
-	/**
1136
-	 * Gets a query part by its name.
1137
-	 *
1138
-	 * @param string $queryPartName
1139
-	 *
1140
-	 * @return mixed
1141
-	 */
1142
-	public function getQueryPart($queryPartName) {
1143
-		return $this->queryBuilder->getQueryPart($queryPartName);
1144
-	}
1145
-
1146
-	/**
1147
-	 * Gets all query parts.
1148
-	 *
1149
-	 * @return array
1150
-	 */
1151
-	public function getQueryParts() {
1152
-		return $this->queryBuilder->getQueryParts();
1153
-	}
1154
-
1155
-	/**
1156
-	 * Resets SQL parts.
1157
-	 *
1158
-	 * @param array|null $queryPartNames
1159
-	 *
1160
-	 * @return $this This QueryBuilder instance.
1161
-	 */
1162
-	public function resetQueryParts($queryPartNames = null) {
1163
-		$this->queryBuilder->resetQueryParts($queryPartNames);
1164
-
1165
-		return $this;
1166
-	}
1167
-
1168
-	/**
1169
-	 * Resets a single SQL part.
1170
-	 *
1171
-	 * @param string $queryPartName
1172
-	 *
1173
-	 * @return $this This QueryBuilder instance.
1174
-	 */
1175
-	public function resetQueryPart($queryPartName) {
1176
-		$this->queryBuilder->resetQueryPart($queryPartName);
1177
-
1178
-		return $this;
1179
-	}
1180
-
1181
-	/**
1182
-	 * Creates a new named parameter and bind the value $value to it.
1183
-	 *
1184
-	 * This method provides a shortcut for PDOStatement::bindValue
1185
-	 * when using prepared statements.
1186
-	 *
1187
-	 * The parameter $value specifies the value that you want to bind. If
1188
-	 * $placeholder is not provided bindValue() will automatically create a
1189
-	 * placeholder for you. An automatic placeholder will be of the name
1190
-	 * ':dcValue1', ':dcValue2' etc.
1191
-	 *
1192
-	 * For more information see {@link https://www.php.net/pdostatement-bindparam}
1193
-	 *
1194
-	 * Example:
1195
-	 * <code>
1196
-	 * $value = 2;
1197
-	 * $q->eq( 'id', $q->bindValue( $value ) );
1198
-	 * $stmt = $q->executeQuery(); // executed with 'id = 2'
1199
-	 * </code>
1200
-	 *
1201
-	 * @license New BSD License
1202
-	 * @link http://www.zetacomponents.org
1203
-	 *
1204
-	 * @param mixed $value
1205
-	 * @param mixed $type
1206
-	 * @param string $placeHolder The name to bind with. The string must start with a colon ':'.
1207
-	 *
1208
-	 * @return IParameter the placeholder name used.
1209
-	 */
1210
-	public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $placeHolder = null) {
1211
-		return new Parameter($this->queryBuilder->createNamedParameter($value, $type, $placeHolder));
1212
-	}
1213
-
1214
-	/**
1215
-	 * Creates a new positional parameter and bind the given value to it.
1216
-	 *
1217
-	 * Attention: If you are using positional parameters with the query builder you have
1218
-	 * to be very careful to bind all parameters in the order they appear in the SQL
1219
-	 * statement , otherwise they get bound in the wrong order which can lead to serious
1220
-	 * bugs in your code.
1221
-	 *
1222
-	 * Example:
1223
-	 * <code>
1224
-	 *  $qb = $conn->getQueryBuilder();
1225
-	 *  $qb->select('u.*')
1226
-	 *     ->from('users', 'u')
1227
-	 *     ->where('u.username = ' . $qb->createPositionalParameter('Foo', IQueryBuilder::PARAM_STR))
1228
-	 *     ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', IQueryBuilder::PARAM_STR))
1229
-	 * </code>
1230
-	 *
1231
-	 * @param mixed $value
1232
-	 * @param integer $type
1233
-	 *
1234
-	 * @return IParameter
1235
-	 */
1236
-	public function createPositionalParameter($value, $type = IQueryBuilder::PARAM_STR) {
1237
-		return new Parameter($this->queryBuilder->createPositionalParameter($value, $type));
1238
-	}
1239
-
1240
-	/**
1241
-	 * Creates a new parameter
1242
-	 *
1243
-	 * Example:
1244
-	 * <code>
1245
-	 *  $qb = $conn->getQueryBuilder();
1246
-	 *  $qb->select('u.*')
1247
-	 *     ->from('users', 'u')
1248
-	 *     ->where('u.username = ' . $qb->createParameter('name'))
1249
-	 *     ->setParameter('name', 'Bar', IQueryBuilder::PARAM_STR))
1250
-	 * </code>
1251
-	 *
1252
-	 * @param string $name
1253
-	 *
1254
-	 * @return IParameter
1255
-	 */
1256
-	public function createParameter($name) {
1257
-		return new Parameter(':' . $name);
1258
-	}
1259
-
1260
-	/**
1261
-	 * Creates a new function
1262
-	 *
1263
-	 * Attention: Column names inside the call have to be quoted before hand
1264
-	 *
1265
-	 * Example:
1266
-	 * <code>
1267
-	 *  $qb = $conn->getQueryBuilder();
1268
-	 *  $qb->select($qb->createFunction('COUNT(*)'))
1269
-	 *     ->from('users', 'u')
1270
-	 *  echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u
1271
-	 * </code>
1272
-	 * <code>
1273
-	 *  $qb = $conn->getQueryBuilder();
1274
-	 *  $qb->select($qb->createFunction('COUNT(`column`)'))
1275
-	 *     ->from('users', 'u')
1276
-	 *  echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u
1277
-	 * </code>
1278
-	 *
1279
-	 * @param string $call
1280
-	 *
1281
-	 * @return IQueryFunction
1282
-	 */
1283
-	public function createFunction($call) {
1284
-		return new QueryFunction($call);
1285
-	}
1286
-
1287
-	/**
1288
-	 * Used to get the id of the last inserted element
1289
-	 * @return int
1290
-	 * @throws \BadMethodCallException When being called before an insert query has been run.
1291
-	 */
1292
-	public function getLastInsertId(): int {
1293
-		if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable) {
1294
-			// lastInsertId() needs the prefix but no quotes
1295
-			$table = $this->prefixTableName($this->lastInsertedTable);
1296
-			return $this->connection->lastInsertId($table);
1297
-		}
1298
-
1299
-		throw new \BadMethodCallException('Invalid call to getLastInsertId without using insert() before.');
1300
-	}
1301
-
1302
-	/**
1303
-	 * Returns the table name quoted and with database prefix as needed by the implementation
1304
-	 *
1305
-	 * @param string|IQueryFunction $table
1306
-	 * @return string
1307
-	 */
1308
-	public function getTableName($table) {
1309
-		if ($table instanceof IQueryFunction) {
1310
-			return (string) $table;
1311
-		}
1312
-
1313
-		$table = $this->prefixTableName($table);
1314
-		return $this->helper->quoteColumnName($table);
1315
-	}
1316
-
1317
-	/**
1318
-	 * Returns the table name with database prefix as needed by the implementation
1319
-	 *
1320
-	 * @param string $table
1321
-	 * @return string
1322
-	 */
1323
-	protected function prefixTableName($table) {
1324
-		if ($this->automaticTablePrefix === false || strpos($table, '*PREFIX*') === 0) {
1325
-			return $table;
1326
-		}
1327
-
1328
-		return '*PREFIX*' . $table;
1329
-	}
1330
-
1331
-	/**
1332
-	 * Returns the column name quoted and with table alias prefix as needed by the implementation
1333
-	 *
1334
-	 * @param string $column
1335
-	 * @param string $tableAlias
1336
-	 * @return string
1337
-	 */
1338
-	public function getColumnName($column, $tableAlias = '') {
1339
-		if ($tableAlias !== '') {
1340
-			$tableAlias .= '.';
1341
-		}
1342
-
1343
-		return $this->helper->quoteColumnName($tableAlias . $column);
1344
-	}
1345
-
1346
-	/**
1347
-	 * Returns the column name quoted and with table alias prefix as needed by the implementation
1348
-	 *
1349
-	 * @param string $alias
1350
-	 * @return string
1351
-	 */
1352
-	public function quoteAlias($alias) {
1353
-		if ($alias === '' || $alias === null) {
1354
-			return $alias;
1355
-		}
1356
-
1357
-		return $this->helper->quoteColumnName($alias);
1358
-	}
59
+    /** @var ConnectionAdapter */
60
+    private $connection;
61
+
62
+    /** @var SystemConfig */
63
+    private $systemConfig;
64
+
65
+    private LoggerInterface $logger;
66
+
67
+    /** @var \Doctrine\DBAL\Query\QueryBuilder */
68
+    private $queryBuilder;
69
+
70
+    /** @var QuoteHelper */
71
+    private $helper;
72
+
73
+    /** @var bool */
74
+    private $automaticTablePrefix = true;
75
+
76
+    /** @var string */
77
+    protected $lastInsertedTable;
78
+
79
+    /**
80
+     * Initializes a new QueryBuilder.
81
+     *
82
+     * @param ConnectionAdapter $connection
83
+     * @param SystemConfig $systemConfig
84
+     */
85
+    public function __construct(ConnectionAdapter $connection, SystemConfig $systemConfig, LoggerInterface $logger) {
86
+        $this->connection = $connection;
87
+        $this->systemConfig = $systemConfig;
88
+        $this->logger = $logger;
89
+        $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection->getInner());
90
+        $this->helper = new QuoteHelper();
91
+    }
92
+
93
+    /**
94
+     * Enable/disable automatic prefixing of table names with the oc_ prefix
95
+     *
96
+     * @param bool $enabled If set to true table names will be prefixed with the
97
+     * owncloud database prefix automatically.
98
+     * @since 8.2.0
99
+     */
100
+    public function automaticTablePrefix($enabled) {
101
+        $this->automaticTablePrefix = (bool) $enabled;
102
+    }
103
+
104
+    /**
105
+     * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
106
+     * This producer method is intended for convenient inline usage. Example:
107
+     *
108
+     * <code>
109
+     *     $qb = $conn->getQueryBuilder()
110
+     *         ->select('u')
111
+     *         ->from('users', 'u')
112
+     *         ->where($qb->expr()->eq('u.id', 1));
113
+     * </code>
114
+     *
115
+     * For more complex expression construction, consider storing the expression
116
+     * builder object in a local variable.
117
+     *
118
+     * @return \OCP\DB\QueryBuilder\IExpressionBuilder
119
+     */
120
+    public function expr() {
121
+        if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
122
+            return new OCIExpressionBuilder($this->connection, $this);
123
+        }
124
+        if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) {
125
+            return new PgSqlExpressionBuilder($this->connection, $this);
126
+        }
127
+        if ($this->connection->getDatabasePlatform() instanceof MySQLPlatform) {
128
+            return new MySqlExpressionBuilder($this->connection, $this);
129
+        }
130
+        if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
131
+            return new SqliteExpressionBuilder($this->connection, $this);
132
+        }
133
+
134
+        return new ExpressionBuilder($this->connection, $this);
135
+    }
136
+
137
+    /**
138
+     * Gets an FunctionBuilder used for object-oriented construction of query functions.
139
+     * This producer method is intended for convenient inline usage. Example:
140
+     *
141
+     * <code>
142
+     *     $qb = $conn->getQueryBuilder()
143
+     *         ->select('u')
144
+     *         ->from('users', 'u')
145
+     *         ->where($qb->fun()->md5('u.id'));
146
+     * </code>
147
+     *
148
+     * For more complex function construction, consider storing the function
149
+     * builder object in a local variable.
150
+     *
151
+     * @return \OCP\DB\QueryBuilder\IFunctionBuilder
152
+     */
153
+    public function func() {
154
+        if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
155
+            return new OCIFunctionBuilder($this->connection, $this, $this->helper);
156
+        }
157
+        if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
158
+            return new SqliteFunctionBuilder($this->connection, $this, $this->helper);
159
+        }
160
+        if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) {
161
+            return new PgSqlFunctionBuilder($this->connection, $this, $this->helper);
162
+        }
163
+
164
+        return new FunctionBuilder($this->connection, $this, $this->helper);
165
+    }
166
+
167
+    /**
168
+     * Gets the type of the currently built query.
169
+     *
170
+     * @return integer
171
+     */
172
+    public function getType() {
173
+        return $this->queryBuilder->getType();
174
+    }
175
+
176
+    /**
177
+     * Gets the associated DBAL Connection for this query builder.
178
+     *
179
+     * @return \OCP\IDBConnection
180
+     */
181
+    public function getConnection() {
182
+        return $this->connection;
183
+    }
184
+
185
+    /**
186
+     * Gets the state of this query builder instance.
187
+     *
188
+     * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
189
+     */
190
+    public function getState() {
191
+        return $this->queryBuilder->getState();
192
+    }
193
+
194
+    /**
195
+     * Executes this query using the bound parameters and their types.
196
+     *
197
+     * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
198
+     * for insert, update and delete statements.
199
+     *
200
+     * @return IResult|int
201
+     */
202
+    public function execute() {
203
+        if ($this->systemConfig->getValue('log_query', false)) {
204
+            try {
205
+                $params = [];
206
+                foreach ($this->getParameters() as $placeholder => $value) {
207
+                    if ($value instanceof \DateTime) {
208
+                        $params[] = $placeholder . ' => DateTime:\'' . $value->format('c') . '\'';
209
+                    } elseif (is_array($value)) {
210
+                        $params[] = $placeholder . ' => (\'' . implode('\', \'', $value) . '\')';
211
+                    } else {
212
+                        $params[] = $placeholder . ' => \'' . $value . '\'';
213
+                    }
214
+                }
215
+                if (empty($params)) {
216
+                    $this->logger->debug('DB QueryBuilder: \'{query}\'', [
217
+                        'query' => $this->getSQL(),
218
+                        'app' => 'core',
219
+                    ]);
220
+                } else {
221
+                    $this->logger->debug('DB QueryBuilder: \'{query}\' with parameters: {params}', [
222
+                        'query' => $this->getSQL(),
223
+                        'params' => implode(', ', $params),
224
+                        'app' => 'core',
225
+                    ]);
226
+                }
227
+            } catch (\Error $e) {
228
+                // likely an error during conversion of $value to string
229
+                $this->logger->error('DB QueryBuilder: error trying to log SQL query', ['exception' => $e]);
230
+            }
231
+        }
232
+
233
+        if (!empty($this->getQueryPart('select'))) {
234
+            $select = $this->getQueryPart('select');
235
+            $hasSelectAll = array_filter($select, static function ($s) {
236
+                return $s === '*';
237
+            });
238
+            $hasSelectSpecific = array_filter($select, static function ($s) {
239
+                return $s !== '*';
240
+            });
241
+
242
+            if (empty($hasSelectAll) === empty($hasSelectSpecific)) {
243
+                $exception = new QueryException('Query is selecting * and specific values in the same query. This is not supported in Oracle.');
244
+                $this->logger->error($exception->getMessage(), [
245
+                    'query' => $this->getSQL(),
246
+                    'app' => 'core',
247
+                    'exception' => $exception,
248
+                ]);
249
+            }
250
+        }
251
+
252
+        $numberOfParameters = 0;
253
+        $hasTooLargeArrayParameter = false;
254
+        foreach ($this->getParameters() as $parameter) {
255
+            if (is_array($parameter)) {
256
+                $count = count($parameter);
257
+                $numberOfParameters += $count;
258
+                $hasTooLargeArrayParameter = $hasTooLargeArrayParameter || ($count > 1000);
259
+            }
260
+        }
261
+
262
+        if ($hasTooLargeArrayParameter) {
263
+            $exception = new QueryException('More than 1000 expressions in a list are not allowed on Oracle.');
264
+            $this->logger->error($exception->getMessage(), [
265
+                'query' => $this->getSQL(),
266
+                'app' => 'core',
267
+                'exception' => $exception,
268
+            ]);
269
+        }
270
+
271
+        if ($numberOfParameters > 65535) {
272
+            $exception = new QueryException('The number of parameters must not exceed 65535. Restriction by PostgreSQL.');
273
+            $this->logger->error($exception->getMessage(), [
274
+                'query' => $this->getSQL(),
275
+                'app' => 'core',
276
+                'exception' => $exception,
277
+            ]);
278
+        }
279
+
280
+        $result = $this->queryBuilder->execute();
281
+        if (is_int($result)) {
282
+            return $result;
283
+        }
284
+        return new ResultAdapter($result);
285
+    }
286
+
287
+    public function executeQuery(): IResult {
288
+        if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder::SELECT) {
289
+            throw new \RuntimeException('Invalid query type, expected SELECT query');
290
+        }
291
+
292
+        try {
293
+            $result = $this->execute();
294
+        } catch (\Doctrine\DBAL\Exception $e) {
295
+            throw \OC\DB\Exceptions\DbalException::wrap($e);
296
+        }
297
+
298
+        if ($result instanceof IResult) {
299
+            return $result;
300
+        }
301
+
302
+        throw new \RuntimeException('Invalid return type for query');
303
+    }
304
+
305
+    /**
306
+     * Monkey-patched compatibility layer for apps that were adapted for Nextcloud 22 before
307
+     * the first beta, where executeStatement was named executeUpdate.
308
+     *
309
+     * Static analysis should catch those misuses, but until then let's try to keep things
310
+     * running.
311
+     *
312
+     * @internal
313
+     * @deprecated
314
+     * @todo drop ASAP
315
+     */
316
+    public function executeUpdate(): int {
317
+        return $this->executeStatement();
318
+    }
319
+
320
+    public function executeStatement(): int {
321
+        if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::SELECT) {
322
+            throw new \RuntimeException('Invalid query type, expected INSERT, DELETE or UPDATE statement');
323
+        }
324
+
325
+        try {
326
+            $result = $this->execute();
327
+        } catch (\Doctrine\DBAL\Exception $e) {
328
+            throw \OC\DB\Exceptions\DbalException::wrap($e);
329
+        }
330
+
331
+        if (!is_int($result)) {
332
+            throw new \RuntimeException('Invalid return type for statement');
333
+        }
334
+
335
+        return $result;
336
+    }
337
+
338
+
339
+    /**
340
+     * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
341
+     *
342
+     * <code>
343
+     *     $qb = $conn->getQueryBuilder()
344
+     *         ->select('u')
345
+     *         ->from('User', 'u')
346
+     *     echo $qb->getSQL(); // SELECT u FROM User u
347
+     * </code>
348
+     *
349
+     * @return string The SQL query string.
350
+     */
351
+    public function getSQL() {
352
+        return $this->queryBuilder->getSQL();
353
+    }
354
+
355
+    /**
356
+     * Sets a query parameter for the query being constructed.
357
+     *
358
+     * <code>
359
+     *     $qb = $conn->getQueryBuilder()
360
+     *         ->select('u')
361
+     *         ->from('users', 'u')
362
+     *         ->where('u.id = :user_id')
363
+     *         ->setParameter(':user_id', 1);
364
+     * </code>
365
+     *
366
+     * @param string|integer $key The parameter position or name.
367
+     * @param mixed $value The parameter value.
368
+     * @param string|null|int $type One of the IQueryBuilder::PARAM_* constants.
369
+     *
370
+     * @return $this This QueryBuilder instance.
371
+     */
372
+    public function setParameter($key, $value, $type = null) {
373
+        $this->queryBuilder->setParameter($key, $value, $type);
374
+
375
+        return $this;
376
+    }
377
+
378
+    /**
379
+     * Sets a collection of query parameters for the query being constructed.
380
+     *
381
+     * <code>
382
+     *     $qb = $conn->getQueryBuilder()
383
+     *         ->select('u')
384
+     *         ->from('users', 'u')
385
+     *         ->where('u.id = :user_id1 OR u.id = :user_id2')
386
+     *         ->setParameters(array(
387
+     *             ':user_id1' => 1,
388
+     *             ':user_id2' => 2
389
+     *         ));
390
+     * </code>
391
+     *
392
+     * @param array $params The query parameters to set.
393
+     * @param array $types The query parameters types to set.
394
+     *
395
+     * @return $this This QueryBuilder instance.
396
+     */
397
+    public function setParameters(array $params, array $types = []) {
398
+        $this->queryBuilder->setParameters($params, $types);
399
+
400
+        return $this;
401
+    }
402
+
403
+    /**
404
+     * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
405
+     *
406
+     * @return array The currently defined query parameters indexed by parameter index or name.
407
+     */
408
+    public function getParameters() {
409
+        return $this->queryBuilder->getParameters();
410
+    }
411
+
412
+    /**
413
+     * Gets a (previously set) query parameter of the query being constructed.
414
+     *
415
+     * @param mixed $key The key (index or name) of the bound parameter.
416
+     *
417
+     * @return mixed The value of the bound parameter.
418
+     */
419
+    public function getParameter($key) {
420
+        return $this->queryBuilder->getParameter($key);
421
+    }
422
+
423
+    /**
424
+     * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
425
+     *
426
+     * @return array The currently defined query parameter types indexed by parameter index or name.
427
+     */
428
+    public function getParameterTypes() {
429
+        return $this->queryBuilder->getParameterTypes();
430
+    }
431
+
432
+    /**
433
+     * Gets a (previously set) query parameter type of the query being constructed.
434
+     *
435
+     * @param mixed $key The key (index or name) of the bound parameter type.
436
+     *
437
+     * @return mixed The value of the bound parameter type.
438
+     */
439
+    public function getParameterType($key) {
440
+        return $this->queryBuilder->getParameterType($key);
441
+    }
442
+
443
+    /**
444
+     * Sets the position of the first result to retrieve (the "offset").
445
+     *
446
+     * @param int $firstResult The first result to return.
447
+     *
448
+     * @return $this This QueryBuilder instance.
449
+     */
450
+    public function setFirstResult($firstResult) {
451
+        $this->queryBuilder->setFirstResult((int) $firstResult);
452
+
453
+        return $this;
454
+    }
455
+
456
+    /**
457
+     * Gets the position of the first result the query object was set to retrieve (the "offset").
458
+     * Returns 0 if {@link setFirstResult} was not applied to this QueryBuilder.
459
+     *
460
+     * @return int The position of the first result.
461
+     */
462
+    public function getFirstResult() {
463
+        return $this->queryBuilder->getFirstResult();
464
+    }
465
+
466
+    /**
467
+     * Sets the maximum number of results to retrieve (the "limit").
468
+     *
469
+     * NOTE: Setting max results to "0" will cause mixed behaviour. While most
470
+     * of the databases will just return an empty result set, Oracle will return
471
+     * all entries.
472
+     *
473
+     * @param int|null $maxResults The maximum number of results to retrieve.
474
+     *
475
+     * @return $this This QueryBuilder instance.
476
+     */
477
+    public function setMaxResults($maxResults) {
478
+        if ($maxResults === null) {
479
+            $this->queryBuilder->setMaxResults($maxResults);
480
+        } else {
481
+            $this->queryBuilder->setMaxResults((int) $maxResults);
482
+        }
483
+
484
+        return $this;
485
+    }
486
+
487
+    /**
488
+     * Gets the maximum number of results the query object was set to retrieve (the "limit").
489
+     * Returns NULL if {@link setMaxResults} was not applied to this query builder.
490
+     *
491
+     * @return int|null The maximum number of results.
492
+     */
493
+    public function getMaxResults() {
494
+        return $this->queryBuilder->getMaxResults();
495
+    }
496
+
497
+    /**
498
+     * Specifies an item that is to be returned in the query result.
499
+     * Replaces any previously specified selections, if any.
500
+     *
501
+     * <code>
502
+     *     $qb = $conn->getQueryBuilder()
503
+     *         ->select('u.id', 'p.id')
504
+     *         ->from('users', 'u')
505
+     *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
506
+     * </code>
507
+     *
508
+     * @param mixed ...$selects The selection expressions.
509
+     *
510
+     * '@return $this This QueryBuilder instance.
511
+     */
512
+    public function select(...$selects) {
513
+        if (count($selects) === 1 && is_array($selects[0])) {
514
+            $selects = $selects[0];
515
+        }
516
+
517
+        $this->queryBuilder->select(
518
+            $this->helper->quoteColumnNames($selects)
519
+        );
520
+
521
+        return $this;
522
+    }
523
+
524
+    /**
525
+     * Specifies an item that is to be returned with a different name in the query result.
526
+     *
527
+     * <code>
528
+     *     $qb = $conn->getQueryBuilder()
529
+     *         ->selectAlias('u.id', 'user_id')
530
+     *         ->from('users', 'u')
531
+     *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
532
+     * </code>
533
+     *
534
+     * @param mixed $select The selection expressions.
535
+     * @param string $alias The column alias used in the constructed query.
536
+     *
537
+     * @return $this This QueryBuilder instance.
538
+     */
539
+    public function selectAlias($select, $alias) {
540
+        $this->queryBuilder->addSelect(
541
+            $this->helper->quoteColumnName($select) . ' AS ' . $this->helper->quoteColumnName($alias)
542
+        );
543
+
544
+        return $this;
545
+    }
546
+
547
+    /**
548
+     * Specifies an item that is to be returned uniquely in the query result.
549
+     *
550
+     * <code>
551
+     *     $qb = $conn->getQueryBuilder()
552
+     *         ->selectDistinct('type')
553
+     *         ->from('users');
554
+     * </code>
555
+     *
556
+     * @param mixed $select The selection expressions.
557
+     *
558
+     * @return $this This QueryBuilder instance.
559
+     */
560
+    public function selectDistinct($select) {
561
+        if (!is_array($select)) {
562
+            $select = [$select];
563
+        }
564
+
565
+        $quotedSelect = $this->helper->quoteColumnNames($select);
566
+
567
+        $this->queryBuilder->addSelect(
568
+            'DISTINCT ' . implode(', ', $quotedSelect)
569
+        );
570
+
571
+        return $this;
572
+    }
573
+
574
+    /**
575
+     * Adds an item that is to be returned in the query result.
576
+     *
577
+     * <code>
578
+     *     $qb = $conn->getQueryBuilder()
579
+     *         ->select('u.id')
580
+     *         ->addSelect('p.id')
581
+     *         ->from('users', 'u')
582
+     *         ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
583
+     * </code>
584
+     *
585
+     * @param mixed ...$selects The selection expression.
586
+     *
587
+     * @return $this This QueryBuilder instance.
588
+     */
589
+    public function addSelect(...$selects) {
590
+        if (count($selects) === 1 && is_array($selects[0])) {
591
+            $selects = $selects[0];
592
+        }
593
+
594
+        $this->queryBuilder->addSelect(
595
+            $this->helper->quoteColumnNames($selects)
596
+        );
597
+
598
+        return $this;
599
+    }
600
+
601
+    /**
602
+     * Turns the query being built into a bulk delete query that ranges over
603
+     * a certain table.
604
+     *
605
+     * <code>
606
+     *     $qb = $conn->getQueryBuilder()
607
+     *         ->delete('users', 'u')
608
+     *         ->where('u.id = :user_id');
609
+     *         ->setParameter(':user_id', 1);
610
+     * </code>
611
+     *
612
+     * @param string $delete The table whose rows are subject to the deletion.
613
+     * @param string $alias The table alias used in the constructed query.
614
+     *
615
+     * @return $this This QueryBuilder instance.
616
+     */
617
+    public function delete($delete = null, $alias = null) {
618
+        $this->queryBuilder->delete(
619
+            $this->getTableName($delete),
620
+            $alias
621
+        );
622
+
623
+        return $this;
624
+    }
625
+
626
+    /**
627
+     * Turns the query being built into a bulk update query that ranges over
628
+     * a certain table
629
+     *
630
+     * <code>
631
+     *     $qb = $conn->getQueryBuilder()
632
+     *         ->update('users', 'u')
633
+     *         ->set('u.password', md5('password'))
634
+     *         ->where('u.id = ?');
635
+     * </code>
636
+     *
637
+     * @param string $update The table whose rows are subject to the update.
638
+     * @param string $alias The table alias used in the constructed query.
639
+     *
640
+     * @return $this This QueryBuilder instance.
641
+     */
642
+    public function update($update = null, $alias = null) {
643
+        $this->queryBuilder->update(
644
+            $this->getTableName($update),
645
+            $alias
646
+        );
647
+
648
+        return $this;
649
+    }
650
+
651
+    /**
652
+     * Turns the query being built into an insert query that inserts into
653
+     * a certain table
654
+     *
655
+     * <code>
656
+     *     $qb = $conn->getQueryBuilder()
657
+     *         ->insert('users')
658
+     *         ->values(
659
+     *             array(
660
+     *                 'name' => '?',
661
+     *                 'password' => '?'
662
+     *             )
663
+     *         );
664
+     * </code>
665
+     *
666
+     * @param string $insert The table into which the rows should be inserted.
667
+     *
668
+     * @return $this This QueryBuilder instance.
669
+     */
670
+    public function insert($insert = null) {
671
+        $this->queryBuilder->insert(
672
+            $this->getTableName($insert)
673
+        );
674
+
675
+        $this->lastInsertedTable = $insert;
676
+
677
+        return $this;
678
+    }
679
+
680
+    /**
681
+     * Creates and adds a query root corresponding to the table identified by the
682
+     * given alias, forming a cartesian product with any existing query roots.
683
+     *
684
+     * <code>
685
+     *     $qb = $conn->getQueryBuilder()
686
+     *         ->select('u.id')
687
+     *         ->from('users', 'u')
688
+     * </code>
689
+     *
690
+     * @param string|IQueryFunction $from The table.
691
+     * @param string|null $alias The alias of the table.
692
+     *
693
+     * @return $this This QueryBuilder instance.
694
+     */
695
+    public function from($from, $alias = null) {
696
+        $this->queryBuilder->from(
697
+            $this->getTableName($from),
698
+            $this->quoteAlias($alias)
699
+        );
700
+
701
+        return $this;
702
+    }
703
+
704
+    /**
705
+     * Creates and adds a join to the query.
706
+     *
707
+     * <code>
708
+     *     $qb = $conn->getQueryBuilder()
709
+     *         ->select('u.name')
710
+     *         ->from('users', 'u')
711
+     *         ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
712
+     * </code>
713
+     *
714
+     * @param string $fromAlias The alias that points to a from clause.
715
+     * @param string $join The table name to join.
716
+     * @param string $alias The alias of the join table.
717
+     * @param string|ICompositeExpression|null $condition The condition for the join.
718
+     *
719
+     * @return $this This QueryBuilder instance.
720
+     */
721
+    public function join($fromAlias, $join, $alias, $condition = null) {
722
+        $this->queryBuilder->join(
723
+            $this->quoteAlias($fromAlias),
724
+            $this->getTableName($join),
725
+            $this->quoteAlias($alias),
726
+            $condition
727
+        );
728
+
729
+        return $this;
730
+    }
731
+
732
+    /**
733
+     * Creates and adds a join to the query.
734
+     *
735
+     * <code>
736
+     *     $qb = $conn->getQueryBuilder()
737
+     *         ->select('u.name')
738
+     *         ->from('users', 'u')
739
+     *         ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
740
+     * </code>
741
+     *
742
+     * @param string $fromAlias The alias that points to a from clause.
743
+     * @param string $join The table name to join.
744
+     * @param string $alias The alias of the join table.
745
+     * @param string|ICompositeExpression|null $condition The condition for the join.
746
+     *
747
+     * @return $this This QueryBuilder instance.
748
+     */
749
+    public function innerJoin($fromAlias, $join, $alias, $condition = null) {
750
+        $this->queryBuilder->innerJoin(
751
+            $this->quoteAlias($fromAlias),
752
+            $this->getTableName($join),
753
+            $this->quoteAlias($alias),
754
+            $condition
755
+        );
756
+
757
+        return $this;
758
+    }
759
+
760
+    /**
761
+     * Creates and adds a left join to the query.
762
+     *
763
+     * <code>
764
+     *     $qb = $conn->getQueryBuilder()
765
+     *         ->select('u.name')
766
+     *         ->from('users', 'u')
767
+     *         ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
768
+     * </code>
769
+     *
770
+     * @param string $fromAlias The alias that points to a from clause.
771
+     * @param string $join The table name to join.
772
+     * @param string $alias The alias of the join table.
773
+     * @param string|ICompositeExpression|null $condition The condition for the join.
774
+     *
775
+     * @return $this This QueryBuilder instance.
776
+     */
777
+    public function leftJoin($fromAlias, $join, $alias, $condition = null) {
778
+        $this->queryBuilder->leftJoin(
779
+            $this->quoteAlias($fromAlias),
780
+            $this->getTableName($join),
781
+            $this->quoteAlias($alias),
782
+            $condition
783
+        );
784
+
785
+        return $this;
786
+    }
787
+
788
+    /**
789
+     * Creates and adds a right join to the query.
790
+     *
791
+     * <code>
792
+     *     $qb = $conn->getQueryBuilder()
793
+     *         ->select('u.name')
794
+     *         ->from('users', 'u')
795
+     *         ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
796
+     * </code>
797
+     *
798
+     * @param string $fromAlias The alias that points to a from clause.
799
+     * @param string $join The table name to join.
800
+     * @param string $alias The alias of the join table.
801
+     * @param string|ICompositeExpression|null $condition The condition for the join.
802
+     *
803
+     * @return $this This QueryBuilder instance.
804
+     */
805
+    public function rightJoin($fromAlias, $join, $alias, $condition = null) {
806
+        $this->queryBuilder->rightJoin(
807
+            $this->quoteAlias($fromAlias),
808
+            $this->getTableName($join),
809
+            $this->quoteAlias($alias),
810
+            $condition
811
+        );
812
+
813
+        return $this;
814
+    }
815
+
816
+    /**
817
+     * Sets a new value for a column in a bulk update query.
818
+     *
819
+     * <code>
820
+     *     $qb = $conn->getQueryBuilder()
821
+     *         ->update('users', 'u')
822
+     *         ->set('u.password', md5('password'))
823
+     *         ->where('u.id = ?');
824
+     * </code>
825
+     *
826
+     * @param string $key The column to set.
827
+     * @param ILiteral|IParameter|IQueryFunction|string $value The value, expression, placeholder, etc.
828
+     *
829
+     * @return $this This QueryBuilder instance.
830
+     */
831
+    public function set($key, $value) {
832
+        $this->queryBuilder->set(
833
+            $this->helper->quoteColumnName($key),
834
+            $this->helper->quoteColumnName($value)
835
+        );
836
+
837
+        return $this;
838
+    }
839
+
840
+    /**
841
+     * Specifies one or more restrictions to the query result.
842
+     * Replaces any previously specified restrictions, if any.
843
+     *
844
+     * <code>
845
+     *     $qb = $conn->getQueryBuilder()
846
+     *         ->select('u.name')
847
+     *         ->from('users', 'u')
848
+     *         ->where('u.id = ?');
849
+     *
850
+     *     // You can optionally programmatically build and/or expressions
851
+     *     $qb = $conn->getQueryBuilder();
852
+     *
853
+     *     $or = $qb->expr()->orx();
854
+     *     $or->add($qb->expr()->eq('u.id', 1));
855
+     *     $or->add($qb->expr()->eq('u.id', 2));
856
+     *
857
+     *     $qb->update('users', 'u')
858
+     *         ->set('u.password', md5('password'))
859
+     *         ->where($or);
860
+     * </code>
861
+     *
862
+     * @param mixed ...$predicates The restriction predicates.
863
+     *
864
+     * @return $this This QueryBuilder instance.
865
+     */
866
+    public function where(...$predicates) {
867
+        if ($this->getQueryPart('where') !== null && $this->systemConfig->getValue('debug', false)) {
868
+            // Only logging a warning, not throwing for now.
869
+            $e = new QueryException('Using where() on non-empty WHERE part, please verify it is intentional to not call whereAnd() or whereOr() instead. Otherwise consider creating a new query builder object or call resetQueryPart(\'where\') first.');
870
+            $this->logger->warning($e->getMessage(), ['exception' => $e]);
871
+        }
872
+
873
+        call_user_func_array(
874
+            [$this->queryBuilder, 'where'],
875
+            $predicates
876
+        );
877
+
878
+        return $this;
879
+    }
880
+
881
+    /**
882
+     * Adds one or more restrictions to the query results, forming a logical
883
+     * conjunction with any previously specified restrictions.
884
+     *
885
+     * <code>
886
+     *     $qb = $conn->getQueryBuilder()
887
+     *         ->select('u')
888
+     *         ->from('users', 'u')
889
+     *         ->where('u.username LIKE ?')
890
+     *         ->andWhere('u.is_active = 1');
891
+     * </code>
892
+     *
893
+     * @param mixed ...$where The query restrictions.
894
+     *
895
+     * @return $this This QueryBuilder instance.
896
+     *
897
+     * @see where()
898
+     */
899
+    public function andWhere(...$where) {
900
+        call_user_func_array(
901
+            [$this->queryBuilder, 'andWhere'],
902
+            $where
903
+        );
904
+
905
+        return $this;
906
+    }
907
+
908
+    /**
909
+     * Adds one or more restrictions to the query results, forming a logical
910
+     * disjunction with any previously specified restrictions.
911
+     *
912
+     * <code>
913
+     *     $qb = $conn->getQueryBuilder()
914
+     *         ->select('u.name')
915
+     *         ->from('users', 'u')
916
+     *         ->where('u.id = 1')
917
+     *         ->orWhere('u.id = 2');
918
+     * </code>
919
+     *
920
+     * @param mixed ...$where The WHERE statement.
921
+     *
922
+     * @return $this This QueryBuilder instance.
923
+     *
924
+     * @see where()
925
+     */
926
+    public function orWhere(...$where) {
927
+        call_user_func_array(
928
+            [$this->queryBuilder, 'orWhere'],
929
+            $where
930
+        );
931
+
932
+        return $this;
933
+    }
934
+
935
+    /**
936
+     * Specifies a grouping over the results of the query.
937
+     * Replaces any previously specified groupings, if any.
938
+     *
939
+     * <code>
940
+     *     $qb = $conn->getQueryBuilder()
941
+     *         ->select('u.name')
942
+     *         ->from('users', 'u')
943
+     *         ->groupBy('u.id');
944
+     * </code>
945
+     *
946
+     * @param mixed ...$groupBys The grouping expression.
947
+     *
948
+     * @return $this This QueryBuilder instance.
949
+     */
950
+    public function groupBy(...$groupBys) {
951
+        if (count($groupBys) === 1 && is_array($groupBys[0])) {
952
+            $groupBys = $groupBys[0];
953
+        }
954
+
955
+        call_user_func_array(
956
+            [$this->queryBuilder, 'groupBy'],
957
+            $this->helper->quoteColumnNames($groupBys)
958
+        );
959
+
960
+        return $this;
961
+    }
962
+
963
+    /**
964
+     * Adds a grouping expression to the query.
965
+     *
966
+     * <code>
967
+     *     $qb = $conn->getQueryBuilder()
968
+     *         ->select('u.name')
969
+     *         ->from('users', 'u')
970
+     *         ->groupBy('u.lastLogin');
971
+     *         ->addGroupBy('u.createdAt')
972
+     * </code>
973
+     *
974
+     * @param mixed ...$groupBy The grouping expression.
975
+     *
976
+     * @return $this This QueryBuilder instance.
977
+     */
978
+    public function addGroupBy(...$groupBys) {
979
+        if (count($groupBys) === 1 && is_array($groupBys[0])) {
980
+            $$groupBys = $groupBys[0];
981
+        }
982
+
983
+        call_user_func_array(
984
+            [$this->queryBuilder, 'addGroupBy'],
985
+            $this->helper->quoteColumnNames($groupBys)
986
+        );
987
+
988
+        return $this;
989
+    }
990
+
991
+    /**
992
+     * Sets a value for a column in an insert query.
993
+     *
994
+     * <code>
995
+     *     $qb = $conn->getQueryBuilder()
996
+     *         ->insert('users')
997
+     *         ->values(
998
+     *             array(
999
+     *                 'name' => '?'
1000
+     *             )
1001
+     *         )
1002
+     *         ->setValue('password', '?');
1003
+     * </code>
1004
+     *
1005
+     * @param string $column The column into which the value should be inserted.
1006
+     * @param IParameter|string $value The value that should be inserted into the column.
1007
+     *
1008
+     * @return $this This QueryBuilder instance.
1009
+     */
1010
+    public function setValue($column, $value) {
1011
+        $this->queryBuilder->setValue(
1012
+            $this->helper->quoteColumnName($column),
1013
+            (string) $value
1014
+        );
1015
+
1016
+        return $this;
1017
+    }
1018
+
1019
+    /**
1020
+     * Specifies values for an insert query indexed by column names.
1021
+     * Replaces any previous values, if any.
1022
+     *
1023
+     * <code>
1024
+     *     $qb = $conn->getQueryBuilder()
1025
+     *         ->insert('users')
1026
+     *         ->values(
1027
+     *             array(
1028
+     *                 'name' => '?',
1029
+     *                 'password' => '?'
1030
+     *             )
1031
+     *         );
1032
+     * </code>
1033
+     *
1034
+     * @param array $values The values to specify for the insert query indexed by column names.
1035
+     *
1036
+     * @return $this This QueryBuilder instance.
1037
+     */
1038
+    public function values(array $values) {
1039
+        $quotedValues = [];
1040
+        foreach ($values as $key => $value) {
1041
+            $quotedValues[$this->helper->quoteColumnName($key)] = $value;
1042
+        }
1043
+
1044
+        $this->queryBuilder->values($quotedValues);
1045
+
1046
+        return $this;
1047
+    }
1048
+
1049
+    /**
1050
+     * Specifies a restriction over the groups of the query.
1051
+     * Replaces any previous having restrictions, if any.
1052
+     *
1053
+     * @param mixed ...$having The restriction over the groups.
1054
+     *
1055
+     * @return $this This QueryBuilder instance.
1056
+     */
1057
+    public function having(...$having) {
1058
+        call_user_func_array(
1059
+            [$this->queryBuilder, 'having'],
1060
+            $having
1061
+        );
1062
+
1063
+        return $this;
1064
+    }
1065
+
1066
+    /**
1067
+     * Adds a restriction over the groups of the query, forming a logical
1068
+     * conjunction with any existing having restrictions.
1069
+     *
1070
+     * @param mixed ...$having The restriction to append.
1071
+     *
1072
+     * @return $this This QueryBuilder instance.
1073
+     */
1074
+    public function andHaving(...$having) {
1075
+        call_user_func_array(
1076
+            [$this->queryBuilder, 'andHaving'],
1077
+            $having
1078
+        );
1079
+
1080
+        return $this;
1081
+    }
1082
+
1083
+    /**
1084
+     * Adds a restriction over the groups of the query, forming a logical
1085
+     * disjunction with any existing having restrictions.
1086
+     *
1087
+     * @param mixed ...$having The restriction to add.
1088
+     *
1089
+     * @return $this This QueryBuilder instance.
1090
+     */
1091
+    public function orHaving(...$having) {
1092
+        call_user_func_array(
1093
+            [$this->queryBuilder, 'orHaving'],
1094
+            $having
1095
+        );
1096
+
1097
+        return $this;
1098
+    }
1099
+
1100
+    /**
1101
+     * Specifies an ordering for the query results.
1102
+     * Replaces any previously specified orderings, if any.
1103
+     *
1104
+     * @param string|IQueryFunction|ILiteral|IParameter $sort The ordering expression.
1105
+     * @param string $order The ordering direction.
1106
+     *
1107
+     * @return $this This QueryBuilder instance.
1108
+     */
1109
+    public function orderBy($sort, $order = null) {
1110
+        $this->queryBuilder->orderBy(
1111
+            $this->helper->quoteColumnName($sort),
1112
+            $order
1113
+        );
1114
+
1115
+        return $this;
1116
+    }
1117
+
1118
+    /**
1119
+     * Adds an ordering to the query results.
1120
+     *
1121
+     * @param string|ILiteral|IParameter|IQueryFunction $sort The ordering expression.
1122
+     * @param string $order The ordering direction.
1123
+     *
1124
+     * @return $this This QueryBuilder instance.
1125
+     */
1126
+    public function addOrderBy($sort, $order = null) {
1127
+        $this->queryBuilder->addOrderBy(
1128
+            $this->helper->quoteColumnName($sort),
1129
+            $order
1130
+        );
1131
+
1132
+        return $this;
1133
+    }
1134
+
1135
+    /**
1136
+     * Gets a query part by its name.
1137
+     *
1138
+     * @param string $queryPartName
1139
+     *
1140
+     * @return mixed
1141
+     */
1142
+    public function getQueryPart($queryPartName) {
1143
+        return $this->queryBuilder->getQueryPart($queryPartName);
1144
+    }
1145
+
1146
+    /**
1147
+     * Gets all query parts.
1148
+     *
1149
+     * @return array
1150
+     */
1151
+    public function getQueryParts() {
1152
+        return $this->queryBuilder->getQueryParts();
1153
+    }
1154
+
1155
+    /**
1156
+     * Resets SQL parts.
1157
+     *
1158
+     * @param array|null $queryPartNames
1159
+     *
1160
+     * @return $this This QueryBuilder instance.
1161
+     */
1162
+    public function resetQueryParts($queryPartNames = null) {
1163
+        $this->queryBuilder->resetQueryParts($queryPartNames);
1164
+
1165
+        return $this;
1166
+    }
1167
+
1168
+    /**
1169
+     * Resets a single SQL part.
1170
+     *
1171
+     * @param string $queryPartName
1172
+     *
1173
+     * @return $this This QueryBuilder instance.
1174
+     */
1175
+    public function resetQueryPart($queryPartName) {
1176
+        $this->queryBuilder->resetQueryPart($queryPartName);
1177
+
1178
+        return $this;
1179
+    }
1180
+
1181
+    /**
1182
+     * Creates a new named parameter and bind the value $value to it.
1183
+     *
1184
+     * This method provides a shortcut for PDOStatement::bindValue
1185
+     * when using prepared statements.
1186
+     *
1187
+     * The parameter $value specifies the value that you want to bind. If
1188
+     * $placeholder is not provided bindValue() will automatically create a
1189
+     * placeholder for you. An automatic placeholder will be of the name
1190
+     * ':dcValue1', ':dcValue2' etc.
1191
+     *
1192
+     * For more information see {@link https://www.php.net/pdostatement-bindparam}
1193
+     *
1194
+     * Example:
1195
+     * <code>
1196
+     * $value = 2;
1197
+     * $q->eq( 'id', $q->bindValue( $value ) );
1198
+     * $stmt = $q->executeQuery(); // executed with 'id = 2'
1199
+     * </code>
1200
+     *
1201
+     * @license New BSD License
1202
+     * @link http://www.zetacomponents.org
1203
+     *
1204
+     * @param mixed $value
1205
+     * @param mixed $type
1206
+     * @param string $placeHolder The name to bind with. The string must start with a colon ':'.
1207
+     *
1208
+     * @return IParameter the placeholder name used.
1209
+     */
1210
+    public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $placeHolder = null) {
1211
+        return new Parameter($this->queryBuilder->createNamedParameter($value, $type, $placeHolder));
1212
+    }
1213
+
1214
+    /**
1215
+     * Creates a new positional parameter and bind the given value to it.
1216
+     *
1217
+     * Attention: If you are using positional parameters with the query builder you have
1218
+     * to be very careful to bind all parameters in the order they appear in the SQL
1219
+     * statement , otherwise they get bound in the wrong order which can lead to serious
1220
+     * bugs in your code.
1221
+     *
1222
+     * Example:
1223
+     * <code>
1224
+     *  $qb = $conn->getQueryBuilder();
1225
+     *  $qb->select('u.*')
1226
+     *     ->from('users', 'u')
1227
+     *     ->where('u.username = ' . $qb->createPositionalParameter('Foo', IQueryBuilder::PARAM_STR))
1228
+     *     ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', IQueryBuilder::PARAM_STR))
1229
+     * </code>
1230
+     *
1231
+     * @param mixed $value
1232
+     * @param integer $type
1233
+     *
1234
+     * @return IParameter
1235
+     */
1236
+    public function createPositionalParameter($value, $type = IQueryBuilder::PARAM_STR) {
1237
+        return new Parameter($this->queryBuilder->createPositionalParameter($value, $type));
1238
+    }
1239
+
1240
+    /**
1241
+     * Creates a new parameter
1242
+     *
1243
+     * Example:
1244
+     * <code>
1245
+     *  $qb = $conn->getQueryBuilder();
1246
+     *  $qb->select('u.*')
1247
+     *     ->from('users', 'u')
1248
+     *     ->where('u.username = ' . $qb->createParameter('name'))
1249
+     *     ->setParameter('name', 'Bar', IQueryBuilder::PARAM_STR))
1250
+     * </code>
1251
+     *
1252
+     * @param string $name
1253
+     *
1254
+     * @return IParameter
1255
+     */
1256
+    public function createParameter($name) {
1257
+        return new Parameter(':' . $name);
1258
+    }
1259
+
1260
+    /**
1261
+     * Creates a new function
1262
+     *
1263
+     * Attention: Column names inside the call have to be quoted before hand
1264
+     *
1265
+     * Example:
1266
+     * <code>
1267
+     *  $qb = $conn->getQueryBuilder();
1268
+     *  $qb->select($qb->createFunction('COUNT(*)'))
1269
+     *     ->from('users', 'u')
1270
+     *  echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u
1271
+     * </code>
1272
+     * <code>
1273
+     *  $qb = $conn->getQueryBuilder();
1274
+     *  $qb->select($qb->createFunction('COUNT(`column`)'))
1275
+     *     ->from('users', 'u')
1276
+     *  echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u
1277
+     * </code>
1278
+     *
1279
+     * @param string $call
1280
+     *
1281
+     * @return IQueryFunction
1282
+     */
1283
+    public function createFunction($call) {
1284
+        return new QueryFunction($call);
1285
+    }
1286
+
1287
+    /**
1288
+     * Used to get the id of the last inserted element
1289
+     * @return int
1290
+     * @throws \BadMethodCallException When being called before an insert query has been run.
1291
+     */
1292
+    public function getLastInsertId(): int {
1293
+        if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable) {
1294
+            // lastInsertId() needs the prefix but no quotes
1295
+            $table = $this->prefixTableName($this->lastInsertedTable);
1296
+            return $this->connection->lastInsertId($table);
1297
+        }
1298
+
1299
+        throw new \BadMethodCallException('Invalid call to getLastInsertId without using insert() before.');
1300
+    }
1301
+
1302
+    /**
1303
+     * Returns the table name quoted and with database prefix as needed by the implementation
1304
+     *
1305
+     * @param string|IQueryFunction $table
1306
+     * @return string
1307
+     */
1308
+    public function getTableName($table) {
1309
+        if ($table instanceof IQueryFunction) {
1310
+            return (string) $table;
1311
+        }
1312
+
1313
+        $table = $this->prefixTableName($table);
1314
+        return $this->helper->quoteColumnName($table);
1315
+    }
1316
+
1317
+    /**
1318
+     * Returns the table name with database prefix as needed by the implementation
1319
+     *
1320
+     * @param string $table
1321
+     * @return string
1322
+     */
1323
+    protected function prefixTableName($table) {
1324
+        if ($this->automaticTablePrefix === false || strpos($table, '*PREFIX*') === 0) {
1325
+            return $table;
1326
+        }
1327
+
1328
+        return '*PREFIX*' . $table;
1329
+    }
1330
+
1331
+    /**
1332
+     * Returns the column name quoted and with table alias prefix as needed by the implementation
1333
+     *
1334
+     * @param string $column
1335
+     * @param string $tableAlias
1336
+     * @return string
1337
+     */
1338
+    public function getColumnName($column, $tableAlias = '') {
1339
+        if ($tableAlias !== '') {
1340
+            $tableAlias .= '.';
1341
+        }
1342
+
1343
+        return $this->helper->quoteColumnName($tableAlias . $column);
1344
+    }
1345
+
1346
+    /**
1347
+     * Returns the column name quoted and with table alias prefix as needed by the implementation
1348
+     *
1349
+     * @param string $alias
1350
+     * @return string
1351
+     */
1352
+    public function quoteAlias($alias) {
1353
+        if ($alias === '' || $alias === null) {
1354
+            return $alias;
1355
+        }
1356
+
1357
+        return $this->helper->quoteColumnName($alias);
1358
+    }
1359 1359
 }
Please login to merge, or discard this patch.