Passed
Push — master ( 121e2d...f9ab75 )
by Blizzz
28:22 queued 13:14
created

QueryBuilder::execute()   F

Complexity

Conditions 15
Paths 1248

Size

Total Lines 87
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 15
eloc 60
c 2
b 0
f 0
nc 1248
nop 0
dl 0
loc 87
rs 1.7499

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

250
					'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...
251
					'app' => 'core',
252
				]);
253
			}
254
		}
255
256
		$numberOfParameters = 0;
257
		$hasTooLargeArrayParameter = false;
258
		foreach ($this->getParameters() as $parameter) {
259
			if (is_array($parameter)) {
260
				$count = count($parameter);
261
				$numberOfParameters += $count;
262
				$hasTooLargeArrayParameter = $hasTooLargeArrayParameter || ($count > 1000);
263
			}
264
		}
265
266
		if ($hasTooLargeArrayParameter) {
267
			$exception = new QueryException('More than 1000 expressions in a list are not allowed on Oracle.');
268
			$this->logger->logException($exception, [
269
				'message' => 'More than 1000 expressions in a list are not allowed on Oracle.',
270
				'query' => $this->getSQL(),
271
				'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

271
				'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...
272
				'app' => 'core',
273
			]);
274
		}
275
276
		if ($numberOfParameters > 65535) {
277
			$exception = new QueryException('The number of parameters must not exceed 65535. Restriction by PostgreSQL.');
278
			$this->logger->logException($exception, [
279
				'message' => 'The number of parameters must not exceed 65535. Restriction by PostgreSQL.',
280
				'query' => $this->getSQL(),
281
				'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

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