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