diff options
Diffstat (limited to 'lib/public/DB')
-rw-r--r-- | lib/public/DB/Events/AddMissingColumnsEvent.php | 43 | ||||
-rw-r--r-- | lib/public/DB/Events/AddMissingIndicesEvent.php | 100 | ||||
-rw-r--r-- | lib/public/DB/Events/AddMissingPrimaryKeyEvent.php | 43 | ||||
-rw-r--r-- | lib/public/DB/Exception.php | 33 | ||||
-rw-r--r-- | lib/public/DB/IPreparedStatement.php | 33 | ||||
-rw-r--r-- | lib/public/DB/IResult.php | 38 | ||||
-rw-r--r-- | lib/public/DB/ISchemaWrapper.php | 46 | ||||
-rw-r--r-- | lib/public/DB/QueryBuilder/ICompositeExpression.php | 33 | ||||
-rw-r--r-- | lib/public/DB/QueryBuilder/IExpressionBuilder.php | 194 | ||||
-rw-r--r-- | lib/public/DB/QueryBuilder/IFunctionBuilder.php | 65 | ||||
-rw-r--r-- | lib/public/DB/QueryBuilder/ILiteral.php | 25 | ||||
-rw-r--r-- | lib/public/DB/QueryBuilder/IParameter.php | 25 | ||||
-rw-r--r-- | lib/public/DB/QueryBuilder/IQueryBuilder.php | 324 | ||||
-rw-r--r-- | lib/public/DB/QueryBuilder/IQueryFunction.php | 25 | ||||
-rw-r--r-- | lib/public/DB/QueryBuilder/Sharded/IShardMapper.php | 25 | ||||
-rw-r--r-- | lib/public/DB/Types.php | 106 |
16 files changed, 806 insertions, 352 deletions
diff --git a/lib/public/DB/Events/AddMissingColumnsEvent.php b/lib/public/DB/Events/AddMissingColumnsEvent.php new file mode 100644 index 00000000000..178a24c0c30 --- /dev/null +++ b/lib/public/DB/Events/AddMissingColumnsEvent.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCP\DB\Events; + +/** + * Event to allow apps to register information about missing database columns + * + * This event will be dispatched for checking on the admin settings and when running + * occ db:add-missing-columns which will then create those columns + * + * @since 28.0.0 + */ +class AddMissingColumnsEvent extends \OCP\EventDispatcher\Event { + /** @var array<array-key, array{tableName: string, columnName: string, typeName: string, options: array{}}> */ + private array $missingColumns = []; + + /** + * @param mixed[] $options + * @since 28.0.0 + */ + public function addMissingColumn(string $tableName, string $columnName, string $typeName, array $options): void { + $this->missingColumns[] = [ + 'tableName' => $tableName, + 'columnName' => $columnName, + 'typeName' => $typeName, + 'options' => $options, + ]; + } + + /** + * @since 28.0.0 + * @return array<array-key, array{tableName: string, columnName: string, typeName: string, options: array{}}> + */ + public function getMissingColumns(): array { + return $this->missingColumns; + } +} diff --git a/lib/public/DB/Events/AddMissingIndicesEvent.php b/lib/public/DB/Events/AddMissingIndicesEvent.php new file mode 100644 index 00000000000..a8d4c5e6db5 --- /dev/null +++ b/lib/public/DB/Events/AddMissingIndicesEvent.php @@ -0,0 +1,100 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCP\DB\Events; + +/** + * Event to allow apps to register information about missing database indices + * + * This event will be dispatched for checking on the admin settings and when running + * occ db:add-missing-indices which will then create those indices + * + * @since 28.0.0 + */ +class AddMissingIndicesEvent extends \OCP\EventDispatcher\Event { + /** @var array<array-key, array{tableName: string, indexName: string, columns: string[], options: array{}, dropUnnamedIndex: bool, uniqueIndex: bool}> */ + private array $missingIndices = []; + + /** @var array<array-key, array{tableName: string, oldIndexNames: array, newIndexName: string, columns: string[], uniqueIndex: bool, options: array{}}> */ + private array $toReplaceIndices = []; + + /** + * @param string[] $columns + * @since 28.0.0 + */ + public function addMissingIndex(string $tableName, string $indexName, array $columns, array $options = [], bool $dropUnnamedIndex = false): void { + $this->missingIndices[] = [ + 'tableName' => $tableName, + 'indexName' => $indexName, + 'columns' => $columns, + 'options' => $options, + 'dropUnnamedIndex' => $dropUnnamedIndex, + 'uniqueIndex' => false, + ]; + } + /** + * @param string[] $columns + * @since 28.0.0 + */ + public function addMissingUniqueIndex(string $tableName, string $indexName, array $columns, array $options = [], bool $dropUnnamedIndex = false): void { + $this->missingIndices[] = [ + 'tableName' => $tableName, + 'indexName' => $indexName, + 'columns' => $columns, + 'options' => $options, + 'dropUnnamedIndex' => $dropUnnamedIndex, + 'uniqueIndex' => true, + ]; + } + + /** + * @since 28.0.0 + * @return array<array-key, array{tableName: string, indexName: string, columns: string[], options: array{}, dropUnnamedIndex: bool, uniqueIndex: bool}> + */ + public function getMissingIndices(): array { + return $this->missingIndices; + } + + /** + * Replace one or more existing indices with a new one. Can be used to make an index unique afterwards or merge two indices into a multicolumn index. + * + * Note: Make sure to not use the same index name for the new index as for old indices. + * + * Example: + * + * <code> + * $event->replaceIndex( + * 'my_table', + * ['old_index_col_a', 'old_index_col_b'], + * 'new_index_col_a_b', + * ['column_a', 'column_b'], + * false + * ); + * </code> + * + * @since 29.0.0 + */ + public function replaceIndex(string $tableName, array $oldIndexNames, string $newIndexName, array $columns, bool $unique, array $options = []): void { + $this->toReplaceIndices[] = [ + 'tableName' => $tableName, + 'oldIndexNames' => $oldIndexNames, + 'newIndexName' => $newIndexName, + 'columns' => $columns, + 'uniqueIndex' => $unique, + 'options' => $options, + ]; + } + + /** + * @since 29.0.0 + * @return array<array-key, array{tableName: string, oldIndexNames: array, newIndexName: string, columns: string[], uniqueIndex: bool, options: array{}}> + */ + public function getIndicesToReplace(): array { + return $this->toReplaceIndices; + } +} diff --git a/lib/public/DB/Events/AddMissingPrimaryKeyEvent.php b/lib/public/DB/Events/AddMissingPrimaryKeyEvent.php new file mode 100644 index 00000000000..66c638e66e0 --- /dev/null +++ b/lib/public/DB/Events/AddMissingPrimaryKeyEvent.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCP\DB\Events; + +/** + * Event to allow apps to register information about missing database primary keys + * + * This event will be dispatched for checking on the admin settings and when running + * occ db:add-missing-primary-keys which will then create those keys + * + * @since 28.0.0 + */ +class AddMissingPrimaryKeyEvent extends \OCP\EventDispatcher\Event { + /** @var array<array-key, array{tableName: string, primaryKeyName: string, columns: string[], formerIndex: null|string}> */ + private array $missingPrimaryKeys = []; + + /** + * @param string[] $columns + * @since 28.0.0 + */ + public function addMissingPrimaryKey(string $tableName, string $primaryKeyName, array $columns, ?string $formerIndex = null): void { + $this->missingPrimaryKeys[] = [ + 'tableName' => $tableName, + 'primaryKeyName' => $primaryKeyName, + 'columns' => $columns, + 'formerIndex' => $formerIndex, + ]; + } + + /** + * @since 28.0.0 + * @return array<array-key, array{tableName: string, primaryKeyName: string, columns: string[], formerIndex: null|string}> + */ + public function getMissingPrimaryKeys(): array { + return $this->missingPrimaryKeys; + } +} diff --git a/lib/public/DB/Exception.php b/lib/public/DB/Exception.php index 1154530e85d..6b908382aea 100644 --- a/lib/public/DB/Exception.php +++ b/lib/public/DB/Exception.php @@ -2,27 +2,10 @@ declare(strict_types=1); -/* - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCP\DB; use Exception as BaseException; @@ -39,7 +22,6 @@ use Exception as BaseException; * @since 21.0.0 */ class Exception extends BaseException { - /** * Nextcloud lost connection to the database * @@ -111,7 +93,7 @@ class Exception extends BaseException { public const REASON_NON_UNIQUE_FIELD_NAME = 10; /** - * A not null contraint was violated + * A not null constraint was violated * * @since 21.0.0 */ @@ -139,6 +121,13 @@ class Exception extends BaseException { public const REASON_UNIQUE_CONSTRAINT_VIOLATION = 14; /** + * The lock wait timeout was exceeded + * + * @since 30.0.0 + */ + public const REASON_LOCK_WAIT_TIMEOUT = 15; + + /** * @return int|null * @psalm-return Exception::REASON_* * @since 21.0.0 diff --git a/lib/public/DB/IPreparedStatement.php b/lib/public/DB/IPreparedStatement.php index 6b6617ba5ea..887451a1caf 100644 --- a/lib/public/DB/IPreparedStatement.php +++ b/lib/public/DB/IPreparedStatement.php @@ -2,27 +2,10 @@ declare(strict_types=1); -/* - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCP\DB; use Doctrine\DBAL\Exception; @@ -30,10 +13,18 @@ use Doctrine\DBAL\ParameterType; use PDO; /** + * This interface allows you to prepare a database query. + * + * This interface must not be implemented in your application but + * instead obtained from IDBConnection::prepare. + * + * ```php + * $prepare = $this->db->prepare($query->getSql()); + * ``` + * * @since 21.0.0 */ interface IPreparedStatement { - /** * @return true * diff --git a/lib/public/DB/IResult.php b/lib/public/DB/IResult.php index 10c788ebbf6..347919ab336 100644 --- a/lib/public/DB/IResult.php +++ b/lib/public/DB/IResult.php @@ -2,36 +2,30 @@ declare(strict_types=1); -/* - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCP\DB; use PDO; /** + * This interface represents the result of a database query. + * + * Usage: + * + * ```php + * $qb = $this->db->getQueryBuilder(); + * $qb->select(...); + * $result = $query->executeQuery(); + * ``` + * + * This interface must not be implemented in your application. + * * @since 21.0.0 */ interface IResult { - /** * @return true * @@ -66,7 +60,7 @@ interface IResult { public function fetchColumn(); /** - * @param int $columnIndex + * Returns the first value of the next row of the result or FALSE if there are no more rows. * * @return false|mixed * diff --git a/lib/public/DB/ISchemaWrapper.php b/lib/public/DB/ISchemaWrapper.php index 3d58a10d2d2..dcf22b52d3d 100644 --- a/lib/public/DB/ISchemaWrapper.php +++ b/lib/public/DB/ISchemaWrapper.php @@ -1,35 +1,25 @@ <?php + /** - * @copyright Copyright (c) 2018 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCP\DB; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\AbstractPlatform; + /** - * Interface ISchemaWrapper + * This interface allows to get information about the database schema. + * This is particularly helpful for database migration scripts. + * + * This interface must not be implemented in your application but + * instead can be obtained in your migration scripts with the + * `$schemaClosure` Closure. * * @since 13.0.0 */ interface ISchemaWrapper { - /** * @param string $tableName * @@ -82,7 +72,7 @@ interface ISchemaWrapper { * @since 13.0.0 */ public function getTableNames(); - + /** * Gets all table names * @@ -90,4 +80,14 @@ interface ISchemaWrapper { * @since 13.0.0 */ public function getTableNamesWithoutPrefix(); + + /** + * Gets the DatabasePlatform for the database. + * + * @return AbstractPlatform + * + * @throws Exception + * @since 23.0.0 + */ + public function getDatabasePlatform(); } diff --git a/lib/public/DB/QueryBuilder/ICompositeExpression.php b/lib/public/DB/QueryBuilder/ICompositeExpression.php index 905465ba9e0..12f321d9e54 100644 --- a/lib/public/DB/QueryBuilder/ICompositeExpression.php +++ b/lib/public/DB/QueryBuilder/ICompositeExpression.php @@ -1,27 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCP\DB\QueryBuilder; /** @@ -37,7 +20,7 @@ interface ICompositeExpression { * @return ICompositeExpression * @since 8.2.0 */ - public function addMultiple(array $parts = []); + public function addMultiple(array $parts = []): ICompositeExpression; /** * Adds an expression to composite expression. @@ -47,7 +30,7 @@ interface ICompositeExpression { * @return ICompositeExpression * @since 8.2.0 */ - public function add($part); + public function add($part): ICompositeExpression; /** * Retrieves the amount of expressions on composite expression. @@ -55,7 +38,7 @@ interface ICompositeExpression { * @return integer * @since 8.2.0 */ - public function count(); + public function count(): int; /** * Returns the type of this composite expression (AND/OR). @@ -63,5 +46,5 @@ interface ICompositeExpression { * @return string * @since 8.2.0 */ - public function getType(); + public function getType(): string; } diff --git a/lib/public/DB/QueryBuilder/IExpressionBuilder.php b/lib/public/DB/QueryBuilder/IExpressionBuilder.php index c62221305d5..12e30a45071 100644 --- a/lib/public/DB/QueryBuilder/IExpressionBuilder.php +++ b/lib/public/DB/QueryBuilder/IExpressionBuilder.php @@ -1,30 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCP\DB\QueryBuilder; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; @@ -32,6 +12,8 @@ use Doctrine\DBAL\Query\Expression\ExpressionBuilder; /** * This class provides a wrapper around Doctrine's ExpressionBuilder * @since 8.2.0 + * + * @psalm-taint-specialize */ interface IExpressionBuilder { /** @@ -69,12 +51,15 @@ interface IExpressionBuilder { * $expr->andX('u.type = ?', 'u.role = ?')); * * @param mixed ...$x Optional clause. Defaults = null, but requires - * at least one defined when converting to string. + * at least one defined when converting to string. * * @return \OCP\DB\QueryBuilder\ICompositeExpression * @since 8.2.0 + * @since 30.0.0 Calling the method without any arguments is deprecated and will throw with the next Doctrine/DBAL update + * + * @psalm-taint-sink sql $x */ - public function andX(...$x); + public function andX(...$x): ICompositeExpression; /** * Creates a disjunction of the given boolean expressions. @@ -86,12 +71,15 @@ interface IExpressionBuilder { * $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?')); * * @param mixed ...$x Optional clause. Defaults = null, but requires - * at least one defined when converting to string. + * at least one defined when converting to string. * * @return \OCP\DB\QueryBuilder\ICompositeExpression * @since 8.2.0 + * @since 30.0.0 Calling the method without any arguments is deprecated and will throw with the next Doctrine/DBAL update + * + * @psalm-taint-sink sql $x */ - public function orX(...$x); + public function orX(...$x): ICompositeExpression; /** * Creates a comparison expression. @@ -100,12 +88,17 @@ interface IExpressionBuilder { * @param string $operator One of the IExpressionBuilder::* constants. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $operator + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function comparison($x, $operator, $y, $type = null); + public function comparison($x, string $operator, $y, $type = null): string; /** * Creates an equality comparison expression with the given arguments. @@ -120,12 +113,16 @@ interface IExpressionBuilder { * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function eq($x, $y, $type = null); + public function eq($x, $y, $type = null): string; /** * Creates a non equality comparison expression with the given arguments. @@ -139,12 +136,16 @@ interface IExpressionBuilder { * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function neq($x, $y, $type = null); + public function neq($x, $y, $type = null): string; /** * Creates a lower-than comparison expression with the given arguments. @@ -158,12 +159,16 @@ interface IExpressionBuilder { * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function lt($x, $y, $type = null); + public function lt($x, $y, $type = null): string; /** * Creates a lower-than-equal comparison expression with the given arguments. @@ -177,12 +182,16 @@ interface IExpressionBuilder { * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function lte($x, $y, $type = null); + public function lte($x, $y, $type = null): string; /** * Creates a greater-than comparison expression with the given arguments. @@ -196,12 +205,16 @@ interface IExpressionBuilder { * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function gt($x, $y, $type = null); + public function gt($x, $y, $type = null): string; /** * Creates a greater-than-equal comparison expression with the given arguments. @@ -215,32 +228,40 @@ interface IExpressionBuilder { * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function gte($x, $y, $type = null); + public function gte($x, $y, $type = null): string; /** * Creates an IS NULL expression with the given arguments. * - * @param string $x The field in string format to be restricted by IS NULL. + * @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be restricted by IS NULL. * * @return string * @since 8.2.0 + * + * @psalm-taint-sink sql $x */ - public function isNull($x); + public function isNull($x): string; /** * Creates an IS NOT NULL expression with the given arguments. * - * @param string $x The field in string format to be restricted by IS NOT NULL. + * @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be restricted by IS NOT NULL. * * @return string * @since 8.2.0 + * + * @psalm-taint-sink sql $x */ - public function isNotNull($x); + public function isNotNull($x): string; /** * Creates a LIKE() comparison expression with the given arguments. @@ -248,12 +269,16 @@ interface IExpressionBuilder { * @param ILiteral|IParameter|IQueryFunction|string $x Field in string format to be inspected by LIKE() comparison. * @param mixed $y Argument to be used in LIKE() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function like($x, $y, $type = null); + public function like($x, $y, $type = null): string; /** * Creates a NOT LIKE() comparison expression with the given arguments. @@ -261,12 +286,16 @@ interface IExpressionBuilder { * @param ILiteral|IParameter|IQueryFunction|string $x Field in string format to be inspected by NOT LIKE() comparison. * @param mixed $y Argument to be used in NOT LIKE() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function notLike($x, $y, $type = null); + public function notLike($x, $y, $type = null): string; /** * Creates a ILIKE() comparison expression with the given arguments. @@ -274,12 +303,16 @@ interface IExpressionBuilder { * @param string $x Field in string format to be inspected by ILIKE() comparison. * @param mixed $y Argument to be used in ILIKE() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function iLike($x, $y, $type = null); + public function iLike($x, $y, $type = null): string; /** * Creates a IN () comparison expression with the given arguments. @@ -287,12 +320,16 @@ interface IExpressionBuilder { * @param ILiteral|IParameter|IQueryFunction|string $x The field in string format to be inspected by IN() comparison. * @param ILiteral|IParameter|IQueryFunction|string|array $y The placeholder or the array of values to be used by IN() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function in($x, $y, $type = null); + public function in($x, $y, $type = null): string; /** * Creates a NOT IN () comparison expression with the given arguments. @@ -300,30 +337,38 @@ interface IExpressionBuilder { * @param ILiteral|IParameter|IQueryFunction|string $x The field in string format to be inspected by NOT IN() comparison. * @param ILiteral|IParameter|IQueryFunction|string|array $y The placeholder or the array of values to be used by NOT IN() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants - * required when comparing text fields for oci compatibility + * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y + * @psalm-taint-sink sql $type */ - public function notIn($x, $y, $type = null); + public function notIn($x, $y, $type = null): string; /** * Creates a $x = '' statement, because Oracle needs a different check * - * @param string $x The field in string format to be inspected by the comparison. + * @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison. * @return string * @since 13.0.0 + * + * @psalm-taint-sink sql $x */ - public function emptyString($x); + public function emptyString($x): string; /** * Creates a `$x <> ''` statement, because Oracle needs a different check * - * @param string $x The field in string format to be inspected by the comparison. + * @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison. * @return string * @since 13.0.0 + * + * @psalm-taint-sink sql $x */ - public function nonEmptyString($x); + public function nonEmptyString($x): string; /** @@ -333,8 +378,11 @@ interface IExpressionBuilder { * @param int $y Bitmap that must be set * @return IQueryFunction * @since 12.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y */ - public function bitwiseAnd($x, $y); + public function bitwiseAnd($x, int $y): IQueryFunction; /** * Creates a bitwise OR comparison @@ -343,27 +391,37 @@ interface IExpressionBuilder { * @param int $y Bitmap that must be set * @return IQueryFunction * @since 12.0.0 + * + * @psalm-taint-sink sql $x + * @psalm-taint-sink sql $y */ - public function bitwiseOr($x, $y); + public function bitwiseOr($x, int $y): IQueryFunction; /** * Quotes a given input parameter. * * @param mixed $input The parameter to be quoted. - * @param mixed|null $type One of the IQueryBuilder::PARAM_* constants + * @param int $type One of the IQueryBuilder::PARAM_* constants * - * @return string + * @return ILiteral * @since 8.2.0 + * + * @psalm-taint-sink sql $input + * @psalm-taint-sink sql $type */ - public function literal($input, $type = null); + public function literal($input, $type = IQueryBuilder::PARAM_STR): ILiteral; /** * Returns a IQueryFunction that casts the column to the given type * - * @param string $column + * @param string|IQueryFunction $column * @param mixed $type One of IQueryBuilder::PARAM_* - * @return string + * @psalm-param IQueryBuilder::PARAM_* $type + * @return IQueryFunction * @since 9.0.0 + * + * @psalm-taint-sink sql $column + * @psalm-taint-sink sql $type */ - public function castColumn($column, $type); + public function castColumn($column, $type): IQueryFunction; } diff --git a/lib/public/DB/QueryBuilder/IFunctionBuilder.php b/lib/public/DB/QueryBuilder/IFunctionBuilder.php index 569bc7485d4..480ec1cb1ac 100644 --- a/lib/public/DB/QueryBuilder/IFunctionBuilder.php +++ b/lib/public/DB/QueryBuilder/IFunctionBuilder.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCP\DB\QueryBuilder; /** @@ -44,13 +25,29 @@ interface IFunctionBuilder { /** * Combines two input strings * - * @param string|ILiteral|IParameter|IQueryFunction $x The first input string - * @param string|ILiteral|IParameter|IQueryFunction $y The seccond input string + * @param string|ILiteral|IParameter|IQueryFunction $x Expressions or literal strings + * @param string|ILiteral|IParameter|IQueryFunction ...$exprs Expressions or literal strings * * @return IQueryFunction * @since 12.0.0 */ - public function concat($x, $y): IQueryFunction; + public function concat($x, ...$expr): IQueryFunction; + + /** + * Returns a string which is the concatenation of all non-NULL values of X + * + * Usage examples: + * + * groupConcat('column') -- with comma as separator (default separator) + * + * groupConcat('column', ';') -- with different separator + * + * @param string|IQueryFunction $expr The expression to group + * @param string|null $separator The separator + * @return IQueryFunction + * @since 24.0.0 + */ + public function groupConcat($expr, ?string $separator = ','): IQueryFunction; /** * Takes a substring from the input string @@ -109,6 +106,24 @@ interface IFunctionBuilder { public function count($count = '', $alias = ''): IQueryFunction; /** + * @param string|ILiteral|IParameter|IQueryFunction $field The input to be measured + * @param string $alias Alias for the length + * + * @return IQueryFunction + * @since 24.0.0 + */ + public function octetLength($field, $alias = ''): IQueryFunction; + + /** + * @param string|ILiteral|IParameter|IQueryFunction $field The input to be measured + * @param string $alias Alias for the length + * + * @return IQueryFunction + * @since 24.0.0 + */ + public function charLength($field, $alias = ''): IQueryFunction; + + /** * Takes the maximum of all rows in a column * * If you want to get the maximum value of multiple columns in the same row, use `greatest` instead diff --git a/lib/public/DB/QueryBuilder/ILiteral.php b/lib/public/DB/QueryBuilder/ILiteral.php index 8fe5c62d3f8..574eb0894eb 100644 --- a/lib/public/DB/QueryBuilder/ILiteral.php +++ b/lib/public/DB/QueryBuilder/ILiteral.php @@ -1,27 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCP\DB\QueryBuilder; /** diff --git a/lib/public/DB/QueryBuilder/IParameter.php b/lib/public/DB/QueryBuilder/IParameter.php index 1636dd8c438..86ea9437e9c 100644 --- a/lib/public/DB/QueryBuilder/IParameter.php +++ b/lib/public/DB/QueryBuilder/IParameter.php @@ -1,27 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCP\DB\QueryBuilder; /** diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php index 24de7b4ce60..4794e7e8877 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -1,83 +1,127 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCP\DB\QueryBuilder; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Types\Types; use OCP\DB\Exception; use OCP\DB\IResult; +use OCP\IDBConnection; /** * This class provides a wrapper around Doctrine's QueryBuilder * @since 8.2.0 + * + * @psalm-taint-specialize */ interface IQueryBuilder { - /** * @since 9.0.0 */ - public const PARAM_NULL = \PDO::PARAM_NULL; + public const PARAM_NULL = ParameterType::NULL; /** * @since 9.0.0 */ - public const PARAM_BOOL = \PDO::PARAM_BOOL; + public const PARAM_BOOL = Types::BOOLEAN; /** * @since 9.0.0 */ - public const PARAM_INT = \PDO::PARAM_INT; + public const PARAM_INT = ParameterType::INTEGER; /** * @since 9.0.0 */ - public const PARAM_STR = \PDO::PARAM_STR; + public const PARAM_STR = ParameterType::STRING; + /** + * @since 9.0.0 + */ + public const PARAM_LOB = ParameterType::LARGE_OBJECT; + /** * @since 9.0.0 + * @deprecated 31.0.0 - use PARAM_DATETIME_MUTABLE instead + */ + public const PARAM_DATE = Types::DATETIME_MUTABLE; + + /** + * For passing a \DateTime instance when only interested in the time part (without timezone support) + * @since 31.0.0 + */ + public const PARAM_TIME_MUTABLE = Types::TIME_MUTABLE; + + /** + * For passing a \DateTime instance when only interested in the date part (without timezone support) + * @since 31.0.0 + */ + public const PARAM_DATE_MUTABLE = Types::DATE_MUTABLE; + + /** + * For passing a \DateTime instance (without timezone support) + * @since 31.0.0 + */ + public const PARAM_DATETIME_MUTABLE = Types::DATETIME_MUTABLE; + + /** + * For passing a \DateTime instance with timezone support + * @since 31.0.0 + */ + public const PARAM_DATETIME_TZ_MUTABLE = Types::DATETIMETZ_MUTABLE; + + /** + * For passing a \DateTimeImmutable instance when only interested in the time part (without timezone support) + * @since 31.0.0 */ - public const PARAM_LOB = \PDO::PARAM_LOB; + public const PARAM_TIME_IMMUTABLE = Types::TIME_MUTABLE; + /** + * For passing a \DateTime instance when only interested in the date part (without timezone support) * @since 9.0.0 */ - public const PARAM_DATE = 'datetime'; + public const PARAM_DATE_IMMUTABLE = Types::DATE_IMMUTABLE; + + /** + * For passing a \DateTime instance (without timezone support) + * @since 31.0.0 + */ + public const PARAM_DATETIME_IMMUTABLE = Types::DATETIME_IMMUTABLE; + + /** + * For passing a \DateTime instance with timezone support + * @since 31.0.0 + */ + public const PARAM_DATETIME_TZ_IMMUTABLE = Types::DATETIMETZ_IMMUTABLE; + + /** + * @since 24.0.0 + */ + public const PARAM_JSON = 'json'; /** * @since 9.0.0 */ - public const PARAM_INT_ARRAY = Connection::PARAM_INT_ARRAY; + public const PARAM_INT_ARRAY = ArrayParameterType::INTEGER; /** * @since 9.0.0 */ - public const PARAM_STR_ARRAY = Connection::PARAM_STR_ARRAY; + public const PARAM_STR_ARRAY = ArrayParameterType::STRING; + /** + * @since 24.0.0 Indicates how many rows can be deleted at once with MySQL + * database server. + */ + public const MAX_ROW_DELETION = 100000; /** * Enable/disable automatic prefixing of table names with the oc_ prefix * * @param bool $enabled If set to true table names will be prefixed with the - * owncloud database prefix automatically. + * owncloud database prefix automatically. * @since 8.2.0 */ public function automaticTablePrefix($enabled); @@ -141,24 +185,52 @@ interface IQueryBuilder { * * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. * @since 8.2.0 + * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update + * and we can not fix this in our wrapper. */ public function getState(); /** * Executes this query using the bound parameters and their types. * - * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} + * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeStatement} * for insert, update and delete statements. * * Warning: until Nextcloud 20, this method could return a \Doctrine\DBAL\Driver\Statement but since * that interface changed in a breaking way the adapter \OCP\DB\QueryBuilder\IStatement is returned * to bridge old code to the new API * + * @param ?IDBConnection $connection (optional) the connection to run the query against. since 30.0 * @return IResult|int * @throws Exception since 21.0.0 * @since 8.2.0 + * @deprecated 22.0.0 Use executeQuery or executeStatement */ - public function execute(); + public function execute(?IDBConnection $connection = null); + + /** + * Execute for select statements + * + * @param ?IDBConnection $connection (optional) the connection to run the query against. since 30.0 + * @return IResult + * @since 22.0.0 + * + * @throws Exception + * @throws \RuntimeException in case of usage with non select query + */ + public function executeQuery(?IDBConnection $connection = null): IResult; + + /** + * Execute insert, update and delete statements + * + * @param ?IDBConnection $connection (optional) the connection to run the query against. since 30.0 + * @return int the number of affected rows + * @since 22.0.0 + * + * @throws Exception + * @throws \RuntimeException in case of usage with select query + */ + public function executeStatement(?IDBConnection $connection = null): int; /** * Gets the complete SQL string formed by the current specifications of this QueryBuilder. @@ -256,7 +328,7 @@ interface IQueryBuilder { /** * Sets the position of the first result to retrieve (the "offset"). * - * @param integer $firstResult The first result to return. + * @param int $firstResult The first result to return. * * @return $this This QueryBuilder instance. * @since 8.2.0 @@ -265,9 +337,9 @@ interface IQueryBuilder { /** * Gets the position of the first result the query object was set to retrieve (the "offset"). - * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. + * Returns 0 if {@link setFirstResult} was not applied to this QueryBuilder. * - * @return integer The position of the first result. + * @return int The position of the first result. * @since 8.2.0 */ public function getFirstResult(); @@ -275,7 +347,7 @@ interface IQueryBuilder { /** * Sets the maximum number of results to retrieve (the "limit"). * - * @param integer $maxResults The maximum number of results to retrieve. + * @param int|null $maxResults The maximum number of results to retrieve. * * @return $this This QueryBuilder instance. * @since 8.2.0 @@ -306,6 +378,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $selects */ public function select(...$selects); @@ -324,6 +398,9 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.1 + * + * @psalm-taint-sink sql $select + * @psalm-taint-sink sql $alias */ public function selectAlias($select, $alias); @@ -340,6 +417,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 9.0.0 + * + * @psalm-taint-sink sql $select */ public function selectDistinct($select); @@ -358,6 +437,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $select */ public function addSelect(...$select); @@ -367,8 +448,8 @@ interface IQueryBuilder { * * <code> * $qb = $conn->getQueryBuilder() - * ->delete('users', 'u') - * ->where('u.id = :user_id'); + * ->delete('users') + * ->where('id = :user_id'); * ->setParameter(':user_id', 1); * </code> * @@ -377,6 +458,9 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * @since 30.0.0 Alias is deprecated and will no longer be used with the next Doctrine/DBAL update + * + * @psalm-taint-sink sql $delete */ public function delete($delete = null, $alias = null); @@ -386,9 +470,10 @@ interface IQueryBuilder { * * <code> * $qb = $conn->getQueryBuilder() - * ->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where('u.id = ?'); + * ->update('users') + * ->set('email', ':email') + * ->where('id = :user_id'); + * ->setParameter(':user_id', 1); * </code> * * @param string $update The table whose rows are subject to the update. @@ -396,6 +481,9 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * @since 30.0.0 Alias is deprecated and will no longer be used with the next Doctrine/DBAL update + * + * @psalm-taint-sink sql $update */ public function update($update = null, $alias = null); @@ -418,6 +506,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $insert */ public function insert($insert = null); @@ -431,11 +521,13 @@ interface IQueryBuilder { * ->from('users', 'u') * </code> * - * @param string $from The table. + * @param string|IQueryFunction $from The table. * @param string|null $alias The alias of the table. * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $from */ public function from($from, $alias = null); @@ -452,10 +544,15 @@ interface IQueryBuilder { * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. - * @param string $condition The condition for the join. + * @param string|ICompositeExpression|null $condition The condition for the join. * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $fromAlias + * @psalm-taint-sink sql $join + * @psalm-taint-sink sql $alias + * @psalm-taint-sink sql $condition */ public function join($fromAlias, $join, $alias, $condition = null); @@ -472,10 +569,15 @@ interface IQueryBuilder { * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. - * @param string $condition The condition for the join. + * @param string|ICompositeExpression|null $condition The condition for the join. * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $fromAlias + * @psalm-taint-sink sql $join + * @psalm-taint-sink sql $alias + * @psalm-taint-sink sql $condition */ public function innerJoin($fromAlias, $join, $alias, $condition = null); @@ -490,12 +592,18 @@ interface IQueryBuilder { * </code> * * @param string $fromAlias The alias that points to a from clause. - * @param string $join The table name to join. + * @param string|IQueryFunction $join The table name to join. * @param string $alias The alias of the join table. - * @param string $condition The condition for the join. + * @param string|ICompositeExpression|null $condition The condition for the join. * * @return $this This QueryBuilder instance. * @since 8.2.0 + * @since 30.0.0 Allow passing IQueryFunction as parameter for `$join` to allow join with a sub-query. + * + * @psalm-taint-sink sql $fromAlias + * @psalm-taint-sink sql $join + * @psalm-taint-sink sql $alias + * @psalm-taint-sink sql $condition */ public function leftJoin($fromAlias, $join, $alias, $condition = null); @@ -512,10 +620,15 @@ interface IQueryBuilder { * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. - * @param string $condition The condition for the join. + * @param string|ICompositeExpression|null $condition The condition for the join. * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $fromAlias + * @psalm-taint-sink sql $join + * @psalm-taint-sink sql $alias + * @psalm-taint-sink sql $condition */ public function rightJoin($fromAlias, $join, $alias, $condition = null); @@ -534,6 +647,9 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $key + * @psalm-taint-sink sql $value */ public function set($key, $value); @@ -547,12 +663,13 @@ interface IQueryBuilder { * ->from('users', 'u') * ->where('u.id = ?'); * - * // You can optionally programatically build and/or expressions + * // You can optionally programmatically build and/or expressions * $qb = $conn->getQueryBuilder(); * - * $or = $qb->expr()->orx(); - * $or->add($qb->expr()->eq('u.id', 1)); - * $or->add($qb->expr()->eq('u.id', 2)); + * $or = $qb->expr()->orx( + * $qb->expr()->eq('u.id', 1), + * $qb->expr()->eq('u.id', 2), + * ); * * $qb->update('users', 'u') * ->set('u.password', md5('password')) @@ -563,6 +680,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $predicates */ public function where(...$predicates); @@ -584,6 +703,8 @@ interface IQueryBuilder { * * @see where() * @since 8.2.0 + * + * @psalm-taint-sink sql $where */ public function andWhere(...$where); @@ -605,6 +726,8 @@ interface IQueryBuilder { * * @see where() * @since 8.2.0 + * + * @psalm-taint-sink sql $where */ public function orWhere(...$where); @@ -623,6 +746,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $groupBys */ public function groupBy(...$groupBys); @@ -641,6 +766,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $groupby */ public function addGroupBy(...$groupBy); @@ -663,6 +790,9 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $column + * @psalm-taint-sink sql $value */ public function setValue($column, $value); @@ -685,6 +815,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $values */ public function values(array $values); @@ -696,6 +828,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $having */ public function having(...$having); @@ -707,6 +841,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $andHaving */ public function andHaving(...$having); @@ -718,6 +854,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $having */ public function orHaving(...$having); @@ -725,22 +863,28 @@ interface IQueryBuilder { * Specifies an ordering for the query results. * Replaces any previously specified orderings, if any. * - * @param string $sort The ordering expression. + * @param string|IQueryFunction|ILiteral|IParameter $sort The ordering expression. * @param string $order The ordering direction. * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $sort + * @psalm-taint-sink sql $order */ public function orderBy($sort, $order = null); /** * Adds an ordering to the query results. * - * @param string $sort The ordering expression. + * @param string|ILiteral|IParameter|IQueryFunction $sort The ordering expression. * @param string $order The ordering direction. * * @return $this This QueryBuilder instance. * @since 8.2.0 + * + * @psalm-taint-sink sql $sort + * @psalm-taint-sink sql $order */ public function addOrderBy($sort, $order = null); @@ -751,6 +895,8 @@ interface IQueryBuilder { * * @return mixed * @since 8.2.0 + * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update + * and we can not fix this in our wrapper. Please track the details you need, outside the object. */ public function getQueryPart($queryPartName); @@ -759,6 +905,8 @@ interface IQueryBuilder { * * @return array * @since 8.2.0 + * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update + * and we can not fix this in our wrapper. Please track the details you need, outside the object. */ public function getQueryParts(); @@ -769,6 +917,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update + * and we can not fix this in our wrapper. Please create a new IQueryBuilder instead. */ public function resetQueryParts($queryPartNames = null); @@ -779,6 +929,8 @@ interface IQueryBuilder { * * @return $this This QueryBuilder instance. * @since 8.2.0 + * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update + * and we can not fix this in our wrapper. Please create a new IQueryBuilder instead. */ public function resetQueryPart($queryPartName); @@ -806,11 +958,13 @@ interface IQueryBuilder { * @link http://www.zetacomponents.org * * @param mixed $value - * @param mixed $type + * @param self::PARAM_* $type * @param string $placeHolder The name to bind with. The string must start with a colon ':'. * * @return IParameter * @since 8.2.0 + * + * @psalm-taint-escape sql */ public function createNamedParameter($value, $type = self::PARAM_STR, $placeHolder = null); @@ -832,10 +986,12 @@ interface IQueryBuilder { * </code> * * @param mixed $value - * @param integer $type + * @param self::PARAM_* $type * * @return IParameter * @since 8.2.0 + * + * @psalm-taint-escape sql */ public function createPositionalParameter($value, $type = self::PARAM_STR); @@ -855,6 +1011,8 @@ interface IQueryBuilder { * * @return IParameter * @since 8.2.0 + * + * @psalm-taint-escape sql */ public function createParameter($name); @@ -881,6 +1039,8 @@ interface IQueryBuilder { * * @return IQueryFunction * @since 8.2.0 + * + * @psalm-taint-sink sql $call */ public function createFunction($call); @@ -890,18 +1050,30 @@ interface IQueryBuilder { * @throws \BadMethodCallException When being called before an insert query has been run. * @since 9.0.0 */ - public function getLastInsertId(); + public function getLastInsertId(): int; /** - * Returns the table name quoted and with database prefix as needed by the implementation + * Returns the table name quoted and with database prefix as needed by the implementation. + * If a query function is passed the function is casted to string, + * this allows passing functions as sub-queries for join expression. * - * @param string $table + * @param string|IQueryFunction $table * @return string * @since 9.0.0 + * @since 24.0.0 accepts IQueryFunction as parameter */ public function getTableName($table); /** + * Returns the table name with database prefix as needed by the implementation + * + * @param string $table + * @return string + * @since 30.0.0 + */ + public function prefixTableName(string $table): string; + + /** * Returns the column name quoted and with table alias prefix as needed by the implementation * * @param string $column @@ -910,4 +1082,30 @@ interface IQueryBuilder { * @since 9.0.0 */ public function getColumnName($column, $tableAlias = ''); + + /** + * Provide a hint for the shard key for queries where this can't be detected otherwise + * + * @param string $column + * @param mixed $value + * @return $this + * @since 30.0.0 + */ + public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self; + + /** + * Set the query to run across all shards if sharding is enabled. + * + * @return $this + * @since 30.0.0 + */ + public function runAcrossAllShards(): self; + + /** + * Get a list of column names that are expected in the query output + * + * @return array + * @since 30.0.0 + */ + public function getOutputColumns(): array; } diff --git a/lib/public/DB/QueryBuilder/IQueryFunction.php b/lib/public/DB/QueryBuilder/IQueryFunction.php index 22ac6081f1f..e19fc2959db 100644 --- a/lib/public/DB/QueryBuilder/IQueryFunction.php +++ b/lib/public/DB/QueryBuilder/IQueryFunction.php @@ -1,27 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCP\DB\QueryBuilder; /** diff --git a/lib/public/DB/QueryBuilder/Sharded/IShardMapper.php b/lib/public/DB/QueryBuilder/Sharded/IShardMapper.php new file mode 100644 index 00000000000..fa00fb68719 --- /dev/null +++ b/lib/public/DB/QueryBuilder/Sharded/IShardMapper.php @@ -0,0 +1,25 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2024 Robin Appelman <robin@icewind.nl> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCP\DB\QueryBuilder\Sharded; + +/** + * Implementation of logic of mapping shard keys to shards. + * @since 30.0.0 + */ +interface IShardMapper { + /** + * Get the shard number for a given shard key and total shard count + * + * @param int $key + * @param int $count + * @return int + * @since 30.0.0 + */ + public function getShardForKey(int $key, int $count): int; +} diff --git a/lib/public/DB/Types.php b/lib/public/DB/Types.php index 2fb1c13a529..969ec5e6611 100644 --- a/lib/public/DB/Types.php +++ b/lib/public/DB/Types.php @@ -2,27 +2,10 @@ declare(strict_types=1); -/* - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCP\DB; /** @@ -58,18 +41,77 @@ final class Types { public const BOOLEAN = 'boolean'; /** + * A datetime instance with only the date set. + * This will be (de)serialized into a \DateTime instance, + * it is recommended to instead use the `DATE_IMMUTABLE` instead. + * + * Warning: When deserialized the timezone will be set to UTC. * @var string * @since 21.0.0 */ public const DATE = 'date'; /** + * An immutable datetime instance with only the date set. + * This will be (de)serialized into a \DateTimeImmutable instance, + * It is recommended to use this over the `DATE` type because + * out `Entity` class works detecting changes through the setter, + * changes on mutable objects can not be detected. + * + * Warning: When deserialized the timezone will be set to UTC. + * @var string + * @since 31.0.0 + */ + public const DATE_IMMUTABLE = 'date_immutable'; + + /** + * A datetime instance with date and time support. + * This will be (de)serialized into a \DateTime instance, + * it is recommended to instead use the `DATETIME_IMMUTABLE` instead. + * + * Warning: When deserialized the timezone will be set to UTC. * @var string * @since 21.0.0 */ public const DATETIME = 'datetime'; /** + * An immutable datetime instance with date and time set. + * This will be (de)serialized into a \DateTimeImmutable instance, + * It is recommended to use this over the `DATETIME` type because + * out `Entity` class works detecting changes through the setter, + * changes on mutable objects can not be detected. + * + * Warning: When deserialized the timezone will be set to UTC. + * @var string + * @since 31.0.0 + */ + public const DATETIME_IMMUTABLE = 'datetime_immutable'; + + + /** + * A datetime instance with timezone support + * This will be (de)serialized into a \DateTime instance, + * it is recommended to instead use the `DATETIME_TZ_IMMUTABLE` instead. + * + * @var string + * @since 31.0.0 + */ + public const DATETIME_TZ = 'datetimetz'; + + /** + * An immutable timezone aware datetime instance with date and time set. + * This will be (de)serialized into a \DateTimeImmutable instance, + * It is recommended to use this over the `DATETIME_TZ` type because + * out `Entity` class works detecting changes through the setter, + * changes on mutable objects can not be detected. + * + * @var string + * @since 31.0.0 + */ + public const DATETIME_TZ_IMMUTABLE = 'datetimetz_immutable'; + + /** * @var string * @since 21.0.0 */ @@ -106,8 +148,32 @@ final class Types { public const TEXT = 'text'; /** + * A datetime instance with only the time set. + * This will be (de)serialized into a \DateTime instance, + * it is recommended to instead use the `TIME_IMMUTABLE` instead. + * + * Warning: When deserialized the timezone will be set to UTC. * @var string * @since 21.0.0 */ public const TIME = 'time'; + + /** + * A datetime instance with only the time set. + * This will be (de)serialized into a \DateTime instance. + * + * It is recommended to use this over the `DATETIME_TZ` type because + * out `Entity` class works detecting changes through the setter, + * changes on mutable objects can not be detected. + * + * @var string + * @since 31.0.0 + */ + public const TIME_IMMUTABLE = 'time_immutable'; + + /** + * @var string + * @since 24.0.0 + */ + public const JSON = 'json'; } |