From d09f835dcac66347204ada3def3b8ea9d837f0ab Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Fri, 15 Apr 2016 19:46:34 +0200 Subject: Move \OC\DB to PSR-4 Besides the statement wrapper that is moved to the legacy folder (namepsace of shame folder) --- lib/private/DB/Adapter.php | 95 ++ lib/private/DB/AdapterMySQL.php | 31 + lib/private/DB/AdapterOCI8.php | 50 + lib/private/DB/AdapterPgSql.php | 37 + lib/private/DB/AdapterSqlite.php | 74 ++ lib/private/DB/Connection.php | 373 +++++++ lib/private/DB/ConnectionFactory.php | 184 ++++ lib/private/DB/MDB2SchemaManager.php | 178 ++++ lib/private/DB/MDB2SchemaReader.php | 359 +++++++ lib/private/DB/MDB2SchemaWriter.php | 178 ++++ lib/private/DB/MigrationException.php | 40 + lib/private/DB/Migrator.php | 292 +++++ lib/private/DB/MySQLMigrator.php | 51 + lib/private/DB/NoCheckMigrator.php | 38 + lib/private/DB/OCSqlitePlatform.php | 48 + lib/private/DB/OracleConnection.php | 93 ++ lib/private/DB/OracleMigrator.php | 74 ++ lib/private/DB/PgSqlTools.php | 70 ++ .../DB/QueryBuilder/CompositeExpression.php | 92 ++ .../ExpressionBuilder/ExpressionBuilder.php | 370 +++++++ .../ExpressionBuilder/MySqlExpressionBuilder.php | 40 + .../ExpressionBuilder/OCIExpressionBuilder.php | 163 +++ .../ExpressionBuilder/PgSqlExpressionBuilder.php | 56 + lib/private/DB/QueryBuilder/Literal.php | 40 + lib/private/DB/QueryBuilder/Parameter.php | 40 + lib/private/DB/QueryBuilder/QueryBuilder.php | 1124 ++++++++++++++++++++ lib/private/DB/QueryBuilder/QueryFunction.php | 40 + lib/private/DB/QueryBuilder/QuoteHelper.php | 75 ++ lib/private/DB/SQLiteMigrator.php | 81 ++ lib/private/DB/SQLiteSessionInit.php | 65 ++ lib/private/db/adapter.php | 95 -- lib/private/db/adaptermysql.php | 31 - lib/private/db/adapteroci8.php | 50 - lib/private/db/adapterpgsql.php | 37 - lib/private/db/adaptersqlite.php | 74 -- lib/private/db/connection.php | 373 ------- lib/private/db/connectionfactory.php | 184 ---- lib/private/db/mdb2schemamanager.php | 178 ---- lib/private/db/mdb2schemareader.php | 359 ------- lib/private/db/mdb2schemawriter.php | 178 ---- lib/private/db/migrationexception.php | 40 - lib/private/db/migrator.php | 292 ----- lib/private/db/mysqlmigrator.php | 51 - lib/private/db/nocheckmigrator.php | 38 - lib/private/db/ocsqliteplatform.php | 48 - lib/private/db/oracleconnection.php | 93 -- lib/private/db/oraclemigrator.php | 74 -- lib/private/db/pgsqltools.php | 70 -- .../db/querybuilder/compositeexpression.php | 92 -- .../expressionbuilder/expressionbuilder.php | 370 ------- .../expressionbuilder/mysqlexpressionbuilder.php | 40 - .../expressionbuilder/ociexpressionbuilder.php | 163 --- .../expressionbuilder/pgsqlexpressionbuilder.php | 56 - lib/private/db/querybuilder/literal.php | 40 - lib/private/db/querybuilder/parameter.php | 40 - lib/private/db/querybuilder/querybuilder.php | 1124 -------------------- lib/private/db/querybuilder/queryfunction.php | 40 - lib/private/db/querybuilder/quotehelper.php | 75 -- lib/private/db/sqlitemigrator.php | 81 -- lib/private/db/sqlitesessioninit.php | 65 -- lib/private/db/statementwrapper.php | 122 --- lib/private/legacy/db/statementwrapper.php | 122 +++ 62 files changed, 4573 insertions(+), 4573 deletions(-) create mode 100644 lib/private/DB/Adapter.php create mode 100644 lib/private/DB/AdapterMySQL.php create mode 100644 lib/private/DB/AdapterOCI8.php create mode 100644 lib/private/DB/AdapterPgSql.php create mode 100644 lib/private/DB/AdapterSqlite.php create mode 100644 lib/private/DB/Connection.php create mode 100644 lib/private/DB/ConnectionFactory.php create mode 100644 lib/private/DB/MDB2SchemaManager.php create mode 100644 lib/private/DB/MDB2SchemaReader.php create mode 100644 lib/private/DB/MDB2SchemaWriter.php create mode 100644 lib/private/DB/MigrationException.php create mode 100644 lib/private/DB/Migrator.php create mode 100644 lib/private/DB/MySQLMigrator.php create mode 100644 lib/private/DB/NoCheckMigrator.php create mode 100644 lib/private/DB/OCSqlitePlatform.php create mode 100644 lib/private/DB/OracleConnection.php create mode 100644 lib/private/DB/OracleMigrator.php create mode 100644 lib/private/DB/PgSqlTools.php create mode 100644 lib/private/DB/QueryBuilder/CompositeExpression.php create mode 100644 lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php create mode 100644 lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php create mode 100644 lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php create mode 100644 lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php create mode 100644 lib/private/DB/QueryBuilder/Literal.php create mode 100644 lib/private/DB/QueryBuilder/Parameter.php create mode 100644 lib/private/DB/QueryBuilder/QueryBuilder.php create mode 100644 lib/private/DB/QueryBuilder/QueryFunction.php create mode 100644 lib/private/DB/QueryBuilder/QuoteHelper.php create mode 100644 lib/private/DB/SQLiteMigrator.php create mode 100644 lib/private/DB/SQLiteSessionInit.php delete mode 100644 lib/private/db/adapter.php delete mode 100644 lib/private/db/adaptermysql.php delete mode 100644 lib/private/db/adapteroci8.php delete mode 100644 lib/private/db/adapterpgsql.php delete mode 100644 lib/private/db/adaptersqlite.php delete mode 100644 lib/private/db/connection.php delete mode 100644 lib/private/db/connectionfactory.php delete mode 100644 lib/private/db/mdb2schemamanager.php delete mode 100644 lib/private/db/mdb2schemareader.php delete mode 100644 lib/private/db/mdb2schemawriter.php delete mode 100644 lib/private/db/migrationexception.php delete mode 100644 lib/private/db/migrator.php delete mode 100644 lib/private/db/mysqlmigrator.php delete mode 100644 lib/private/db/nocheckmigrator.php delete mode 100644 lib/private/db/ocsqliteplatform.php delete mode 100644 lib/private/db/oracleconnection.php delete mode 100644 lib/private/db/oraclemigrator.php delete mode 100644 lib/private/db/pgsqltools.php delete mode 100644 lib/private/db/querybuilder/compositeexpression.php delete mode 100644 lib/private/db/querybuilder/expressionbuilder/expressionbuilder.php delete mode 100644 lib/private/db/querybuilder/expressionbuilder/mysqlexpressionbuilder.php delete mode 100644 lib/private/db/querybuilder/expressionbuilder/ociexpressionbuilder.php delete mode 100644 lib/private/db/querybuilder/expressionbuilder/pgsqlexpressionbuilder.php delete mode 100644 lib/private/db/querybuilder/literal.php delete mode 100644 lib/private/db/querybuilder/parameter.php delete mode 100644 lib/private/db/querybuilder/querybuilder.php delete mode 100644 lib/private/db/querybuilder/queryfunction.php delete mode 100644 lib/private/db/querybuilder/quotehelper.php delete mode 100644 lib/private/db/sqlitemigrator.php delete mode 100644 lib/private/db/sqlitesessioninit.php delete mode 100644 lib/private/db/statementwrapper.php create mode 100644 lib/private/legacy/db/statementwrapper.php (limited to 'lib') diff --git a/lib/private/DB/Adapter.php b/lib/private/DB/Adapter.php new file mode 100644 index 00000000000..9522f768c88 --- /dev/null +++ b/lib/private/DB/Adapter.php @@ -0,0 +1,95 @@ + + * @author Joas Schilling + * @author Jonny007-MKD <1-23-4-5@web.de> + * @author Morris Jobke + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +/** + * This handles the way we use to write queries, into something that can be + * handled by the database abstraction layer. + */ +class Adapter { + + /** + * @var \OC\DB\Connection $conn + */ + protected $conn; + + public function __construct($conn) { + $this->conn = $conn; + } + + /** + * @param string $table name + * @return int id of last insert statement + */ + public function lastInsertId($table) { + return $this->conn->realLastInsertId($table); + } + + /** + * @param string $statement that needs to be changed so the db can handle it + * @return string changed statement + */ + public function fixupStatement($statement) { + return $statement; + } + + /** + * Insert a row if the matching row does not exists. + * + * @param string $table The table name (will replace *PREFIX* with the actual prefix) + * @param array $input data that should be inserted into the table (column name => value) + * @param array|null $compare List of values that should be checked for "if not exists" + * If this is null or an empty array, all keys of $input will be compared + * Please note: text fields (clob) must not be used in the compare array + * @return int number of inserted rows + * @throws \Doctrine\DBAL\DBALException + */ + public function insertIfNotExist($table, $input, array $compare = null) { + if (empty($compare)) { + $compare = array_keys($input); + } + $query = 'INSERT INTO `' .$table . '` (`' + . implode('`,`', array_keys($input)) . '`) SELECT ' + . str_repeat('?,', count($input)-1).'? ' // Is there a prettier alternative? + . 'FROM `' . $table . '` WHERE '; + + $inserts = array_values($input); + foreach($compare as $key) { + $query .= '`' . $key . '`'; + if (is_null($input[$key])) { + $query .= ' IS NULL AND '; + } else { + $inserts[] = $input[$key]; + $query .= ' = ? AND '; + } + } + $query = substr($query, 0, strlen($query) - 5); + $query .= ' HAVING COUNT(*) = 0'; + + return $this->conn->executeUpdate($query, $inserts); + } +} diff --git a/lib/private/DB/AdapterMySQL.php b/lib/private/DB/AdapterMySQL.php new file mode 100644 index 00000000000..ab87c589747 --- /dev/null +++ b/lib/private/DB/AdapterMySQL.php @@ -0,0 +1,31 @@ + + * @author Robin Appelman + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + + +namespace OC\DB; + +class AdapterMySQL extends Adapter { + public function fixupStatement($statement) { + $statement = str_replace(' ILIKE ', ' COLLATE utf8_general_ci LIKE ', $statement); + return $statement; + } +} diff --git a/lib/private/DB/AdapterOCI8.php b/lib/private/DB/AdapterOCI8.php new file mode 100644 index 00000000000..970d3eefa4d --- /dev/null +++ b/lib/private/DB/AdapterOCI8.php @@ -0,0 +1,50 @@ + + * @author Morris Jobke + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + + +namespace OC\DB; + +class AdapterOCI8 extends Adapter { + public function lastInsertId($table) { + if (is_null($table)) { + throw new \InvalidArgumentException('Oracle requires a table name to be passed into lastInsertId()'); + } + if ($table !== null) { + $suffix = '_SEQ'; + $table = '"' . $table . $suffix . '"'; + } + return $this->conn->realLastInsertId($table); + } + + const UNIX_TIMESTAMP_REPLACEMENT = "(cast(sys_extract_utc(systimestamp) as date) - date'1970-01-01') * 86400"; + + public function fixupStatement($statement) { + $statement = preg_replace('( LIKE \?)', '$0 ESCAPE \'\\\'', $statement); + $statement = preg_replace('/`(\w+)` ILIKE \?/', 'REGEXP_LIKE(`$1`, \'^\' || REPLACE(?, \'%\', \'.*\') || \'$\', \'i\')', $statement); + $statement = str_replace('`', '"', $statement); + $statement = str_ireplace('NOW()', 'CURRENT_TIMESTAMP', $statement); + $statement = str_ireplace('UNIX_TIMESTAMP()', self::UNIX_TIMESTAMP_REPLACEMENT, $statement); + return $statement; + } +} diff --git a/lib/private/DB/AdapterPgSql.php b/lib/private/DB/AdapterPgSql.php new file mode 100644 index 00000000000..a7d9377a0bf --- /dev/null +++ b/lib/private/DB/AdapterPgSql.php @@ -0,0 +1,37 @@ + + * @author Morris Jobke + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + + +namespace OC\DB; + +class AdapterPgSql extends Adapter { + public function lastInsertId($table) { + return $this->conn->fetchColumn('SELECT lastval()'); + } + + const UNIX_TIMESTAMP_REPLACEMENT = 'cast(extract(epoch from current_timestamp) as integer)'; + public function fixupStatement($statement) { + $statement = str_replace( '`', '"', $statement ); + $statement = str_ireplace( 'UNIX_TIMESTAMP()', self::UNIX_TIMESTAMP_REPLACEMENT, $statement ); + return $statement; + } +} diff --git a/lib/private/DB/AdapterSqlite.php b/lib/private/DB/AdapterSqlite.php new file mode 100644 index 00000000000..d7769238abc --- /dev/null +++ b/lib/private/DB/AdapterSqlite.php @@ -0,0 +1,74 @@ + + * @author Joas Schilling + * @author Morris Jobke + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + + +namespace OC\DB; + +class AdapterSqlite extends Adapter { + public function fixupStatement($statement) { + $statement = preg_replace('( I?LIKE \?)', '$0 ESCAPE \'\\\'', $statement); + $statement = preg_replace('/`(\w+)` ILIKE \?/', 'LOWER($1) LIKE LOWER(?)', $statement); + $statement = str_replace( '`', '"', $statement ); + $statement = str_ireplace( 'NOW()', 'datetime(\'now\')', $statement ); + $statement = str_ireplace( 'UNIX_TIMESTAMP()', 'strftime(\'%s\',\'now\')', $statement ); + return $statement; + } + + /** + * Insert a row if the matching row does not exists. + * + * @param string $table The table name (will replace *PREFIX* with the actual prefix) + * @param array $input data that should be inserted into the table (column name => value) + * @param array|null $compare List of values that should be checked for "if not exists" + * If this is null or an empty array, all keys of $input will be compared + * Please note: text fields (clob) must not be used in the compare array + * @return int number of inserted rows + * @throws \Doctrine\DBAL\DBALException + */ + public function insertIfNotExist($table, $input, array $compare = null) { + if (empty($compare)) { + $compare = array_keys($input); + } + $fieldList = '`' . implode('`,`', array_keys($input)) . '`'; + $query = "INSERT INTO `$table` ($fieldList) SELECT " + . str_repeat('?,', count($input)-1).'? ' + . " WHERE NOT EXISTS (SELECT 1 FROM `$table` WHERE "; + + $inserts = array_values($input); + foreach($compare as $key) { + $query .= '`' . $key . '`'; + if (is_null($input[$key])) { + $query .= ' IS NULL AND '; + } else { + $inserts[] = $input[$key]; + $query .= ' = ? AND '; + } + } + $query = substr($query, 0, strlen($query) - 5); + $query .= ')'; + + return $this->conn->executeUpdate($query, $inserts); + } +} diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php new file mode 100644 index 00000000000..7904fab0726 --- /dev/null +++ b/lib/private/DB/Connection.php @@ -0,0 +1,373 @@ + + * @author Joas Schilling + * @author Morris Jobke + * @author Robin Appelman + * @author Robin McCorkell + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Cache\QueryCacheProfile; +use Doctrine\Common\EventManager; +use OC\DB\QueryBuilder\QueryBuilder; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\PreconditionNotMetException; + +class Connection extends \Doctrine\DBAL\Connection implements IDBConnection { + /** + * @var string $tablePrefix + */ + protected $tablePrefix; + + /** + * @var \OC\DB\Adapter $adapter + */ + protected $adapter; + + public function connect() { + try { + return parent::connect(); + } catch (DBALException $e) { + // throw a new exception to prevent leaking info from the stacktrace + throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode()); + } + } + + /** + * Returns a QueryBuilder for the connection. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder + */ + public function getQueryBuilder() { + return new QueryBuilder($this); + } + + /** + * Gets the QueryBuilder for the connection. + * + * @return \Doctrine\DBAL\Query\QueryBuilder + * @deprecated please use $this->getQueryBuilder() instead + */ + public function createQueryBuilder() { + $backtrace = $this->getCallerBacktrace(); + \OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]); + return parent::createQueryBuilder(); + } + + /** + * Gets the ExpressionBuilder for the connection. + * + * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder + * @deprecated please use $this->getQueryBuilder()->expr() instead + */ + public function getExpressionBuilder() { + $backtrace = $this->getCallerBacktrace(); + \OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]); + return parent::getExpressionBuilder(); + } + + /** + * Get the file and line that called the method where `getCallerBacktrace()` was used + * + * @return string + */ + protected function getCallerBacktrace() { + $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + + // 0 is the method where we use `getCallerBacktrace` + // 1 is the target method which uses the method we want to log + if (isset($traces[1])) { + return $traces[1]['file'] . ':' . $traces[1]['line']; + } + + return ''; + } + + /** + * @return string + */ + public function getPrefix() { + return $this->tablePrefix; + } + + /** + * Initializes a new instance of the Connection class. + * + * @param array $params The connection parameters. + * @param \Doctrine\DBAL\Driver $driver + * @param \Doctrine\DBAL\Configuration $config + * @param \Doctrine\Common\EventManager $eventManager + * @throws \Exception + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, + EventManager $eventManager = null) + { + if (!isset($params['adapter'])) { + throw new \Exception('adapter not set'); + } + if (!isset($params['tablePrefix'])) { + throw new \Exception('tablePrefix not set'); + } + parent::__construct($params, $driver, $config, $eventManager); + $this->adapter = new $params['adapter']($this); + $this->tablePrefix = $params['tablePrefix']; + + parent::setTransactionIsolation(parent::TRANSACTION_READ_COMMITTED); + } + + /** + * Prepares an SQL statement. + * + * @param string $statement The SQL statement to prepare. + * @param int $limit + * @param int $offset + * @return \Doctrine\DBAL\Driver\Statement The prepared statement. + */ + public function prepare( $statement, $limit=null, $offset=null ) { + if ($limit === -1) { + $limit = null; + } + if (!is_null($limit)) { + $platform = $this->getDatabasePlatform(); + $statement = $platform->modifyLimitQuery($statement, $limit, $offset); + } + $statement = $this->replaceTablePrefix($statement); + $statement = $this->adapter->fixupStatement($statement); + + if(\OC::$server->getSystemConfig()->getValue( 'log_query', false)) { + \OCP\Util::writeLog('core', 'DB prepare : '.$statement, \OCP\Util::DEBUG); + } + return parent::prepare($statement); + } + + /** + * Executes an, optionally parametrized, SQL query. + * + * If the query is parametrized, a prepared statement is used. + * If an SQLLogger is configured, the execution is logged. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query, if any. + * @param array $types The types the previous parameters are in. + * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp The query cache profile, optional. + * + * @return \Doctrine\DBAL\Driver\Statement The executed statement. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + $query = $this->replaceTablePrefix($query); + $query = $this->adapter->fixupStatement($query); + return parent::executeQuery($query, $params, $types, $qcp); + } + + /** + * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters + * and returns the number of affected rows. + * + * This method supports PDO binding types as well as DBAL mapping types. + * + * @param string $query The SQL query. + * @param array $params The query parameters. + * @param array $types The parameter types. + * + * @return integer The number of affected rows. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function executeUpdate($query, array $params = array(), array $types = array()) + { + $query = $this->replaceTablePrefix($query); + $query = $this->adapter->fixupStatement($query); + return parent::executeUpdate($query, $params, $types); + } + + /** + * Returns the ID of the last inserted row, or the last value from a sequence object, + * depending on the underlying driver. + * + * Note: This method may not return a meaningful or consistent result across different drivers, + * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY + * columns or sequences. + * + * @param string $seqName Name of the sequence object from which the ID should be returned. + * @return string A string representation of the last inserted ID. + */ + public function lastInsertId($seqName = null) { + if ($seqName) { + $seqName = $this->replaceTablePrefix($seqName); + } + return $this->adapter->lastInsertId($seqName); + } + + // internal use + public function realLastInsertId($seqName = null) { + return parent::lastInsertId($seqName); + } + + /** + * Insert a row if the matching row does not exists. + * + * @param string $table The table name (will replace *PREFIX* with the actual prefix) + * @param array $input data that should be inserted into the table (column name => value) + * @param array|null $compare List of values that should be checked for "if not exists" + * If this is null or an empty array, all keys of $input will be compared + * Please note: text fields (clob) must not be used in the compare array + * @return int number of inserted rows + * @throws \Doctrine\DBAL\DBALException + */ + public function insertIfNotExist($table, $input, array $compare = null) { + return $this->adapter->insertIfNotExist($table, $input, $compare); + } + + private function getType($value) { + if (is_bool($value)) { + return IQueryBuilder::PARAM_BOOL; + } else if (is_int($value)) { + return IQueryBuilder::PARAM_INT; + } else { + return IQueryBuilder::PARAM_STR; + } + } + + /** + * Insert or update a row value + * + * @param string $table + * @param array $keys (column name => value) + * @param array $values (column name => value) + * @param array $updatePreconditionValues ensure values match preconditions (column name => value) + * @return int number of new rows + * @throws \Doctrine\DBAL\DBALException + * @throws PreconditionNotMetException + */ + public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) { + try { + $insertQb = $this->getQueryBuilder(); + $insertQb->insert($table) + ->values( + array_map(function($value) use ($insertQb) { + return $insertQb->createNamedParameter($value, $this->getType($value)); + }, array_merge($keys, $values)) + ); + return $insertQb->execute(); + } catch (\Doctrine\DBAL\Exception\ConstraintViolationException $e) { + // value already exists, try update + $updateQb = $this->getQueryBuilder(); + $updateQb->update($table); + foreach ($values as $name => $value) { + $updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value))); + } + $where = $updateQb->expr()->andx(); + $whereValues = array_merge($keys, $updatePreconditionValues); + foreach ($whereValues as $name => $value) { + $where->add($updateQb->expr()->eq( + $name, + $updateQb->createNamedParameter($value, $this->getType($value)), + $this->getType($value) + )); + } + $updateQb->where($where); + $affected = $updateQb->execute(); + + if ($affected === 0 && !empty($updatePreconditionValues)) { + throw new PreconditionNotMetException(); + } + + return 0; + } + } + + /** + * returns the error code and message as a string for logging + * works with DoctrineException + * @return string + */ + public function getError() { + $msg = $this->errorCode() . ': '; + $errorInfo = $this->errorInfo(); + if (is_array($errorInfo)) { + $msg .= 'SQLSTATE = '.$errorInfo[0] . ', '; + $msg .= 'Driver Code = '.$errorInfo[1] . ', '; + $msg .= 'Driver Message = '.$errorInfo[2]; + } + return $msg; + } + + /** + * Drop a table from the database if it exists + * + * @param string $table table name without the prefix + */ + public function dropTable($table) { + $table = $this->tablePrefix . trim($table); + $schema = $this->getSchemaManager(); + if($schema->tablesExist(array($table))) { + $schema->dropTable($table); + } + } + + /** + * Check if a table exists + * + * @param string $table table name without the prefix + * @return bool + */ + public function tableExists($table){ + $table = $this->tablePrefix . trim($table); + $schema = $this->getSchemaManager(); + return $schema->tablesExist(array($table)); + } + + // internal use + /** + * @param string $statement + * @return string + */ + protected function replaceTablePrefix($statement) { + return str_replace( '*PREFIX*', $this->tablePrefix, $statement ); + } + + /** + * Check if a transaction is active + * + * @return bool + * @since 8.2.0 + */ + public function inTransaction() { + return $this->getTransactionNestingLevel() > 0; + } + + /** + * Espace a parameter to be used in a LIKE query + * + * @param string $param + * @return string + */ + public function escapeLikeParameter($param) { + return addcslashes($param, '\\_%'); + } +} diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php new file mode 100644 index 00000000000..0856d8d19c0 --- /dev/null +++ b/lib/private/DB/ConnectionFactory.php @@ -0,0 +1,184 @@ + + * @author Jörn Friedrich Dreyer + * @author Morris Jobke + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; +use Doctrine\DBAL\Event\Listeners\OracleSessionInit; +use Doctrine\DBAL\Event\Listeners\SQLSessionInit; +use Doctrine\DBAL\Event\Listeners\MysqlSessionInit; + +/** +* Takes care of creating and configuring Doctrine connections. +*/ +class ConnectionFactory { + /** + * @var array + * + * Array mapping DBMS type to default connection parameters passed to + * \Doctrine\DBAL\DriverManager::getConnection(). + */ + protected $defaultConnectionParams = array( + 'mysql' => array( + 'adapter' => '\OC\DB\AdapterMySQL', + 'charset' => 'UTF8', + 'driver' => 'pdo_mysql', + 'wrapperClass' => 'OC\DB\Connection', + ), + 'oci' => array( + 'adapter' => '\OC\DB\AdapterOCI8', + 'charset' => 'AL32UTF8', + 'driver' => 'oci8', + 'wrapperClass' => 'OC\DB\OracleConnection', + ), + 'pgsql' => array( + 'adapter' => '\OC\DB\AdapterPgSql', + 'driver' => 'pdo_pgsql', + 'wrapperClass' => 'OC\DB\Connection', + ), + 'sqlite3' => array( + 'adapter' => '\OC\DB\AdapterSqlite', + 'driver' => 'pdo_sqlite', + 'wrapperClass' => 'OC\DB\Connection', + ), + ); + + /** + * @brief Get default connection parameters for a given DBMS. + * @param string $type DBMS type + * @throws \InvalidArgumentException If $type is invalid + * @return array Default connection parameters. + */ + public function getDefaultConnectionParams($type) { + $normalizedType = $this->normalizeType($type); + if (!isset($this->defaultConnectionParams[$normalizedType])) { + throw new \InvalidArgumentException("Unsupported type: $type"); + } + $result = $this->defaultConnectionParams[$normalizedType]; + // \PDO::MYSQL_ATTR_FOUND_ROWS may not be defined, e.g. when the MySQL + // driver is missing. In this case, we won't be able to connect anyway. + if ($normalizedType === 'mysql' && defined('\PDO::MYSQL_ATTR_FOUND_ROWS')) { + $result['driverOptions'] = array( + \PDO::MYSQL_ATTR_FOUND_ROWS => true, + ); + } + return $result; + } + + /** + * @brief Get default connection parameters for a given DBMS. + * @param string $type DBMS type + * @param array $additionalConnectionParams Additional connection parameters + * @return \OC\DB\Connection + */ + public function getConnection($type, $additionalConnectionParams) { + $normalizedType = $this->normalizeType($type); + $eventManager = new \Doctrine\Common\EventManager(); + switch ($normalizedType) { + case 'mysql': + // Send "SET NAMES utf8". Only required on PHP 5.3 below 5.3.6. + // See http://stackoverflow.com/questions/4361459/php-pdo-charset-set-names#4361485 + $eventManager->addEventSubscriber(new MysqlSessionInit); + $eventManager->addEventSubscriber( + new SQLSessionInit("SET SESSION AUTOCOMMIT=1")); + break; + case 'oci': + $eventManager->addEventSubscriber(new OracleSessionInit); + break; + case 'sqlite3': + $journalMode = $additionalConnectionParams['sqlite.journal_mode']; + $additionalConnectionParams['platform'] = new OCSqlitePlatform(); + $eventManager->addEventSubscriber(new SQLiteSessionInit(true, $journalMode)); + break; + } + $connection = \Doctrine\DBAL\DriverManager::getConnection( + array_merge($this->getDefaultConnectionParams($type), $additionalConnectionParams), + new \Doctrine\DBAL\Configuration(), + $eventManager + ); + return $connection; + } + + /** + * @brief Normalize DBMS type + * @param string $type DBMS type + * @return string Normalized DBMS type + */ + public function normalizeType($type) { + return $type === 'sqlite' ? 'sqlite3' : $type; + } + + /** + * @brief Checks whether the specified DBMS type is valid. + * @return bool + */ + public function isValidType($type) { + $normalizedType = $this->normalizeType($type); + return isset($this->defaultConnectionParams[$normalizedType]); + } + + /** + * Create the connection parameters for the config + * + * @param \OC\SystemConfig $config + * @return array + */ + public function createConnectionParams($config) { + $type = $config->getValue('dbtype', 'sqlite'); + + $connectionParams = array( + 'user' => $config->getValue('dbuser', ''), + 'password' => $config->getValue('dbpassword', ''), + ); + $name = $config->getValue('dbname', 'owncloud'); + + if ($this->normalizeType($type) === 'sqlite3') { + $dataDir = $config->getValue("datadirectory", \OC::$SERVERROOT . '/data'); + $connectionParams['path'] = $dataDir . '/' . $name . '.db'; + } else { + $host = $config->getValue('dbhost', ''); + if (strpos($host, ':')) { + // Host variable may carry a port or socket. + list($host, $portOrSocket) = explode(':', $host, 2); + if (ctype_digit($portOrSocket)) { + $connectionParams['port'] = $portOrSocket; + } else { + $connectionParams['unix_socket'] = $portOrSocket; + } + } + $connectionParams['host'] = $host; + $connectionParams['dbname'] = $name; + } + + $connectionParams['tablePrefix'] = $config->getValue('dbtableprefix', 'oc_'); + $connectionParams['sqlite.journal_mode'] = $config->getValue('sqlite.journal_mode', 'WAL'); + + //additional driver options, eg. for mysql ssl + $driverOptions = $config->getValue('dbdriveroptions', null); + if ($driverOptions) { + $connectionParams['driverOptions'] = $driverOptions; + } + + return $connectionParams; + } +} diff --git a/lib/private/DB/MDB2SchemaManager.php b/lib/private/DB/MDB2SchemaManager.php new file mode 100644 index 00000000000..f73f6b4351a --- /dev/null +++ b/lib/private/DB/MDB2SchemaManager.php @@ -0,0 +1,178 @@ + + * @author Jörn Friedrich Dreyer + * @author Lukas Reschke + * @author Morris Jobke + * @author Robin Appelman + * @author Roeland Jago Douma + * @author Thomas Müller + * @author Vincent Petry + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Platforms\OraclePlatform; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Platforms\SqlitePlatform; +use OCP\IDBConnection; + +class MDB2SchemaManager { + /** @var \OC\DB\Connection $conn */ + protected $conn; + + /** + * @param IDBConnection $conn + */ + public function __construct($conn) { + $this->conn = $conn; + } + + /** + * saves database scheme to xml file + * @param string $file name of file + * @return bool + * + * TODO: write more documentation + */ + public function getDbStructure($file) { + return \OC\DB\MDB2SchemaWriter::saveSchemaToFile($file, $this->conn); + } + + /** + * Creates tables from XML file + * @param string $file file to read structure from + * @return bool + * + * TODO: write more documentation + */ + public function createDbFromStructure($file) { + $schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $this->conn->getDatabasePlatform()); + $toSchema = $schemaReader->loadSchemaFromFile($file); + return $this->executeSchemaChange($toSchema); + } + + /** + * @return \OC\DB\Migrator + */ + public function getMigrator() { + $random = \OC::$server->getSecureRandom(); + $platform = $this->conn->getDatabasePlatform(); + $config = \OC::$server->getConfig(); + $dispatcher = \OC::$server->getEventDispatcher(); + if ($platform instanceof SqlitePlatform) { + return new SQLiteMigrator($this->conn, $random, $config, $dispatcher); + } else if ($platform instanceof OraclePlatform) { + return new OracleMigrator($this->conn, $random, $config, $dispatcher); + } else if ($platform instanceof MySqlPlatform) { + return new MySQLMigrator($this->conn, $random, $config, $dispatcher); + } else if ($platform instanceof PostgreSqlPlatform) { + return new Migrator($this->conn, $random, $config, $dispatcher); + } else { + return new NoCheckMigrator($this->conn, $random, $config, $dispatcher); + } + } + + /** + * Reads database schema from file + * + * @param string $file file to read from + * @return \Doctrine\DBAL\Schema\Schema + */ + private function readSchemaFromFile($file) { + $platform = $this->conn->getDatabasePlatform(); + $schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $platform); + return $schemaReader->loadSchemaFromFile($file); + } + + /** + * update the database scheme + * @param string $file file to read structure from + * @param bool $generateSql only return the sql needed for the upgrade + * @return string|boolean + */ + public function updateDbFromStructure($file, $generateSql = false) { + $toSchema = $this->readSchemaFromFile($file); + $migrator = $this->getMigrator(); + + if ($generateSql) { + return $migrator->generateChangeScript($toSchema); + } else { + $migrator->migrate($toSchema); + return true; + } + } + + /** + * update the database scheme + * @param string $file file to read structure from + * @return boolean + */ + public function simulateUpdateDbFromStructure($file) { + $toSchema = $this->readSchemaFromFile($file); + $this->getMigrator()->checkMigrate($toSchema); + return true; + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * @return string + */ + public function generateChangeScript($schema) { + $migrator = $this->getMigrator(); + return $migrator->generateChangeScript($schema); + } + + /** + * remove all tables defined in a database structure xml file + * + * @param string $file the xml file describing the tables + */ + public function removeDBStructure($file) { + $schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $this->conn->getDatabasePlatform()); + $fromSchema = $schemaReader->loadSchemaFromFile($file); + $toSchema = clone $fromSchema; + /** @var $table \Doctrine\DBAL\Schema\Table */ + foreach ($toSchema->getTables() as $table) { + $toSchema->dropTable($table->getName()); + } + $comparator = new \Doctrine\DBAL\Schema\Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + $this->executeSchemaChange($schemaDiff); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema|\Doctrine\DBAL\Schema\SchemaDiff $schema + * @return bool + */ + private function executeSchemaChange($schema) { + $this->conn->beginTransaction(); + foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) { + $this->conn->query($sql); + } + $this->conn->commit(); + + if ($this->conn->getDatabasePlatform() instanceof SqlitePlatform) { + $this->conn->close(); + $this->conn->connect(); + } + return true; + } +} diff --git a/lib/private/DB/MDB2SchemaReader.php b/lib/private/DB/MDB2SchemaReader.php new file mode 100644 index 00000000000..375fec185cb --- /dev/null +++ b/lib/private/DB/MDB2SchemaReader.php @@ -0,0 +1,359 @@ + + * @author Joas Schilling + * @author Jörn Friedrich Dreyer + * @author Morris Jobke + * @author Oliver Gasser + * @author Robin Appelman + * @author Robin McCorkell + * @author Thomas Müller + * @author Victor Dubiniuk + * @author Vincent Petry + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\SchemaConfig; +use OCP\IConfig; + +class MDB2SchemaReader { + /** + * @var string $DBNAME + */ + protected $DBNAME; + + /** + * @var string $DBTABLEPREFIX + */ + protected $DBTABLEPREFIX; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + protected $platform; + + /** @var \Doctrine\DBAL\Schema\SchemaConfig $schemaConfig */ + protected $schemaConfig; + + /** + * @param \OCP\IConfig $config + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(IConfig $config, AbstractPlatform $platform) { + $this->platform = $platform; + $this->DBNAME = $config->getSystemValue('dbname', 'owncloud'); + $this->DBTABLEPREFIX = $config->getSystemValue('dbtableprefix', 'oc_'); + + // Oracle does not support longer index names then 30 characters. + // We use this limit for all DBs to make sure it does not cause a + // problem. + $this->schemaConfig = new SchemaConfig(); + $this->schemaConfig->setMaxIdentifierLength(30); + } + + /** + * @param string $file + * @return \Doctrine\DBAL\Schema\Schema + * @throws \DomainException + */ + public function loadSchemaFromFile($file) { + $schema = new \Doctrine\DBAL\Schema\Schema(); + $loadEntities = libxml_disable_entity_loader(false); + $xml = simplexml_load_file($file); + libxml_disable_entity_loader($loadEntities); + foreach ($xml->children() as $child) { + /** + * @var \SimpleXMLElement $child + */ + switch ($child->getName()) { + case 'name': + case 'create': + case 'overwrite': + case 'charset': + break; + case 'table': + $this->loadTable($schema, $child); + break; + default: + throw new \DomainException('Unknown element: ' . $child->getName()); + + } + } + return $schema; + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * @param \SimpleXMLElement $xml + * @throws \DomainException + */ + private function loadTable($schema, $xml) { + $table = null; + foreach ($xml->children() as $child) { + /** + * @var \SimpleXMLElement $child + */ + switch ($child->getName()) { + case 'name': + $name = (string)$child; + $name = str_replace('*dbprefix*', $this->DBTABLEPREFIX, $name); + $name = $this->platform->quoteIdentifier($name); + $table = $schema->createTable($name); + $table->addOption('collate', 'utf8_bin'); + $table->setSchemaConfig($this->schemaConfig); + break; + case 'create': + case 'overwrite': + case 'charset': + break; + case 'declaration': + if (is_null($table)) { + throw new \DomainException('Table declaration before table name'); + } + $this->loadDeclaration($table, $child); + break; + default: + throw new \DomainException('Unknown element: ' . $child->getName()); + + } + } + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \SimpleXMLElement $xml + * @throws \DomainException + */ + private function loadDeclaration($table, $xml) { + foreach ($xml->children() as $child) { + /** + * @var \SimpleXMLElement $child + */ + switch ($child->getName()) { + case 'field': + $this->loadField($table, $child); + break; + case 'index': + $this->loadIndex($table, $child); + break; + default: + throw new \DomainException('Unknown element: ' . $child->getName()); + + } + } + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \SimpleXMLElement $xml + * @throws \DomainException + */ + private function loadField($table, $xml) { + $options = array( 'notnull' => false ); + foreach ($xml->children() as $child) { + /** + * @var \SimpleXMLElement $child + */ + switch ($child->getName()) { + case 'name': + $name = (string)$child; + $name = $this->platform->quoteIdentifier($name); + break; + case 'type': + $type = (string)$child; + switch ($type) { + case 'text': + $type = 'string'; + break; + case 'clob': + $type = 'text'; + break; + case 'timestamp': + $type = 'datetime'; + break; + case 'numeric': + $type = 'decimal'; + break; + } + break; + case 'length': + $length = (string)$child; + $options['length'] = $length; + break; + case 'unsigned': + $unsigned = $this->asBool($child); + $options['unsigned'] = $unsigned; + break; + case 'notnull': + $notnull = $this->asBool($child); + $options['notnull'] = $notnull; + break; + case 'autoincrement': + $autoincrement = $this->asBool($child); + $options['autoincrement'] = $autoincrement; + break; + case 'default': + $default = (string)$child; + $options['default'] = $default; + break; + case 'comments': + $comment = (string)$child; + $options['comment'] = $comment; + break; + case 'primary': + $primary = $this->asBool($child); + $options['primary'] = $primary; + break; + case 'precision': + $precision = (string)$child; + $options['precision'] = $precision; + break; + case 'scale': + $scale = (string)$child; + $options['scale'] = $scale; + break; + default: + throw new \DomainException('Unknown element: ' . $child->getName()); + + } + } + if (isset($name) && isset($type)) { + if (isset($options['default']) && empty($options['default'])) { + if (empty($options['notnull']) || !$options['notnull']) { + unset($options['default']); + $options['notnull'] = false; + } else { + $options['default'] = ''; + } + if ($type == 'integer' || $type == 'decimal') { + $options['default'] = 0; + } elseif ($type == 'boolean') { + $options['default'] = false; + } + if (!empty($options['autoincrement']) && $options['autoincrement']) { + unset($options['default']); + } + } + if ($type === 'integer' && isset($options['default'])) { + $options['default'] = (int)$options['default']; + } + if ($type === 'integer' && isset($options['length'])) { + $length = $options['length']; + if ($length < 4) { + $type = 'smallint'; + } else if ($length > 4) { + $type = 'bigint'; + } + } + if ($type === 'boolean' && isset($options['default'])) { + $options['default'] = $this->asBool($options['default']); + } + if (!empty($options['autoincrement']) + && !empty($options['notnull']) + ) { + $options['primary'] = true; + } + $table->addColumn($name, $type, $options); + if (!empty($options['primary']) && $options['primary']) { + $table->setPrimaryKey(array($name)); + } + } + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \SimpleXMLElement $xml + * @throws \DomainException + */ + private function loadIndex($table, $xml) { + $name = null; + $fields = array(); + foreach ($xml->children() as $child) { + /** + * @var \SimpleXMLElement $child + */ + switch ($child->getName()) { + case 'name': + $name = (string)$child; + break; + case 'primary': + $primary = $this->asBool($child); + break; + case 'unique': + $unique = $this->asBool($child); + break; + case 'field': + foreach ($child->children() as $field) { + /** + * @var \SimpleXMLElement $field + */ + switch ($field->getName()) { + case 'name': + $field_name = (string)$field; + $field_name = $this->platform->quoteIdentifier($field_name); + $fields[] = $field_name; + break; + case 'sorting': + break; + default: + throw new \DomainException('Unknown element: ' . $field->getName()); + + } + } + break; + default: + throw new \DomainException('Unknown element: ' . $child->getName()); + + } + } + if (!empty($fields)) { + if (isset($primary) && $primary) { + if ($table->hasPrimaryKey()) { + return; + } + $table->setPrimaryKey($fields, $name); + } else { + if (isset($unique) && $unique) { + $table->addUniqueIndex($fields, $name); + } else { + $table->addIndex($fields, $name); + } + } + } else { + throw new \DomainException('Empty index definition: ' . $name . ' options:' . print_r($fields, true)); + } + } + + /** + * @param \SimpleXMLElement|string $xml + * @return bool + */ + private function asBool($xml) { + $result = (string)$xml; + if ($result == 'true') { + $result = true; + } elseif ($result == 'false') { + $result = false; + } + return (bool)$result; + } + +} diff --git a/lib/private/DB/MDB2SchemaWriter.php b/lib/private/DB/MDB2SchemaWriter.php new file mode 100644 index 00000000000..7dc3bd223a7 --- /dev/null +++ b/lib/private/DB/MDB2SchemaWriter.php @@ -0,0 +1,178 @@ + + * @author Jörn Friedrich Dreyer + * @author Morris Jobke + * @author tbelau666 + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\Index; + +class MDB2SchemaWriter { + + /** + * @param string $file + * @param \OC\DB\Connection $conn + * @return bool + */ + static public function saveSchemaToFile($file, \OC\DB\Connection $conn) { + $config = \OC::$server->getConfig(); + + $xml = new \SimpleXMLElement(''); + $xml->addChild('name', $config->getSystemValue('dbname', 'owncloud')); + $xml->addChild('create', 'true'); + $xml->addChild('overwrite', 'false'); + $xml->addChild('charset', 'utf8'); + + // FIX ME: bloody work around + if ($config->getSystemValue('dbtype', 'sqlite') === 'oci') { + $filterExpression = '/^"' . preg_quote($conn->getPrefix()) . '/'; + } else { + $filterExpression = '/^' . preg_quote($conn->getPrefix()) . '/'; + } + $conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); + + foreach ($conn->getSchemaManager()->listTables() as $table) { + self::saveTable($table, $xml->addChild('table')); + } + file_put_contents($file, $xml->asXML()); + return true; + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \SimpleXMLElement $xml + */ + private static function saveTable($table, $xml) { + $xml->addChild('name', $table->getName()); + $declaration = $xml->addChild('declaration'); + foreach($table->getColumns() as $column) { + self::saveColumn($column, $declaration->addChild('field')); + } + foreach($table->getIndexes() as $index) { + if ($index->getName() == 'PRIMARY') { + $autoincrement = false; + foreach($index->getColumns() as $column) { + if ($table->getColumn($column)->getAutoincrement()) { + $autoincrement = true; + } + } + if ($autoincrement) { + continue; + } + } + self::saveIndex($index, $declaration->addChild('index')); + } + } + + /** + * @param Column $column + * @param \SimpleXMLElement $xml + */ + private static function saveColumn($column, $xml) { + $xml->addChild('name', $column->getName()); + switch($column->getType()) { + case 'SmallInt': + case 'Integer': + case 'BigInt': + $xml->addChild('type', 'integer'); + $default = $column->getDefault(); + if (is_null($default) && $column->getAutoincrement()) { + $default = '0'; + } + $xml->addChild('default', $default); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + if ($column->getAutoincrement()) { + $xml->addChild('autoincrement', '1'); + } + if ($column->getUnsigned()) { + $xml->addChild('unsigned', 'true'); + } + $length = '4'; + if ($column->getType() == 'SmallInt') { + $length = '2'; + } + elseif ($column->getType() == 'BigInt') { + $length = '8'; + } + $xml->addChild('length', $length); + break; + case 'String': + $xml->addChild('type', 'text'); + $default = trim($column->getDefault()); + if ($default === '') { + $default = false; + } + $xml->addChild('default', $default); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + $xml->addChild('length', $column->getLength()); + break; + case 'Text': + $xml->addChild('type', 'clob'); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + break; + case 'Decimal': + $xml->addChild('type', 'decimal'); + $xml->addChild('default', $column->getDefault()); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + $xml->addChild('length', '15'); + break; + case 'Boolean': + $xml->addChild('type', 'integer'); + $xml->addChild('default', $column->getDefault()); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + $xml->addChild('length', '1'); + break; + case 'DateTime': + $xml->addChild('type', 'timestamp'); + $xml->addChild('default', $column->getDefault()); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + break; + + } + } + + /** + * @param Index $index + * @param \SimpleXMLElement $xml + */ + private static function saveIndex($index, $xml) { + $xml->addChild('name', $index->getName()); + if ($index->isPrimary()) { + $xml->addChild('primary', 'true'); + } + elseif ($index->isUnique()) { + $xml->addChild('unique', 'true'); + } + foreach($index->getColumns() as $column) { + $field = $xml->addChild('field'); + $field->addChild('name', $column); + $field->addChild('sorting', 'ascending'); + + } + } + + private static function toBool($bool) { + return $bool ? 'true' : 'false'; + } +} diff --git a/lib/private/DB/MigrationException.php b/lib/private/DB/MigrationException.php new file mode 100644 index 00000000000..57e4c5b8334 --- /dev/null +++ b/lib/private/DB/MigrationException.php @@ -0,0 +1,40 @@ + + * @author Robin Appelman + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + + +class MigrationException extends \Exception { + private $table; + + public function __construct($table, $message) { + $this->table = $table; + parent::__construct($message); + } + + /** + * @return string + */ + public function getTable() { + return $this->table; + } +} diff --git a/lib/private/DB/Migrator.php b/lib/private/DB/Migrator.php new file mode 100644 index 00000000000..8b8a34d9865 --- /dev/null +++ b/lib/private/DB/Migrator.php @@ -0,0 +1,292 @@ + + * @author Morris Jobke + * @author Robin Appelman + * @author tbelau666 + * @author Thomas Müller + * @author Victor Dubiniuk + * @author Vincent Petry + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +use \Doctrine\DBAL\DBALException; +use \Doctrine\DBAL\Schema\Index; +use \Doctrine\DBAL\Schema\Table; +use \Doctrine\DBAL\Schema\Schema; +use \Doctrine\DBAL\Schema\SchemaConfig; +use \Doctrine\DBAL\Schema\Comparator; +use OCP\IConfig; +use OCP\Security\ISecureRandom; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\GenericEvent; + +class Migrator { + + /** + * @var \Doctrine\DBAL\Connection $connection + */ + protected $connection; + + /** + * @var ISecureRandom + */ + private $random; + + /** @var IConfig */ + protected $config; + + /** @var EventDispatcher */ + private $dispatcher; + + /** @var bool */ + private $noEmit = false; + + /** + * @param \Doctrine\DBAL\Connection|Connection $connection + * @param ISecureRandom $random + * @param IConfig $config + * @param EventDispatcher $dispatcher + */ + public function __construct(\Doctrine\DBAL\Connection $connection, + ISecureRandom $random, + IConfig $config, + EventDispatcher $dispatcher = null) { + $this->connection = $connection; + $this->random = $random; + $this->config = $config; + $this->dispatcher = $dispatcher; + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + */ + public function migrate(Schema $targetSchema) { + $this->noEmit = true; + $this->applySchema($targetSchema); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + * @return string + */ + public function generateChangeScript(Schema $targetSchema) { + $schemaDiff = $this->getDiff($targetSchema, $this->connection); + + $script = ''; + $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform()); + foreach ($sqls as $sql) { + $script .= $this->convertStatementToScript($sql); + } + + return $script; + } + + /** + * @param Schema $targetSchema + * @throws \OC\DB\MigrationException + */ + public function checkMigrate(Schema $targetSchema) { + $this->noEmit = true; + /**@var \Doctrine\DBAL\Schema\Table[] $tables */ + $tables = $targetSchema->getTables(); + $filterExpression = $this->getFilterExpression(); + $this->connection->getConfiguration()-> + setFilterSchemaAssetsExpression($filterExpression); + $existingTables = $this->connection->getSchemaManager()->listTableNames(); + + $step = 0; + foreach ($tables as $table) { + if (strpos($table->getName(), '.')) { + list(, $tableName) = explode('.', $table->getName()); + } else { + $tableName = $table->getName(); + } + $this->emitCheckStep($tableName, $step++, count($tables)); + // don't need to check for new tables + if (array_search($tableName, $existingTables) !== false) { + $this->checkTableMigrate($table); + } + } + } + + /** + * Create a unique name for the temporary table + * + * @param string $name + * @return string + */ + protected function generateTemporaryTableName($name) { + return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); + } + + /** + * Check the migration of a table on a copy so we can detect errors before messing with the real table + * + * @param \Doctrine\DBAL\Schema\Table $table + * @throws \OC\DB\MigrationException + */ + protected function checkTableMigrate(Table $table) { + $name = $table->getName(); + $tmpName = $this->generateTemporaryTableName($name); + + $this->copyTable($name, $tmpName); + + //create the migration schema for the temporary table + $tmpTable = $this->renameTableSchema($table, $tmpName); + $schemaConfig = new SchemaConfig(); + $schemaConfig->setName($this->connection->getDatabase()); + $schema = new Schema(array($tmpTable), array(), $schemaConfig); + + try { + $this->applySchema($schema); + $this->dropTable($tmpName); + } catch (DBALException $e) { + // pgsql needs to commit it's failed transaction before doing anything else + if ($this->connection->isTransactionActive()) { + $this->connection->commit(); + } + $this->dropTable($tmpName); + throw new MigrationException($table->getName(), $e->getMessage()); + } + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param string $newName + * @return \Doctrine\DBAL\Schema\Table + */ + protected function renameTableSchema(Table $table, $newName) { + /** + * @var \Doctrine\DBAL\Schema\Index[] $indexes + */ + $indexes = $table->getIndexes(); + $newIndexes = array(); + foreach ($indexes as $index) { + if ($index->isPrimary()) { + // do not rename primary key + $indexName = $index->getName(); + } else { + // avoid conflicts in index names + $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER); + } + $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary()); + } + + // foreign keys are not supported so we just set it to an empty array + return new Table($newName, $table->getColumns(), $newIndexes, array(), 0, $table->getOptions()); + } + + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { + $filterExpression = $this->getFilterExpression(); + $this->connection->getConfiguration()-> + setFilterSchemaAssetsExpression($filterExpression); + $sourceSchema = $connection->getSchemaManager()->createSchema(); + + // remove tables we don't know about + /** @var $table \Doctrine\DBAL\Schema\Table */ + foreach ($sourceSchema->getTables() as $table) { + if (!$targetSchema->hasTable($table->getName())) { + $sourceSchema->dropTable($table->getName()); + } + } + // remove sequences we don't know about + foreach ($sourceSchema->getSequences() as $table) { + if (!$targetSchema->hasSequence($table->getName())) { + $sourceSchema->dropSequence($table->getName()); + } + } + + $comparator = new Comparator(); + return $comparator->compare($sourceSchema, $targetSchema); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + * @param \Doctrine\DBAL\Connection $connection + */ + protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { + if (is_null($connection)) { + $connection = $this->connection; + } + + $schemaDiff = $this->getDiff($targetSchema, $connection); + + $connection->beginTransaction(); + $sqls = $schemaDiff->toSql($connection->getDatabasePlatform()); + $step = 0; + foreach ($sqls as $sql) { + $this->emit($sql, $step++, count($sqls)); + $connection->query($sql); + } + $connection->commit(); + } + + /** + * @param string $sourceName + * @param string $targetName + */ + protected function copyTable($sourceName, $targetName) { + $quotedSource = $this->connection->quoteIdentifier($sourceName); + $quotedTarget = $this->connection->quoteIdentifier($targetName); + + $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')'); + $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource); + } + + /** + * @param string $name + */ + protected function dropTable($name) { + $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name)); + } + + /** + * @param $statement + * @return string + */ + protected function convertStatementToScript($statement) { + $script = $statement . ';'; + $script .= PHP_EOL; + $script .= PHP_EOL; + return $script; + } + + protected function getFilterExpression() { + return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; + } + + protected function emit($sql, $step, $max) { + if ($this->noEmit) { + return; + } + if(is_null($this->dispatcher)) { + return; + } + $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max])); + } + + private function emitCheckStep($tableName, $step, $max) { + if(is_null($this->dispatcher)) { + return; + } + $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step+1, $max])); + } +} diff --git a/lib/private/DB/MySQLMigrator.php b/lib/private/DB/MySQLMigrator.php new file mode 100644 index 00000000000..1b3f70a817d --- /dev/null +++ b/lib/private/DB/MySQLMigrator.php @@ -0,0 +1,51 @@ + + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +use Doctrine\DBAL\Schema\Schema; + +class MySQLMigrator extends Migrator { + /** + * @param Schema $targetSchema + * @param \Doctrine\DBAL\Connection $connection + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { + $platform = $connection->getDatabasePlatform(); + $platform->registerDoctrineTypeMapping('enum', 'string'); + $platform->registerDoctrineTypeMapping('bit', 'string'); + + $schemaDiff = parent::getDiff($targetSchema, $connection); + + // identifiers need to be quoted for mysql + foreach ($schemaDiff->changedTables as $tableDiff) { + $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); + foreach ($tableDiff->changedColumns as $column) { + $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); + } + } + + return $schemaDiff; + } +} diff --git a/lib/private/DB/NoCheckMigrator.php b/lib/private/DB/NoCheckMigrator.php new file mode 100644 index 00000000000..23afae11816 --- /dev/null +++ b/lib/private/DB/NoCheckMigrator.php @@ -0,0 +1,38 @@ + + * @author Robin Appelman + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +use Doctrine\DBAL\Schema\Schema; + +/** + * migrator for database platforms that don't support the upgrade check + * + * @package OC\DB + */ +class NoCheckMigrator extends Migrator { + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + * @throws \OC\DB\MigrationException + */ + public function checkMigrate(Schema $targetSchema) {} +} diff --git a/lib/private/DB/OCSqlitePlatform.php b/lib/private/DB/OCSqlitePlatform.php new file mode 100644 index 00000000000..fe68bc3cc91 --- /dev/null +++ b/lib/private/DB/OCSqlitePlatform.php @@ -0,0 +1,48 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +class OCSqlitePlatform extends \Doctrine\DBAL\Platforms\SqlitePlatform { + /** + * {@inheritDoc} + */ + public function getColumnDeclarationSQL($name, array $field) { + $def = parent::getColumnDeclarationSQL($name, $field); + if (!empty($field['autoincrement'])) { + $def .= ' PRIMARY KEY AUTOINCREMENT'; + } + return $def; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = array()){ + // if auto increment is set the column is already defined as primary key + foreach ($columns as $column) { + if (!empty($column['autoincrement'])) { + $options['primary'] = null; + } + } + return parent::_getCreateTableSQL($name, $columns, $options); + } +} diff --git a/lib/private/DB/OracleConnection.php b/lib/private/DB/OracleConnection.php new file mode 100644 index 00000000000..a95f37a8e6c --- /dev/null +++ b/lib/private/DB/OracleConnection.php @@ -0,0 +1,93 @@ + + * @author Lukas Reschke + * @author Morris Jobke + * @author Robin Appelman + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +class OracleConnection extends Connection { + /** + * Quote the keys of the array + */ + private function quoteKeys(array $data) { + $return = array(); + foreach($data as $key => $value) { + $return[$this->quoteIdentifier($key)] = $value; + } + return $return; + } + + /** + * {@inheritDoc} + */ + public function insert($tableName, array $data, array $types = array()) { + $tableName = $this->quoteIdentifier($tableName); + $data = $this->quoteKeys($data); + return parent::insert($tableName, $data, $types); + } + + /** + * {@inheritDoc} + */ + public function update($tableName, array $data, array $identifier, array $types = array()) { + $tableName = $this->quoteIdentifier($tableName); + $data = $this->quoteKeys($data); + $identifier = $this->quoteKeys($identifier); + return parent::update($tableName, $data, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function delete($tableExpression, array $identifier, array $types = array()) { + $tableName = $this->quoteIdentifier($tableExpression); + $identifier = $this->quoteKeys($identifier); + return parent::delete($tableName, $identifier); + } + + /** + * Drop a table from the database if it exists + * + * @param string $table table name without the prefix + */ + public function dropTable($table) { + $table = $this->tablePrefix . trim($table); + $table = $this->quoteIdentifier($table); + $schema = $this->getSchemaManager(); + if($schema->tablesExist(array($table))) { + $schema->dropTable($table); + } + } + + /** + * Check if a table exists + * + * @param string $table table name without the prefix + * @return bool + */ + public function tableExists($table){ + $table = $this->tablePrefix . trim($table); + $table = $this->quoteIdentifier($table); + $schema = $this->getSchemaManager(); + return $schema->tablesExist(array($table)); + } +} diff --git a/lib/private/DB/OracleMigrator.php b/lib/private/DB/OracleMigrator.php new file mode 100644 index 00000000000..ceb89cf64d4 --- /dev/null +++ b/lib/private/DB/OracleMigrator.php @@ -0,0 +1,74 @@ + + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +use Doctrine\DBAL\Schema\Schema; + +class OracleMigrator extends NoCheckMigrator { + /** + * @param Schema $targetSchema + * @param \Doctrine\DBAL\Connection $connection + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { + $schemaDiff = parent::getDiff($targetSchema, $connection); + + // oracle forces us to quote the identifiers + foreach ($schemaDiff->changedTables as $tableDiff) { + $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); + foreach ($tableDiff->changedColumns as $column) { + $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); + } + } + + return $schemaDiff; + } + + /** + * @param string $name + * @return string + */ + protected function generateTemporaryTableName($name) { + return 'oc_' . uniqid(); + } + + /** + * @param $statement + * @return string + */ + protected function convertStatementToScript($statement) { + if (substr($statement, -1) === ';') { + return $statement . PHP_EOL . '/' . PHP_EOL; + } + $script = $statement . ';'; + $script .= PHP_EOL; + $script .= PHP_EOL; + return $script; + } + + protected function getFilterExpression() { + return '/^"' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; + } + +} diff --git a/lib/private/DB/PgSqlTools.php b/lib/private/DB/PgSqlTools.php new file mode 100644 index 00000000000..e9e507551e6 --- /dev/null +++ b/lib/private/DB/PgSqlTools.php @@ -0,0 +1,70 @@ + + * @author Morris Jobke + * @author tbelau666 + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; +use OCP\IConfig; + +/** +* Various PostgreSQL specific helper functions. +*/ +class PgSqlTools { + + /** @var \OCP\IConfig */ + private $config; + + /** + * @param \OCP\IConfig $config + */ + public function __construct(IConfig $config) { + $this->config = $config; + } + + /** + * @brief Resynchronizes all sequences of a database after using INSERTs + * without leaving out the auto-incremented column. + * @param \OC\DB\Connection $conn + * @return null + */ + public function resynchronizeDatabaseSequences(Connection $conn) { + $filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; + $databaseName = $conn->getDatabase(); + $conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); + + foreach ($conn->getSchemaManager()->listSequences() as $sequence) { + $sequenceName = $sequence->getName(); + $sqlInfo = 'SELECT table_schema, table_name, column_name + FROM information_schema.columns + WHERE column_default = ? AND table_catalog = ?'; + $sequenceInfo = $conn->fetchAssoc($sqlInfo, array( + "nextval('$sequenceName'::regclass)", + $databaseName + )); + $tableName = $sequenceInfo['table_name']; + $columnName = $sequenceInfo['column_name']; + $sqlMaxId = "SELECT MAX($columnName) FROM $tableName"; + $sqlSetval = "SELECT setval('$sequenceName', ($sqlMaxId))"; + $conn->executeQuery($sqlSetval); + } + } +} diff --git a/lib/private/DB/QueryBuilder/CompositeExpression.php b/lib/private/DB/QueryBuilder/CompositeExpression.php new file mode 100644 index 00000000000..927dfe38378 --- /dev/null +++ b/lib/private/DB/QueryBuilder/CompositeExpression.php @@ -0,0 +1,92 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB\QueryBuilder; + +use OCP\DB\QueryBuilder\ICompositeExpression; + +class CompositeExpression implements ICompositeExpression, \Countable { + /** @var \Doctrine\DBAL\Query\Expression\CompositeExpression */ + protected $compositeExpression; + + /** + * Constructor. + * + * @param \Doctrine\DBAL\Query\Expression\CompositeExpression $compositeExpression + */ + public function __construct(\Doctrine\DBAL\Query\Expression\CompositeExpression $compositeExpression) { + $this->compositeExpression = $compositeExpression; + } + + /** + * Adds multiple parts to composite expression. + * + * @param array $parts + * + * @return \OCP\DB\QueryBuilder\ICompositeExpression + */ + public function addMultiple(array $parts = array()) { + $this->compositeExpression->addMultiple($parts); + + return $this; + } + + /** + * Adds an expression to composite expression. + * + * @param mixed $part + * + * @return \OCP\DB\QueryBuilder\ICompositeExpression + */ + public function add($part) { + $this->compositeExpression->add($part); + + return $this; + } + + /** + * Retrieves the amount of expressions on composite expression. + * + * @return integer + */ + public function count() { + return $this->compositeExpression->count(); + } + + /** + * Returns the type of this composite expression (AND/OR). + * + * @return string + */ + public function getType() { + return $this->compositeExpression->getType(); + } + + /** + * Retrieves the string representation of this composite expression. + * + * @return string + */ + public function __toString() + { + return (string) $this->compositeExpression; + } +} diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php new file mode 100644 index 00000000000..ce98816c42d --- /dev/null +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php @@ -0,0 +1,370 @@ + + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB\QueryBuilder\ExpressionBuilder; + +use Doctrine\DBAL\Query\Expression\ExpressionBuilder as DoctrineExpressionBuilder; +use OC\DB\QueryBuilder\CompositeExpression; +use OC\DB\QueryBuilder\Literal; +use OC\DB\QueryBuilder\QueryFunction; +use OC\DB\QueryBuilder\QuoteHelper; +use OCP\DB\QueryBuilder\IExpressionBuilder; +use OCP\IDBConnection; + +class ExpressionBuilder implements IExpressionBuilder { + /** @var \Doctrine\DBAL\Query\Expression\ExpressionBuilder */ + protected $expressionBuilder; + + /** @var QuoteHelper */ + protected $helper; + + /** + * Initializes a new ExpressionBuilder. + * + * @param \OCP\IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->helper = new QuoteHelper(); + $this->expressionBuilder = new DoctrineExpressionBuilder($connection); + } + + /** + * Creates a conjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?) AND (u.role = ?) + * $expr->andX('u.type = ?', 'u.role = ?')); + * + * @param mixed $x Optional clause. Defaults = null, but requires + * at least one defined when converting to string. + * + * @return \OCP\DB\QueryBuilder\ICompositeExpression + */ + public function andX($x = null) { + $arguments = func_get_args(); + $compositeExpression = call_user_func_array([$this->expressionBuilder, 'andX'], $arguments); + return new CompositeExpression($compositeExpression); + } + + /** + * Creates a disjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?) OR (u.role = ?) + * $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. + * + * @return \OCP\DB\QueryBuilder\ICompositeExpression + */ + public function orX($x = null) { + $arguments = func_get_args(); + $compositeExpression = call_user_func_array([$this->expressionBuilder, 'orX'], $arguments); + return new CompositeExpression($compositeExpression); + } + + /** + * Creates a comparison expression. + * + * @param mixed $x The left expression. + * @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 + * + * @return string + */ + public function comparison($x, $operator, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->comparison($x, $operator, $y); + } + + /** + * Creates an equality comparison expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a = . Example: + * + * [php] + * // u.id = ? + * $expr->eq('u.id', '?'); + * + * @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 + * + * @return string + */ + public function eq($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->eq($x, $y); + } + + /** + * Creates a non equality comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <> . Example: + * + * [php] + * // u.id <> 1 + * $q->where($q->expr()->neq('u.id', '1')); + * + * @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 + * + * @return string + */ + public function neq($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->neq($x, $y); + } + + /** + * Creates a lower-than comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a < . Example: + * + * [php] + * // u.id < ? + * $q->where($q->expr()->lt('u.id', '?')); + * + * @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 + * + * @return string + */ + public function lt($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->lt($x, $y); + } + + /** + * Creates a lower-than-equal comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <= . Example: + * + * [php] + * // u.id <= ? + * $q->where($q->expr()->lte('u.id', '?')); + * + * @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 + * + * @return string + */ + public function lte($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->lte($x, $y); + } + + /** + * Creates a greater-than comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a > . Example: + * + * [php] + * // u.id > ? + * $q->where($q->expr()->gt('u.id', '?')); + * + * @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 + * + * @return string + */ + public function gt($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->gt($x, $y); + } + + /** + * Creates a greater-than-equal comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a >= . Example: + * + * [php] + * // u.id >= ? + * $q->where($q->expr()->gte('u.id', '?')); + * + * @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 + * + * @return string + */ + public function gte($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->gte($x, $y); + } + + /** + * Creates an IS NULL expression with the given arguments. + * + * @param string $x The field in string format to be restricted by IS NULL. + * + * @return string + */ + public function isNull($x) { + $x = $this->helper->quoteColumnName($x); + return $this->expressionBuilder->isNull($x); + } + + /** + * 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. + * + * @return string + */ + public function isNotNull($x) { + $x = $this->helper->quoteColumnName($x); + return $this->expressionBuilder->isNotNull($x); + } + + /** + * Creates a LIKE() comparison expression with the given arguments. + * + * @param 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 + * + * @return string + */ + public function like($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->like($x, $y); + } + + /** + * Creates a ILIKE() comparison expression with the given arguments. + * + * @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 + * + * @return string + * @since 9.0.0 + */ + public function iLike($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->comparison("LOWER($x)", 'LIKE', "LOWER($y)"); + } + + /** + * Creates a NOT LIKE() comparison expression with the given arguments. + * + * @param 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 + * + * @return string + */ + public function notLike($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->notLike($x, $y); + } + + /** + * Creates a IN () comparison expression with the given arguments. + * + * @param string $x The field in string format to be inspected by IN() comparison. + * @param 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 + * + * @return string + */ + public function in($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnNames($y); + return $this->expressionBuilder->in($x, $y); + } + + /** + * Creates a NOT IN () comparison expression with the given arguments. + * + * @param string $x The field in string format to be inspected by NOT IN() comparison. + * @param 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 + * + * @return string + */ + public function notIn($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnNames($y); + return $this->expressionBuilder->notIn($x, $y); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input The parameter to be quoted. + * @param mixed|null $type One of the IQueryBuilder::PARAM_* constants + * + * @return Literal + */ + public function literal($input, $type = null) { + return new Literal($this->expressionBuilder->literal($input, $type)); + } + + /** + * Returns a IQueryFunction that casts the column to the given type + * + * @param string $column + * @param mixed $type One of IQueryBuilder::PARAM_* + * @return string + */ + public function castColumn($column, $type) { + return new QueryFunction( + $this->helper->quoteColumnName($column) + ); + } +} diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php new file mode 100644 index 00000000000..0d34787d26a --- /dev/null +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php @@ -0,0 +1,40 @@ + + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB\QueryBuilder\ExpressionBuilder; + + +use OC\DB\QueryBuilder\QueryFunction; +use OCP\DB\QueryBuilder\IQueryBuilder; + +class MySqlExpressionBuilder extends ExpressionBuilder { + + /** + * @inheritdoc + */ + public function iLike($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->comparison($x, ' COLLATE utf8_general_ci LIKE', $y); + } + +} diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php new file mode 100644 index 00000000000..5d615ae52e8 --- /dev/null +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php @@ -0,0 +1,163 @@ + + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB\QueryBuilder\ExpressionBuilder; + + +use OC\DB\QueryBuilder\QueryFunction; +use OCP\DB\QueryBuilder\ILiteral; +use OCP\DB\QueryBuilder\IParameter; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DB\QueryBuilder\IQueryFunction; + +class OCIExpressionBuilder extends ExpressionBuilder { + + /** + * @param mixed $column + * @param mixed|null $type + * @return array|IQueryFunction|string + */ + protected function prepareColumn($column, $type) { + if ($type === IQueryBuilder::PARAM_STR && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) { + $column = $this->castColumn($column, $type); + } else { + $column = $this->helper->quoteColumnNames($column); + } + return $column; + } + + /** + * @inheritdoc + */ + public function comparison($x, $operator, $y, $type = null) { + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); + + return $this->expressionBuilder->comparison($x, $operator, $y); + } + + /** + * @inheritdoc + */ + public function eq($x, $y, $type = null) { + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); + + return $this->expressionBuilder->eq($x, $y); + } + + /** + * @inheritdoc + */ + public function neq($x, $y, $type = null) { + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); + + return $this->expressionBuilder->neq($x, $y); + } + + /** + * @inheritdoc + */ + public function lt($x, $y, $type = null) { + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); + + return $this->expressionBuilder->lt($x, $y); + } + + /** + * @inheritdoc + */ + public function lte($x, $y, $type = null) { + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); + + return $this->expressionBuilder->lte($x, $y); + } + + /** + * @inheritdoc + */ + public function gt($x, $y, $type = null) { + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); + + return $this->expressionBuilder->gt($x, $y); + } + + /** + * @inheritdoc + */ + public function gte($x, $y, $type = null) { + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); + + return $this->expressionBuilder->gte($x, $y); + } + + /** + * @inheritdoc + */ + public function in($x, $y, $type = null) { + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); + + return $this->expressionBuilder->in($x, $y); + } + + /** + * @inheritdoc + */ + public function notIn($x, $y, $type = null) { + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); + + return $this->expressionBuilder->notIn($x, $y); + } + + /** + * Returns a IQueryFunction that casts the column to the given type + * + * @param string $column + * @param mixed $type One of IQueryBuilder::PARAM_* + * @return IQueryFunction + */ + public function castColumn($column, $type) { + if ($type === IQueryBuilder::PARAM_STR) { + $column = $this->helper->quoteColumnName($column); + return new QueryFunction('to_char(' . $column . ')'); + } + + return parent::castColumn($column, $type); + } + + /** + * @inheritdoc + */ + public function iLike($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return new QueryFunction('REGEXP_LIKE('.$x.', \'^\' || REPLACE('.$y.', \'%\', \'.*\') || \'$\', \'i\')'); + } +} diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php new file mode 100644 index 00000000000..a8f1af87a45 --- /dev/null +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php @@ -0,0 +1,56 @@ + + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB\QueryBuilder\ExpressionBuilder; + + +use OC\DB\QueryBuilder\QueryFunction; +use OCP\DB\QueryBuilder\IQueryBuilder; + +class PgSqlExpressionBuilder extends ExpressionBuilder { + + /** + * Returns a IQueryFunction that casts the column to the given type + * + * @param string $column + * @param mixed $type One of IQueryBuilder::PARAM_* + * @return string + */ + public function castColumn($column, $type) { + if ($type === IQueryBuilder::PARAM_INT) { + $column = $this->helper->quoteColumnName($column); + return new QueryFunction('CAST(' . $column . ' AS INT)'); + } + + return parent::castColumn($column, $type); + } + + /** + * @inheritdoc + */ + public function iLike($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + return $this->expressionBuilder->comparison($x, 'ILIKE', $y); + } + +} diff --git a/lib/private/DB/QueryBuilder/Literal.php b/lib/private/DB/QueryBuilder/Literal.php new file mode 100644 index 00000000000..0cc96ab48b4 --- /dev/null +++ b/lib/private/DB/QueryBuilder/Literal.php @@ -0,0 +1,40 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB\QueryBuilder; + +use OCP\DB\QueryBuilder\ILiteral; + +class Literal implements ILiteral{ + /** @var mixed */ + protected $literal; + + public function __construct($literal) { + $this->literal = $literal; + } + + /** + * @return string + */ + public function __toString() { + return (string) $this->literal; + } +} diff --git a/lib/private/DB/QueryBuilder/Parameter.php b/lib/private/DB/QueryBuilder/Parameter.php new file mode 100644 index 00000000000..1c233c83f1d --- /dev/null +++ b/lib/private/DB/QueryBuilder/Parameter.php @@ -0,0 +1,40 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB\QueryBuilder; + +use OCP\DB\QueryBuilder\IParameter; + +class Parameter implements IParameter { + /** @var mixed */ + protected $name; + + public function __construct($name) { + $this->name = $name; + } + + /** + * @return string + */ + public function __toString() { + return (string) $this->name; + } +} diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php new file mode 100644 index 00000000000..bb463e43a75 --- /dev/null +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -0,0 +1,1124 @@ + + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB\QueryBuilder; + +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use OC\DB\OracleConnection; +use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder; +use OC\DB\QueryBuilder\ExpressionBuilder\MySqlExpressionBuilder; +use OC\DB\QueryBuilder\ExpressionBuilder\OCIExpressionBuilder; +use OC\DB\QueryBuilder\ExpressionBuilder\PgSqlExpressionBuilder; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DB\QueryBuilder\IQueryFunction; +use OCP\DB\QueryBuilder\IParameter; +use OCP\IDBConnection; + +class QueryBuilder implements IQueryBuilder { + + /** @var \OCP\IDBConnection */ + private $connection; + + /** @var \Doctrine\DBAL\Query\QueryBuilder */ + private $queryBuilder; + + /** @var QuoteHelper */ + private $helper; + + /** @var bool */ + private $automaticTablePrefix = true; + + /** @var string */ + protected $lastInsertedTable; + + /** + * Initializes a new QueryBuilder. + * + * @param \OCP\IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection); + $this->helper = new QuoteHelper(); + } + + /** + * 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. + * @since 8.2.0 + */ + public function automaticTablePrefix($enabled) { + $this->automaticTablePrefix = (bool) $enabled; + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * This producer method is intended for convenient inline usage. Example: + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where($qb->expr()->eq('u.id', 1)); + * + * + * For more complex expression construction, consider storing the expression + * builder object in a local variable. + * + * @return \OCP\DB\QueryBuilder\IExpressionBuilder + */ + public function expr() { + if ($this->connection instanceof OracleConnection) { + return new OCIExpressionBuilder($this->connection); + } else if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { + return new PgSqlExpressionBuilder($this->connection); + } else if ($this->connection->getDatabasePlatform() instanceof MySqlPlatform) { + return new MySqlExpressionBuilder($this->connection); + } else { + return new ExpressionBuilder($this->connection); + } + } + + /** + * Gets the type of the currently built query. + * + * @return integer + */ + public function getType() { + return $this->queryBuilder->getType(); + } + + /** + * Gets the associated DBAL Connection for this query builder. + * + * @return \OCP\IDBConnection + */ + public function getConnection() { + return $this->connection; + } + + /** + * Gets the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() { + return $this->queryBuilder->getState(); + } + + /** + * Executes this query using the bound parameters and their types. + * + * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} + * for insert, update and delete statements. + * + * @return \Doctrine\DBAL\Driver\Statement|int + */ + public function execute() { + return $this->queryBuilder->execute(); + } + + /** + * Gets the complete SQL string formed by the current specifications of this QueryBuilder. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * echo $qb->getSQL(); // SELECT u FROM User u + * + * + * @return string The SQL query string. + */ + public function getSQL() { + return $this->queryBuilder->getSQL(); + } + + /** + * Sets a query parameter for the query being constructed. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.id = :user_id') + * ->setParameter(':user_id', 1); + * + * + * @param string|integer $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|null $type One of the IQueryBuilder::PARAM_* constants. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function setParameter($key, $value, $type = null) { + $this->queryBuilder->setParameter($key, $value, $type); + + return $this; + } + + /** + * Sets a collection of query parameters for the query being constructed. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.id = :user_id1 OR u.id = :user_id2') + * ->setParameters(array( + * ':user_id1' => 1, + * ':user_id2' => 2 + * )); + * + * + * @param array $params The query parameters to set. + * @param array $types The query parameters types to set. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function setParameters(array $params, array $types = array()) { + $this->queryBuilder->setParameters($params, $types); + + return $this; + } + + /** + * Gets all defined query parameters for the query being constructed indexed by parameter index or name. + * + * @return array The currently defined query parameters indexed by parameter index or name. + */ + public function getParameters() { + return $this->queryBuilder->getParameters(); + } + + /** + * Gets a (previously set) query parameter of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter. + * + * @return mixed The value of the bound parameter. + */ + public function getParameter($key) { + return $this->queryBuilder->getParameter($key); + } + + /** + * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. + * + * @return array The currently defined query parameter types indexed by parameter index or name. + */ + public function getParameterTypes() { + return $this->queryBuilder->getParameterTypes(); + } + + /** + * Gets a (previously set) query parameter type of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter type. + * + * @return mixed The value of the bound parameter type. + */ + public function getParameterType($key) { + return $this->queryBuilder->getParameterType($key); + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function setFirstResult($firstResult) { + $this->queryBuilder->setFirstResult($firstResult); + + return $this; + } + + /** + * 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. + * + * @return integer The position of the first result. + */ + public function getFirstResult() { + return $this->queryBuilder->getFirstResult(); + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * NOTE: Setting max results to "0" will cause mixed behaviour. While most + * of the databases will just return an empty result set, Oracle will return + * all entries. + * + * @param integer $maxResults The maximum number of results to retrieve. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function setMaxResults($maxResults) { + $this->queryBuilder->setMaxResults($maxResults); + + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query builder. + * + * @return integer The maximum number of results. + */ + public function getMaxResults() { + return $this->queryBuilder->getMaxResults(); + } + + /** + * Specifies an item that is to be returned in the query result. + * Replaces any previously specified selections, if any. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.id', 'p.id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expressions. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function select($select = null) { + $selects = is_array($select) ? $select : func_get_args(); + + $this->queryBuilder->select( + $this->helper->quoteColumnNames($selects) + ); + + return $this; + } + + /** + * Specifies an item that is to be returned with a different name in the query result. + * + * + * $qb = $conn->getQueryBuilder() + * ->selectAlias('u.id', 'user_id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expressions. + * @param string $alias The column alias used in the constructed query. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function selectAlias($select, $alias) { + + $this->queryBuilder->addSelect( + $this->helper->quoteColumnName($select) . ' AS ' . $this->helper->quoteColumnName($alias) + ); + + return $this; + } + + /** + * Specifies an item that is to be returned uniquely in the query result. + * + * + * $qb = $conn->getQueryBuilder() + * ->selectDistinct('type') + * ->from('users'); + * + * + * @param mixed $select The selection expressions. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function selectDistinct($select) { + + $this->queryBuilder->addSelect( + 'DISTINCT ' . $this->helper->quoteColumnName($select) + ); + + return $this; + } + + /** + * Adds an item that is to be returned in the query result. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.id') + * ->addSelect('p.id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expression. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function addSelect($select = null) { + $selects = is_array($select) ? $select : func_get_args(); + + $this->queryBuilder->addSelect( + $this->helper->quoteColumnNames($selects) + ); + + return $this; + } + + /** + * Turns the query being built into a bulk delete query that ranges over + * a certain table. + * + * + * $qb = $conn->getQueryBuilder() + * ->delete('users', 'u') + * ->where('u.id = :user_id'); + * ->setParameter(':user_id', 1); + * + * + * @param string $delete The table whose rows are subject to the deletion. + * @param string $alias The table alias used in the constructed query. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function delete($delete = null, $alias = null) { + $this->queryBuilder->delete( + $this->getTableName($delete), + $alias + ); + + return $this; + } + + /** + * Turns the query being built into a bulk update query that ranges over + * a certain table + * + * + * $qb = $conn->getQueryBuilder() + * ->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $update The table whose rows are subject to the update. + * @param string $alias The table alias used in the constructed query. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function update($update = null, $alias = null) { + $this->queryBuilder->update( + $this->getTableName($update), + $alias + ); + + return $this; + } + + /** + * Turns the query being built into an insert query that inserts into + * a certain table + * + * + * $qb = $conn->getQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?', + * 'password' => '?' + * ) + * ); + * + * + * @param string $insert The table into which the rows should be inserted. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function insert($insert = null) { + $this->queryBuilder->insert( + $this->getTableName($insert) + ); + + $this->lastInsertedTable = $insert; + + return $this; + } + + /** + * Creates and adds a query root corresponding to the table identified by the + * given alias, forming a cartesian product with any existing query roots. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.id') + * ->from('users', 'u') + * + * + * @param string $from The table. + * @param string|null $alias The alias of the table. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function from($from, $alias = null) { + $this->queryBuilder->from( + $this->getTableName($from), + $alias + ); + + return $this; + } + + /** + * Creates and adds a join to the query. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @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. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function join($fromAlias, $join, $alias, $condition = null) { + $this->queryBuilder->join( + $fromAlias, + $this->getTableName($join), + $alias, + $condition + ); + + return $this; + } + + /** + * Creates and adds a join to the query. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @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. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function innerJoin($fromAlias, $join, $alias, $condition = null) { + $this->queryBuilder->innerJoin( + $fromAlias, + $this->getTableName($join), + $alias, + $condition + ); + + return $this; + } + + /** + * Creates and adds a left join to the query. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @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. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function leftJoin($fromAlias, $join, $alias, $condition = null) { + $this->queryBuilder->leftJoin( + $fromAlias, + $this->getTableName($join), + $alias, + $condition + ); + + return $this; + } + + /** + * Creates and adds a right join to the query. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @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. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function rightJoin($fromAlias, $join, $alias, $condition = null) { + $this->queryBuilder->rightJoin( + $fromAlias, + $this->getTableName($join), + $alias, + $condition + ); + + return $this; + } + + /** + * Sets a new value for a column in a bulk update query. + * + * + * $qb = $conn->getQueryBuilder() + * ->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $key The column to set. + * @param string $value The value, expression, placeholder, etc. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function set($key, $value) { + $this->queryBuilder->set( + $this->helper->quoteColumnName($key), + $this->helper->quoteColumnName($value) + ); + + return $this; + } + + /** + * Specifies one or more restrictions to the query result. + * Replaces any previously specified restrictions, if any. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->where('u.id = ?'); + * + * // You can optionally programatically 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)); + * + * $qb->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where($or); + * + * + * @param mixed $predicates The restriction predicates. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function where($predicates) { + call_user_func_array( + [$this->queryBuilder, 'where'], + func_get_args() + ); + + return $this; + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * conjunction with any previously specified restrictions. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.username LIKE ?') + * ->andWhere('u.is_active = 1'); + * + * + * @param mixed $where The query restrictions. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * + * @see where() + */ + public function andWhere($where) { + call_user_func_array( + [$this->queryBuilder, 'andWhere'], + func_get_args() + ); + + return $this; + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * disjunction with any previously specified restrictions. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->where('u.id = 1') + * ->orWhere('u.id = 2'); + * + * + * @param mixed $where The WHERE statement. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * + * @see where() + */ + public function orWhere($where) { + call_user_func_array( + [$this->queryBuilder, 'orWhere'], + func_get_args() + ); + + return $this; + } + + /** + * Specifies a grouping over the results of the query. + * Replaces any previously specified groupings, if any. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->groupBy('u.id'); + * + * + * @param mixed $groupBy The grouping expression. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function groupBy($groupBy) { + $groupBys = is_array($groupBy) ? $groupBy : func_get_args(); + + call_user_func_array( + [$this->queryBuilder, 'groupBy'], + $this->helper->quoteColumnNames($groupBys) + ); + + return $this; + } + + /** + * Adds a grouping expression to the query. + * + * + * $qb = $conn->getQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->groupBy('u.lastLogin'); + * ->addGroupBy('u.createdAt') + * + * + * @param mixed $groupBy The grouping expression. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function addGroupBy($groupBy) { + $groupBys = is_array($groupBy) ? $groupBy : func_get_args(); + + call_user_func_array( + [$this->queryBuilder, 'addGroupBy'], + $this->helper->quoteColumnNames($groupBys) + ); + + return $this; + } + + /** + * Sets a value for a column in an insert query. + * + * + * $qb = $conn->getQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?' + * ) + * ) + * ->setValue('password', '?'); + * + * + * @param string $column The column into which the value should be inserted. + * @param string $value The value that should be inserted into the column. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function setValue($column, $value) { + $this->queryBuilder->setValue( + $this->helper->quoteColumnName($column), + $value + ); + + return $this; + } + + /** + * Specifies values for an insert query indexed by column names. + * Replaces any previous values, if any. + * + * + * $qb = $conn->getQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?', + * 'password' => '?' + * ) + * ); + * + * + * @param array $values The values to specify for the insert query indexed by column names. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function values(array $values) { + $quotedValues = []; + foreach ($values as $key => $value) { + $quotedValues[$this->helper->quoteColumnName($key)] = $value; + } + + $this->queryBuilder->values($quotedValues); + + return $this; + } + + /** + * Specifies a restriction over the groups of the query. + * Replaces any previous having restrictions, if any. + * + * @param mixed $having The restriction over the groups. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function having($having) { + call_user_func_array( + [$this->queryBuilder, 'having'], + func_get_args() + ); + + return $this; + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * conjunction with any existing having restrictions. + * + * @param mixed $having The restriction to append. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function andHaving($having) { + call_user_func_array( + [$this->queryBuilder, 'andHaving'], + func_get_args() + ); + + return $this; + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * disjunction with any existing having restrictions. + * + * @param mixed $having The restriction to add. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function orHaving($having) { + call_user_func_array( + [$this->queryBuilder, 'orHaving'], + func_get_args() + ); + + return $this; + } + + /** + * Specifies an ordering for the query results. + * Replaces any previously specified orderings, if any. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function orderBy($sort, $order = null) { + $this->queryBuilder->orderBy( + $this->helper->quoteColumnName($sort), + $order + ); + + return $this; + } + + /** + * Adds an ordering to the query results. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) { + $this->queryBuilder->addOrderBy( + $this->helper->quoteColumnName($sort), + $order + ); + + return $this; + } + + /** + * Gets a query part by its name. + * + * @param string $queryPartName + * + * @return mixed + */ + public function getQueryPart($queryPartName) { + return $this->queryBuilder->getQueryPart($queryPartName); + } + + /** + * Gets all query parts. + * + * @return array + */ + public function getQueryParts() { + return $this->queryBuilder->getQueryParts(); + } + + /** + * Resets SQL parts. + * + * @param array|null $queryPartNames + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function resetQueryParts($queryPartNames = null) { + $this->queryBuilder->resetQueryParts($queryPartNames); + + return $this; + } + + /** + * Resets a single SQL part. + * + * @param string $queryPartName + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function resetQueryPart($queryPartName) { + $this->queryBuilder->resetQueryPart($queryPartName); + + return $this; + } + + /** + * Creates a new named parameter and bind the value $value to it. + * + * This method provides a shortcut for PDOStatement::bindValue + * when using prepared statements. + * + * The parameter $value specifies the value that you want to bind. If + * $placeholder is not provided bindValue() will automatically create a + * placeholder for you. An automatic placeholder will be of the name + * ':dcValue1', ':dcValue2' etc. + * + * For more information see {@link http://php.net/pdostatement-bindparam} + * + * Example: + * + * $value = 2; + * $q->eq( 'id', $q->bindValue( $value ) ); + * $stmt = $q->executeQuery(); // executed with 'id = 2' + * + * + * @license New BSD License + * @link http://www.zetacomponents.org + * + * @param mixed $value + * @param mixed $type + * @param string $placeHolder The name to bind with. The string must start with a colon ':'. + * + * @return IParameter the placeholder name used. + */ + public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $placeHolder = null) { + return new Parameter($this->queryBuilder->createNamedParameter($value, $type, $placeHolder)); + } + + /** + * Creates a new positional parameter and bind the given value to it. + * + * Attention: If you are using positional parameters with the query builder you have + * to be very careful to bind all parameters in the order they appear in the SQL + * statement , otherwise they get bound in the wrong order which can lead to serious + * bugs in your code. + * + * Example: + * + * $qb = $conn->getQueryBuilder(); + * $qb->select('u.*') + * ->from('users', 'u') + * ->where('u.username = ' . $qb->createPositionalParameter('Foo', IQueryBuilder::PARAM_STR)) + * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', IQueryBuilder::PARAM_STR)) + * + * + * @param mixed $value + * @param integer $type + * + * @return IParameter + */ + public function createPositionalParameter($value, $type = IQueryBuilder::PARAM_STR) { + return new Parameter($this->queryBuilder->createPositionalParameter($value, $type)); + } + + /** + * Creates a new parameter + * + * Example: + * + * $qb = $conn->getQueryBuilder(); + * $qb->select('u.*') + * ->from('users', 'u') + * ->where('u.username = ' . $qb->createParameter('name')) + * ->setParameter('name', 'Bar', IQueryBuilder::PARAM_STR)) + * + * + * @param string $name + * + * @return IParameter + */ + public function createParameter($name) { + return new Parameter(':' . $name); + } + + /** + * Creates a new function + * + * Attention: Column names inside the call have to be quoted before hand + * + * Example: + * + * $qb = $conn->getQueryBuilder(); + * $qb->select($qb->createFunction('COUNT(*)')) + * ->from('users', 'u') + * echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u + * + * + * $qb = $conn->getQueryBuilder(); + * $qb->select($qb->createFunction('COUNT(`column`)')) + * ->from('users', 'u') + * echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u + * + * + * @param string $call + * + * @return IQueryFunction + */ + public function createFunction($call) { + return new QueryFunction($call); + } + + /** + * Used to get the id of the last inserted element + * @return int + * @throws \BadMethodCallException When being called before an insert query has been run. + */ + public function getLastInsertId() { + if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable) { + // lastInsertId() needs the prefix but no quotes + $table = $this->prefixTableName($this->lastInsertedTable); + return (int) $this->connection->lastInsertId($table); + } + + throw new \BadMethodCallException('Invalid call to getLastInsertId without using insert() before.'); + } + + /** + * Returns the table name quoted and with database prefix as needed by the implementation + * + * @param string $table + * @return string + */ + public function getTableName($table) { + $table = $this->prefixTableName($table); + return $this->helper->quoteColumnName($table); + } + + /** + * Returns the table name with database prefix as needed by the implementation + * + * @param string $table + * @return string + */ + protected function prefixTableName($table) { + if ($this->automaticTablePrefix === false || strpos($table, '*PREFIX*') === 0) { + return $table; + } + + return '*PREFIX*' . $table; + } + + /** + * Returns the column name quoted and with table alias prefix as needed by the implementation + * + * @param string $column + * @param string $tableAlias + * @return string + */ + public function getColumnName($column, $tableAlias = '') { + if ($tableAlias !== '') { + $tableAlias .= '.'; + } + + return $this->helper->quoteColumnName($tableAlias . $column); + } +} diff --git a/lib/private/DB/QueryBuilder/QueryFunction.php b/lib/private/DB/QueryBuilder/QueryFunction.php new file mode 100644 index 00000000000..ac6d73f3cbf --- /dev/null +++ b/lib/private/DB/QueryBuilder/QueryFunction.php @@ -0,0 +1,40 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB\QueryBuilder; + +use OCP\DB\QueryBuilder\IQueryFunction; + +class QueryFunction implements IQueryFunction { + /** @var string */ + protected $function; + + public function __construct($function) { + $this->function = $function; + } + + /** + * @return string + */ + public function __toString() { + return (string) $this->function; + } +} diff --git a/lib/private/DB/QueryBuilder/QuoteHelper.php b/lib/private/DB/QueryBuilder/QuoteHelper.php new file mode 100644 index 00000000000..fda243a3786 --- /dev/null +++ b/lib/private/DB/QueryBuilder/QuoteHelper.php @@ -0,0 +1,75 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB\QueryBuilder; + +use OCP\DB\QueryBuilder\ILiteral; +use OCP\DB\QueryBuilder\IParameter; +use OCP\DB\QueryBuilder\IQueryFunction; + +class QuoteHelper { + /** + * @param array|string|ILiteral|IParameter|IQueryFunction $strings string, Literal or Parameter + * @return array|string + */ + public function quoteColumnNames($strings) { + if (!is_array($strings)) { + return $this->quoteColumnName($strings); + } + + $return = []; + foreach ($strings as $string) { + $return[] = $this->quoteColumnName($string); + } + + return $return; + } + + /** + * @param string|ILiteral|IParameter|IQueryFunction $string string, Literal or Parameter + * @return string + */ + public function quoteColumnName($string) { + if ($string instanceof IParameter || $string instanceof ILiteral || $string instanceof IQueryFunction) { + return (string) $string; + } + + if ($string === null || $string === 'null' || $string === '*') { + return $string; + } + + if (!is_string($string)) { + throw new \InvalidArgumentException('Only strings, Literals and Parameters are allowed'); + } + + if (substr_count($string, '.')) { + list($alias, $columnName) = explode('.', $string, 2); + + if ($columnName === '*') { + return $string; + } + + return $alias . '.`' . $columnName . '`'; + } + + return '`' . $string . '`'; + } +} diff --git a/lib/private/DB/SQLiteMigrator.php b/lib/private/DB/SQLiteMigrator.php new file mode 100644 index 00000000000..8ea32581011 --- /dev/null +++ b/lib/private/DB/SQLiteMigrator.php @@ -0,0 +1,81 @@ + + * @author Robin Appelman + * @author Thomas Müller + * @author Victor Dubiniuk + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Schema; + +class SQLiteMigrator extends Migrator { + + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + * @throws \OC\DB\MigrationException + * + * For sqlite we simple make a copy of the entire database, and test the migration on that + */ + public function checkMigrate(\Doctrine\DBAL\Schema\Schema $targetSchema) { + $dbFile = $this->connection->getDatabase(); + $tmpFile = $this->buildTempDatabase(); + copy($dbFile, $tmpFile); + + $connectionParams = array( + 'path' => $tmpFile, + 'driver' => 'pdo_sqlite', + ); + $conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); + try { + $this->applySchema($targetSchema, $conn); + $conn->close(); + unlink($tmpFile); + } catch (DBALException $e) { + $conn->close(); + unlink($tmpFile); + throw new MigrationException('', $e->getMessage()); + } + } + + /** + * @return string + */ + private function buildTempDatabase() { + $dataDir = $this->config->getSystemValue("datadirectory", \OC::$SERVERROOT . '/data'); + $tmpFile = uniqid("oc_"); + return "$dataDir/$tmpFile.db"; + } + + /** + * @param Schema $targetSchema + * @param \Doctrine\DBAL\Connection $connection + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { + $platform = $connection->getDatabasePlatform(); + $platform->registerDoctrineTypeMapping('tinyint unsigned', 'integer'); + $platform->registerDoctrineTypeMapping('smallint unsigned', 'integer'); + $platform->registerDoctrineTypeMapping('varchar ', 'string'); + + return parent::getDiff($targetSchema, $connection); + } +} diff --git a/lib/private/DB/SQLiteSessionInit.php b/lib/private/DB/SQLiteSessionInit.php new file mode 100644 index 00000000000..0683c47d08e --- /dev/null +++ b/lib/private/DB/SQLiteSessionInit.php @@ -0,0 +1,65 @@ + + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OC\DB; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +class SQLiteSessionInit implements EventSubscriber { + /** + * @var bool + */ + private $caseSensitiveLike; + + /** + * @var string + */ + private $journalMode; + + /** + * Configure case sensitive like for each connection + * + * @param bool $caseSensitiveLike + * @param string $journalMode + */ + public function __construct($caseSensitiveLike, $journalMode) { + $this->caseSensitiveLike = $caseSensitiveLike; + $this->journalMode = $journalMode; + } + + /** + * @param ConnectionEventArgs $args + * @return void + */ + public function postConnect(ConnectionEventArgs $args) { + $sensitive = ($this->caseSensitiveLike) ? 'true' : 'false'; + $args->getConnection()->executeUpdate('PRAGMA case_sensitive_like = ' . $sensitive); + $args->getConnection()->executeUpdate('PRAGMA journal_mode = ' . $this->journalMode); + } + + public function getSubscribedEvents() { + return array(Events::postConnect); + } +} diff --git a/lib/private/db/adapter.php b/lib/private/db/adapter.php deleted file mode 100644 index 9522f768c88..00000000000 --- a/lib/private/db/adapter.php +++ /dev/null @@ -1,95 +0,0 @@ - - * @author Joas Schilling - * @author Jonny007-MKD <1-23-4-5@web.de> - * @author Morris Jobke - * @author Robin Appelman - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -/** - * This handles the way we use to write queries, into something that can be - * handled by the database abstraction layer. - */ -class Adapter { - - /** - * @var \OC\DB\Connection $conn - */ - protected $conn; - - public function __construct($conn) { - $this->conn = $conn; - } - - /** - * @param string $table name - * @return int id of last insert statement - */ - public function lastInsertId($table) { - return $this->conn->realLastInsertId($table); - } - - /** - * @param string $statement that needs to be changed so the db can handle it - * @return string changed statement - */ - public function fixupStatement($statement) { - return $statement; - } - - /** - * Insert a row if the matching row does not exists. - * - * @param string $table The table name (will replace *PREFIX* with the actual prefix) - * @param array $input data that should be inserted into the table (column name => value) - * @param array|null $compare List of values that should be checked for "if not exists" - * If this is null or an empty array, all keys of $input will be compared - * Please note: text fields (clob) must not be used in the compare array - * @return int number of inserted rows - * @throws \Doctrine\DBAL\DBALException - */ - public function insertIfNotExist($table, $input, array $compare = null) { - if (empty($compare)) { - $compare = array_keys($input); - } - $query = 'INSERT INTO `' .$table . '` (`' - . implode('`,`', array_keys($input)) . '`) SELECT ' - . str_repeat('?,', count($input)-1).'? ' // Is there a prettier alternative? - . 'FROM `' . $table . '` WHERE '; - - $inserts = array_values($input); - foreach($compare as $key) { - $query .= '`' . $key . '`'; - if (is_null($input[$key])) { - $query .= ' IS NULL AND '; - } else { - $inserts[] = $input[$key]; - $query .= ' = ? AND '; - } - } - $query = substr($query, 0, strlen($query) - 5); - $query .= ' HAVING COUNT(*) = 0'; - - return $this->conn->executeUpdate($query, $inserts); - } -} diff --git a/lib/private/db/adaptermysql.php b/lib/private/db/adaptermysql.php deleted file mode 100644 index ab87c589747..00000000000 --- a/lib/private/db/adaptermysql.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @author Robin Appelman - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - - -namespace OC\DB; - -class AdapterMySQL extends Adapter { - public function fixupStatement($statement) { - $statement = str_replace(' ILIKE ', ' COLLATE utf8_general_ci LIKE ', $statement); - return $statement; - } -} diff --git a/lib/private/db/adapteroci8.php b/lib/private/db/adapteroci8.php deleted file mode 100644 index 970d3eefa4d..00000000000 --- a/lib/private/db/adapteroci8.php +++ /dev/null @@ -1,50 +0,0 @@ - - * @author Morris Jobke - * @author Robin Appelman - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - - -namespace OC\DB; - -class AdapterOCI8 extends Adapter { - public function lastInsertId($table) { - if (is_null($table)) { - throw new \InvalidArgumentException('Oracle requires a table name to be passed into lastInsertId()'); - } - if ($table !== null) { - $suffix = '_SEQ'; - $table = '"' . $table . $suffix . '"'; - } - return $this->conn->realLastInsertId($table); - } - - const UNIX_TIMESTAMP_REPLACEMENT = "(cast(sys_extract_utc(systimestamp) as date) - date'1970-01-01') * 86400"; - - public function fixupStatement($statement) { - $statement = preg_replace('( LIKE \?)', '$0 ESCAPE \'\\\'', $statement); - $statement = preg_replace('/`(\w+)` ILIKE \?/', 'REGEXP_LIKE(`$1`, \'^\' || REPLACE(?, \'%\', \'.*\') || \'$\', \'i\')', $statement); - $statement = str_replace('`', '"', $statement); - $statement = str_ireplace('NOW()', 'CURRENT_TIMESTAMP', $statement); - $statement = str_ireplace('UNIX_TIMESTAMP()', self::UNIX_TIMESTAMP_REPLACEMENT, $statement); - return $statement; - } -} diff --git a/lib/private/db/adapterpgsql.php b/lib/private/db/adapterpgsql.php deleted file mode 100644 index a7d9377a0bf..00000000000 --- a/lib/private/db/adapterpgsql.php +++ /dev/null @@ -1,37 +0,0 @@ - - * @author Morris Jobke - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - - -namespace OC\DB; - -class AdapterPgSql extends Adapter { - public function lastInsertId($table) { - return $this->conn->fetchColumn('SELECT lastval()'); - } - - const UNIX_TIMESTAMP_REPLACEMENT = 'cast(extract(epoch from current_timestamp) as integer)'; - public function fixupStatement($statement) { - $statement = str_replace( '`', '"', $statement ); - $statement = str_ireplace( 'UNIX_TIMESTAMP()', self::UNIX_TIMESTAMP_REPLACEMENT, $statement ); - return $statement; - } -} diff --git a/lib/private/db/adaptersqlite.php b/lib/private/db/adaptersqlite.php deleted file mode 100644 index d7769238abc..00000000000 --- a/lib/private/db/adaptersqlite.php +++ /dev/null @@ -1,74 +0,0 @@ - - * @author Joas Schilling - * @author Morris Jobke - * @author Robin Appelman - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - - -namespace OC\DB; - -class AdapterSqlite extends Adapter { - public function fixupStatement($statement) { - $statement = preg_replace('( I?LIKE \?)', '$0 ESCAPE \'\\\'', $statement); - $statement = preg_replace('/`(\w+)` ILIKE \?/', 'LOWER($1) LIKE LOWER(?)', $statement); - $statement = str_replace( '`', '"', $statement ); - $statement = str_ireplace( 'NOW()', 'datetime(\'now\')', $statement ); - $statement = str_ireplace( 'UNIX_TIMESTAMP()', 'strftime(\'%s\',\'now\')', $statement ); - return $statement; - } - - /** - * Insert a row if the matching row does not exists. - * - * @param string $table The table name (will replace *PREFIX* with the actual prefix) - * @param array $input data that should be inserted into the table (column name => value) - * @param array|null $compare List of values that should be checked for "if not exists" - * If this is null or an empty array, all keys of $input will be compared - * Please note: text fields (clob) must not be used in the compare array - * @return int number of inserted rows - * @throws \Doctrine\DBAL\DBALException - */ - public function insertIfNotExist($table, $input, array $compare = null) { - if (empty($compare)) { - $compare = array_keys($input); - } - $fieldList = '`' . implode('`,`', array_keys($input)) . '`'; - $query = "INSERT INTO `$table` ($fieldList) SELECT " - . str_repeat('?,', count($input)-1).'? ' - . " WHERE NOT EXISTS (SELECT 1 FROM `$table` WHERE "; - - $inserts = array_values($input); - foreach($compare as $key) { - $query .= '`' . $key . '`'; - if (is_null($input[$key])) { - $query .= ' IS NULL AND '; - } else { - $inserts[] = $input[$key]; - $query .= ' = ? AND '; - } - } - $query = substr($query, 0, strlen($query) - 5); - $query .= ')'; - - return $this->conn->executeUpdate($query, $inserts); - } -} diff --git a/lib/private/db/connection.php b/lib/private/db/connection.php deleted file mode 100644 index 7904fab0726..00000000000 --- a/lib/private/db/connection.php +++ /dev/null @@ -1,373 +0,0 @@ - - * @author Joas Schilling - * @author Morris Jobke - * @author Robin Appelman - * @author Robin McCorkell - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Driver; -use Doctrine\DBAL\Configuration; -use Doctrine\DBAL\Cache\QueryCacheProfile; -use Doctrine\Common\EventManager; -use OC\DB\QueryBuilder\QueryBuilder; -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IDBConnection; -use OCP\PreconditionNotMetException; - -class Connection extends \Doctrine\DBAL\Connection implements IDBConnection { - /** - * @var string $tablePrefix - */ - protected $tablePrefix; - - /** - * @var \OC\DB\Adapter $adapter - */ - protected $adapter; - - public function connect() { - try { - return parent::connect(); - } catch (DBALException $e) { - // throw a new exception to prevent leaking info from the stacktrace - throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode()); - } - } - - /** - * Returns a QueryBuilder for the connection. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder - */ - public function getQueryBuilder() { - return new QueryBuilder($this); - } - - /** - * Gets the QueryBuilder for the connection. - * - * @return \Doctrine\DBAL\Query\QueryBuilder - * @deprecated please use $this->getQueryBuilder() instead - */ - public function createQueryBuilder() { - $backtrace = $this->getCallerBacktrace(); - \OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]); - return parent::createQueryBuilder(); - } - - /** - * Gets the ExpressionBuilder for the connection. - * - * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder - * @deprecated please use $this->getQueryBuilder()->expr() instead - */ - public function getExpressionBuilder() { - $backtrace = $this->getCallerBacktrace(); - \OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]); - return parent::getExpressionBuilder(); - } - - /** - * Get the file and line that called the method where `getCallerBacktrace()` was used - * - * @return string - */ - protected function getCallerBacktrace() { - $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - - // 0 is the method where we use `getCallerBacktrace` - // 1 is the target method which uses the method we want to log - if (isset($traces[1])) { - return $traces[1]['file'] . ':' . $traces[1]['line']; - } - - return ''; - } - - /** - * @return string - */ - public function getPrefix() { - return $this->tablePrefix; - } - - /** - * Initializes a new instance of the Connection class. - * - * @param array $params The connection parameters. - * @param \Doctrine\DBAL\Driver $driver - * @param \Doctrine\DBAL\Configuration $config - * @param \Doctrine\Common\EventManager $eventManager - * @throws \Exception - */ - public function __construct(array $params, Driver $driver, Configuration $config = null, - EventManager $eventManager = null) - { - if (!isset($params['adapter'])) { - throw new \Exception('adapter not set'); - } - if (!isset($params['tablePrefix'])) { - throw new \Exception('tablePrefix not set'); - } - parent::__construct($params, $driver, $config, $eventManager); - $this->adapter = new $params['adapter']($this); - $this->tablePrefix = $params['tablePrefix']; - - parent::setTransactionIsolation(parent::TRANSACTION_READ_COMMITTED); - } - - /** - * Prepares an SQL statement. - * - * @param string $statement The SQL statement to prepare. - * @param int $limit - * @param int $offset - * @return \Doctrine\DBAL\Driver\Statement The prepared statement. - */ - public function prepare( $statement, $limit=null, $offset=null ) { - if ($limit === -1) { - $limit = null; - } - if (!is_null($limit)) { - $platform = $this->getDatabasePlatform(); - $statement = $platform->modifyLimitQuery($statement, $limit, $offset); - } - $statement = $this->replaceTablePrefix($statement); - $statement = $this->adapter->fixupStatement($statement); - - if(\OC::$server->getSystemConfig()->getValue( 'log_query', false)) { - \OCP\Util::writeLog('core', 'DB prepare : '.$statement, \OCP\Util::DEBUG); - } - return parent::prepare($statement); - } - - /** - * Executes an, optionally parametrized, SQL query. - * - * If the query is parametrized, a prepared statement is used. - * If an SQLLogger is configured, the execution is logged. - * - * @param string $query The SQL query to execute. - * @param array $params The parameters to bind to the query, if any. - * @param array $types The types the previous parameters are in. - * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp The query cache profile, optional. - * - * @return \Doctrine\DBAL\Driver\Statement The executed statement. - * - * @throws \Doctrine\DBAL\DBALException - */ - public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) - { - $query = $this->replaceTablePrefix($query); - $query = $this->adapter->fixupStatement($query); - return parent::executeQuery($query, $params, $types, $qcp); - } - - /** - * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters - * and returns the number of affected rows. - * - * This method supports PDO binding types as well as DBAL mapping types. - * - * @param string $query The SQL query. - * @param array $params The query parameters. - * @param array $types The parameter types. - * - * @return integer The number of affected rows. - * - * @throws \Doctrine\DBAL\DBALException - */ - public function executeUpdate($query, array $params = array(), array $types = array()) - { - $query = $this->replaceTablePrefix($query); - $query = $this->adapter->fixupStatement($query); - return parent::executeUpdate($query, $params, $types); - } - - /** - * Returns the ID of the last inserted row, or the last value from a sequence object, - * depending on the underlying driver. - * - * Note: This method may not return a meaningful or consistent result across different drivers, - * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY - * columns or sequences. - * - * @param string $seqName Name of the sequence object from which the ID should be returned. - * @return string A string representation of the last inserted ID. - */ - public function lastInsertId($seqName = null) { - if ($seqName) { - $seqName = $this->replaceTablePrefix($seqName); - } - return $this->adapter->lastInsertId($seqName); - } - - // internal use - public function realLastInsertId($seqName = null) { - return parent::lastInsertId($seqName); - } - - /** - * Insert a row if the matching row does not exists. - * - * @param string $table The table name (will replace *PREFIX* with the actual prefix) - * @param array $input data that should be inserted into the table (column name => value) - * @param array|null $compare List of values that should be checked for "if not exists" - * If this is null or an empty array, all keys of $input will be compared - * Please note: text fields (clob) must not be used in the compare array - * @return int number of inserted rows - * @throws \Doctrine\DBAL\DBALException - */ - public function insertIfNotExist($table, $input, array $compare = null) { - return $this->adapter->insertIfNotExist($table, $input, $compare); - } - - private function getType($value) { - if (is_bool($value)) { - return IQueryBuilder::PARAM_BOOL; - } else if (is_int($value)) { - return IQueryBuilder::PARAM_INT; - } else { - return IQueryBuilder::PARAM_STR; - } - } - - /** - * Insert or update a row value - * - * @param string $table - * @param array $keys (column name => value) - * @param array $values (column name => value) - * @param array $updatePreconditionValues ensure values match preconditions (column name => value) - * @return int number of new rows - * @throws \Doctrine\DBAL\DBALException - * @throws PreconditionNotMetException - */ - public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) { - try { - $insertQb = $this->getQueryBuilder(); - $insertQb->insert($table) - ->values( - array_map(function($value) use ($insertQb) { - return $insertQb->createNamedParameter($value, $this->getType($value)); - }, array_merge($keys, $values)) - ); - return $insertQb->execute(); - } catch (\Doctrine\DBAL\Exception\ConstraintViolationException $e) { - // value already exists, try update - $updateQb = $this->getQueryBuilder(); - $updateQb->update($table); - foreach ($values as $name => $value) { - $updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value))); - } - $where = $updateQb->expr()->andx(); - $whereValues = array_merge($keys, $updatePreconditionValues); - foreach ($whereValues as $name => $value) { - $where->add($updateQb->expr()->eq( - $name, - $updateQb->createNamedParameter($value, $this->getType($value)), - $this->getType($value) - )); - } - $updateQb->where($where); - $affected = $updateQb->execute(); - - if ($affected === 0 && !empty($updatePreconditionValues)) { - throw new PreconditionNotMetException(); - } - - return 0; - } - } - - /** - * returns the error code and message as a string for logging - * works with DoctrineException - * @return string - */ - public function getError() { - $msg = $this->errorCode() . ': '; - $errorInfo = $this->errorInfo(); - if (is_array($errorInfo)) { - $msg .= 'SQLSTATE = '.$errorInfo[0] . ', '; - $msg .= 'Driver Code = '.$errorInfo[1] . ', '; - $msg .= 'Driver Message = '.$errorInfo[2]; - } - return $msg; - } - - /** - * Drop a table from the database if it exists - * - * @param string $table table name without the prefix - */ - public function dropTable($table) { - $table = $this->tablePrefix . trim($table); - $schema = $this->getSchemaManager(); - if($schema->tablesExist(array($table))) { - $schema->dropTable($table); - } - } - - /** - * Check if a table exists - * - * @param string $table table name without the prefix - * @return bool - */ - public function tableExists($table){ - $table = $this->tablePrefix . trim($table); - $schema = $this->getSchemaManager(); - return $schema->tablesExist(array($table)); - } - - // internal use - /** - * @param string $statement - * @return string - */ - protected function replaceTablePrefix($statement) { - return str_replace( '*PREFIX*', $this->tablePrefix, $statement ); - } - - /** - * Check if a transaction is active - * - * @return bool - * @since 8.2.0 - */ - public function inTransaction() { - return $this->getTransactionNestingLevel() > 0; - } - - /** - * Espace a parameter to be used in a LIKE query - * - * @param string $param - * @return string - */ - public function escapeLikeParameter($param) { - return addcslashes($param, '\\_%'); - } -} diff --git a/lib/private/db/connectionfactory.php b/lib/private/db/connectionfactory.php deleted file mode 100644 index 0856d8d19c0..00000000000 --- a/lib/private/db/connectionfactory.php +++ /dev/null @@ -1,184 +0,0 @@ - - * @author Jörn Friedrich Dreyer - * @author Morris Jobke - * @author Robin Appelman - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; -use Doctrine\DBAL\Event\Listeners\OracleSessionInit; -use Doctrine\DBAL\Event\Listeners\SQLSessionInit; -use Doctrine\DBAL\Event\Listeners\MysqlSessionInit; - -/** -* Takes care of creating and configuring Doctrine connections. -*/ -class ConnectionFactory { - /** - * @var array - * - * Array mapping DBMS type to default connection parameters passed to - * \Doctrine\DBAL\DriverManager::getConnection(). - */ - protected $defaultConnectionParams = array( - 'mysql' => array( - 'adapter' => '\OC\DB\AdapterMySQL', - 'charset' => 'UTF8', - 'driver' => 'pdo_mysql', - 'wrapperClass' => 'OC\DB\Connection', - ), - 'oci' => array( - 'adapter' => '\OC\DB\AdapterOCI8', - 'charset' => 'AL32UTF8', - 'driver' => 'oci8', - 'wrapperClass' => 'OC\DB\OracleConnection', - ), - 'pgsql' => array( - 'adapter' => '\OC\DB\AdapterPgSql', - 'driver' => 'pdo_pgsql', - 'wrapperClass' => 'OC\DB\Connection', - ), - 'sqlite3' => array( - 'adapter' => '\OC\DB\AdapterSqlite', - 'driver' => 'pdo_sqlite', - 'wrapperClass' => 'OC\DB\Connection', - ), - ); - - /** - * @brief Get default connection parameters for a given DBMS. - * @param string $type DBMS type - * @throws \InvalidArgumentException If $type is invalid - * @return array Default connection parameters. - */ - public function getDefaultConnectionParams($type) { - $normalizedType = $this->normalizeType($type); - if (!isset($this->defaultConnectionParams[$normalizedType])) { - throw new \InvalidArgumentException("Unsupported type: $type"); - } - $result = $this->defaultConnectionParams[$normalizedType]; - // \PDO::MYSQL_ATTR_FOUND_ROWS may not be defined, e.g. when the MySQL - // driver is missing. In this case, we won't be able to connect anyway. - if ($normalizedType === 'mysql' && defined('\PDO::MYSQL_ATTR_FOUND_ROWS')) { - $result['driverOptions'] = array( - \PDO::MYSQL_ATTR_FOUND_ROWS => true, - ); - } - return $result; - } - - /** - * @brief Get default connection parameters for a given DBMS. - * @param string $type DBMS type - * @param array $additionalConnectionParams Additional connection parameters - * @return \OC\DB\Connection - */ - public function getConnection($type, $additionalConnectionParams) { - $normalizedType = $this->normalizeType($type); - $eventManager = new \Doctrine\Common\EventManager(); - switch ($normalizedType) { - case 'mysql': - // Send "SET NAMES utf8". Only required on PHP 5.3 below 5.3.6. - // See http://stackoverflow.com/questions/4361459/php-pdo-charset-set-names#4361485 - $eventManager->addEventSubscriber(new MysqlSessionInit); - $eventManager->addEventSubscriber( - new SQLSessionInit("SET SESSION AUTOCOMMIT=1")); - break; - case 'oci': - $eventManager->addEventSubscriber(new OracleSessionInit); - break; - case 'sqlite3': - $journalMode = $additionalConnectionParams['sqlite.journal_mode']; - $additionalConnectionParams['platform'] = new OCSqlitePlatform(); - $eventManager->addEventSubscriber(new SQLiteSessionInit(true, $journalMode)); - break; - } - $connection = \Doctrine\DBAL\DriverManager::getConnection( - array_merge($this->getDefaultConnectionParams($type), $additionalConnectionParams), - new \Doctrine\DBAL\Configuration(), - $eventManager - ); - return $connection; - } - - /** - * @brief Normalize DBMS type - * @param string $type DBMS type - * @return string Normalized DBMS type - */ - public function normalizeType($type) { - return $type === 'sqlite' ? 'sqlite3' : $type; - } - - /** - * @brief Checks whether the specified DBMS type is valid. - * @return bool - */ - public function isValidType($type) { - $normalizedType = $this->normalizeType($type); - return isset($this->defaultConnectionParams[$normalizedType]); - } - - /** - * Create the connection parameters for the config - * - * @param \OC\SystemConfig $config - * @return array - */ - public function createConnectionParams($config) { - $type = $config->getValue('dbtype', 'sqlite'); - - $connectionParams = array( - 'user' => $config->getValue('dbuser', ''), - 'password' => $config->getValue('dbpassword', ''), - ); - $name = $config->getValue('dbname', 'owncloud'); - - if ($this->normalizeType($type) === 'sqlite3') { - $dataDir = $config->getValue("datadirectory", \OC::$SERVERROOT . '/data'); - $connectionParams['path'] = $dataDir . '/' . $name . '.db'; - } else { - $host = $config->getValue('dbhost', ''); - if (strpos($host, ':')) { - // Host variable may carry a port or socket. - list($host, $portOrSocket) = explode(':', $host, 2); - if (ctype_digit($portOrSocket)) { - $connectionParams['port'] = $portOrSocket; - } else { - $connectionParams['unix_socket'] = $portOrSocket; - } - } - $connectionParams['host'] = $host; - $connectionParams['dbname'] = $name; - } - - $connectionParams['tablePrefix'] = $config->getValue('dbtableprefix', 'oc_'); - $connectionParams['sqlite.journal_mode'] = $config->getValue('sqlite.journal_mode', 'WAL'); - - //additional driver options, eg. for mysql ssl - $driverOptions = $config->getValue('dbdriveroptions', null); - if ($driverOptions) { - $connectionParams['driverOptions'] = $driverOptions; - } - - return $connectionParams; - } -} diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php deleted file mode 100644 index f73f6b4351a..00000000000 --- a/lib/private/db/mdb2schemamanager.php +++ /dev/null @@ -1,178 +0,0 @@ - - * @author Jörn Friedrich Dreyer - * @author Lukas Reschke - * @author Morris Jobke - * @author Robin Appelman - * @author Roeland Jago Douma - * @author Thomas Müller - * @author Vincent Petry - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -use Doctrine\DBAL\Platforms\MySqlPlatform; -use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\PostgreSqlPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; -use OCP\IDBConnection; - -class MDB2SchemaManager { - /** @var \OC\DB\Connection $conn */ - protected $conn; - - /** - * @param IDBConnection $conn - */ - public function __construct($conn) { - $this->conn = $conn; - } - - /** - * saves database scheme to xml file - * @param string $file name of file - * @return bool - * - * TODO: write more documentation - */ - public function getDbStructure($file) { - return \OC\DB\MDB2SchemaWriter::saveSchemaToFile($file, $this->conn); - } - - /** - * Creates tables from XML file - * @param string $file file to read structure from - * @return bool - * - * TODO: write more documentation - */ - public function createDbFromStructure($file) { - $schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $this->conn->getDatabasePlatform()); - $toSchema = $schemaReader->loadSchemaFromFile($file); - return $this->executeSchemaChange($toSchema); - } - - /** - * @return \OC\DB\Migrator - */ - public function getMigrator() { - $random = \OC::$server->getSecureRandom(); - $platform = $this->conn->getDatabasePlatform(); - $config = \OC::$server->getConfig(); - $dispatcher = \OC::$server->getEventDispatcher(); - if ($platform instanceof SqlitePlatform) { - return new SQLiteMigrator($this->conn, $random, $config, $dispatcher); - } else if ($platform instanceof OraclePlatform) { - return new OracleMigrator($this->conn, $random, $config, $dispatcher); - } else if ($platform instanceof MySqlPlatform) { - return new MySQLMigrator($this->conn, $random, $config, $dispatcher); - } else if ($platform instanceof PostgreSqlPlatform) { - return new Migrator($this->conn, $random, $config, $dispatcher); - } else { - return new NoCheckMigrator($this->conn, $random, $config, $dispatcher); - } - } - - /** - * Reads database schema from file - * - * @param string $file file to read from - * @return \Doctrine\DBAL\Schema\Schema - */ - private function readSchemaFromFile($file) { - $platform = $this->conn->getDatabasePlatform(); - $schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $platform); - return $schemaReader->loadSchemaFromFile($file); - } - - /** - * update the database scheme - * @param string $file file to read structure from - * @param bool $generateSql only return the sql needed for the upgrade - * @return string|boolean - */ - public function updateDbFromStructure($file, $generateSql = false) { - $toSchema = $this->readSchemaFromFile($file); - $migrator = $this->getMigrator(); - - if ($generateSql) { - return $migrator->generateChangeScript($toSchema); - } else { - $migrator->migrate($toSchema); - return true; - } - } - - /** - * update the database scheme - * @param string $file file to read structure from - * @return boolean - */ - public function simulateUpdateDbFromStructure($file) { - $toSchema = $this->readSchemaFromFile($file); - $this->getMigrator()->checkMigrate($toSchema); - return true; - } - - /** - * @param \Doctrine\DBAL\Schema\Schema $schema - * @return string - */ - public function generateChangeScript($schema) { - $migrator = $this->getMigrator(); - return $migrator->generateChangeScript($schema); - } - - /** - * remove all tables defined in a database structure xml file - * - * @param string $file the xml file describing the tables - */ - public function removeDBStructure($file) { - $schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $this->conn->getDatabasePlatform()); - $fromSchema = $schemaReader->loadSchemaFromFile($file); - $toSchema = clone $fromSchema; - /** @var $table \Doctrine\DBAL\Schema\Table */ - foreach ($toSchema->getTables() as $table) { - $toSchema->dropTable($table->getName()); - } - $comparator = new \Doctrine\DBAL\Schema\Comparator(); - $schemaDiff = $comparator->compare($fromSchema, $toSchema); - $this->executeSchemaChange($schemaDiff); - } - - /** - * @param \Doctrine\DBAL\Schema\Schema|\Doctrine\DBAL\Schema\SchemaDiff $schema - * @return bool - */ - private function executeSchemaChange($schema) { - $this->conn->beginTransaction(); - foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) { - $this->conn->query($sql); - } - $this->conn->commit(); - - if ($this->conn->getDatabasePlatform() instanceof SqlitePlatform) { - $this->conn->close(); - $this->conn->connect(); - } - return true; - } -} diff --git a/lib/private/db/mdb2schemareader.php b/lib/private/db/mdb2schemareader.php deleted file mode 100644 index 375fec185cb..00000000000 --- a/lib/private/db/mdb2schemareader.php +++ /dev/null @@ -1,359 +0,0 @@ - - * @author Joas Schilling - * @author Jörn Friedrich Dreyer - * @author Morris Jobke - * @author Oliver Gasser - * @author Robin Appelman - * @author Robin McCorkell - * @author Thomas Müller - * @author Victor Dubiniuk - * @author Vincent Petry - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Schema\SchemaConfig; -use OCP\IConfig; - -class MDB2SchemaReader { - /** - * @var string $DBNAME - */ - protected $DBNAME; - - /** - * @var string $DBTABLEPREFIX - */ - protected $DBTABLEPREFIX; - - /** - * @var \Doctrine\DBAL\Platforms\AbstractPlatform $platform - */ - protected $platform; - - /** @var \Doctrine\DBAL\Schema\SchemaConfig $schemaConfig */ - protected $schemaConfig; - - /** - * @param \OCP\IConfig $config - * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform - */ - public function __construct(IConfig $config, AbstractPlatform $platform) { - $this->platform = $platform; - $this->DBNAME = $config->getSystemValue('dbname', 'owncloud'); - $this->DBTABLEPREFIX = $config->getSystemValue('dbtableprefix', 'oc_'); - - // Oracle does not support longer index names then 30 characters. - // We use this limit for all DBs to make sure it does not cause a - // problem. - $this->schemaConfig = new SchemaConfig(); - $this->schemaConfig->setMaxIdentifierLength(30); - } - - /** - * @param string $file - * @return \Doctrine\DBAL\Schema\Schema - * @throws \DomainException - */ - public function loadSchemaFromFile($file) { - $schema = new \Doctrine\DBAL\Schema\Schema(); - $loadEntities = libxml_disable_entity_loader(false); - $xml = simplexml_load_file($file); - libxml_disable_entity_loader($loadEntities); - foreach ($xml->children() as $child) { - /** - * @var \SimpleXMLElement $child - */ - switch ($child->getName()) { - case 'name': - case 'create': - case 'overwrite': - case 'charset': - break; - case 'table': - $this->loadTable($schema, $child); - break; - default: - throw new \DomainException('Unknown element: ' . $child->getName()); - - } - } - return $schema; - } - - /** - * @param \Doctrine\DBAL\Schema\Schema $schema - * @param \SimpleXMLElement $xml - * @throws \DomainException - */ - private function loadTable($schema, $xml) { - $table = null; - foreach ($xml->children() as $child) { - /** - * @var \SimpleXMLElement $child - */ - switch ($child->getName()) { - case 'name': - $name = (string)$child; - $name = str_replace('*dbprefix*', $this->DBTABLEPREFIX, $name); - $name = $this->platform->quoteIdentifier($name); - $table = $schema->createTable($name); - $table->addOption('collate', 'utf8_bin'); - $table->setSchemaConfig($this->schemaConfig); - break; - case 'create': - case 'overwrite': - case 'charset': - break; - case 'declaration': - if (is_null($table)) { - throw new \DomainException('Table declaration before table name'); - } - $this->loadDeclaration($table, $child); - break; - default: - throw new \DomainException('Unknown element: ' . $child->getName()); - - } - } - } - - /** - * @param \Doctrine\DBAL\Schema\Table $table - * @param \SimpleXMLElement $xml - * @throws \DomainException - */ - private function loadDeclaration($table, $xml) { - foreach ($xml->children() as $child) { - /** - * @var \SimpleXMLElement $child - */ - switch ($child->getName()) { - case 'field': - $this->loadField($table, $child); - break; - case 'index': - $this->loadIndex($table, $child); - break; - default: - throw new \DomainException('Unknown element: ' . $child->getName()); - - } - } - } - - /** - * @param \Doctrine\DBAL\Schema\Table $table - * @param \SimpleXMLElement $xml - * @throws \DomainException - */ - private function loadField($table, $xml) { - $options = array( 'notnull' => false ); - foreach ($xml->children() as $child) { - /** - * @var \SimpleXMLElement $child - */ - switch ($child->getName()) { - case 'name': - $name = (string)$child; - $name = $this->platform->quoteIdentifier($name); - break; - case 'type': - $type = (string)$child; - switch ($type) { - case 'text': - $type = 'string'; - break; - case 'clob': - $type = 'text'; - break; - case 'timestamp': - $type = 'datetime'; - break; - case 'numeric': - $type = 'decimal'; - break; - } - break; - case 'length': - $length = (string)$child; - $options['length'] = $length; - break; - case 'unsigned': - $unsigned = $this->asBool($child); - $options['unsigned'] = $unsigned; - break; - case 'notnull': - $notnull = $this->asBool($child); - $options['notnull'] = $notnull; - break; - case 'autoincrement': - $autoincrement = $this->asBool($child); - $options['autoincrement'] = $autoincrement; - break; - case 'default': - $default = (string)$child; - $options['default'] = $default; - break; - case 'comments': - $comment = (string)$child; - $options['comment'] = $comment; - break; - case 'primary': - $primary = $this->asBool($child); - $options['primary'] = $primary; - break; - case 'precision': - $precision = (string)$child; - $options['precision'] = $precision; - break; - case 'scale': - $scale = (string)$child; - $options['scale'] = $scale; - break; - default: - throw new \DomainException('Unknown element: ' . $child->getName()); - - } - } - if (isset($name) && isset($type)) { - if (isset($options['default']) && empty($options['default'])) { - if (empty($options['notnull']) || !$options['notnull']) { - unset($options['default']); - $options['notnull'] = false; - } else { - $options['default'] = ''; - } - if ($type == 'integer' || $type == 'decimal') { - $options['default'] = 0; - } elseif ($type == 'boolean') { - $options['default'] = false; - } - if (!empty($options['autoincrement']) && $options['autoincrement']) { - unset($options['default']); - } - } - if ($type === 'integer' && isset($options['default'])) { - $options['default'] = (int)$options['default']; - } - if ($type === 'integer' && isset($options['length'])) { - $length = $options['length']; - if ($length < 4) { - $type = 'smallint'; - } else if ($length > 4) { - $type = 'bigint'; - } - } - if ($type === 'boolean' && isset($options['default'])) { - $options['default'] = $this->asBool($options['default']); - } - if (!empty($options['autoincrement']) - && !empty($options['notnull']) - ) { - $options['primary'] = true; - } - $table->addColumn($name, $type, $options); - if (!empty($options['primary']) && $options['primary']) { - $table->setPrimaryKey(array($name)); - } - } - } - - /** - * @param \Doctrine\DBAL\Schema\Table $table - * @param \SimpleXMLElement $xml - * @throws \DomainException - */ - private function loadIndex($table, $xml) { - $name = null; - $fields = array(); - foreach ($xml->children() as $child) { - /** - * @var \SimpleXMLElement $child - */ - switch ($child->getName()) { - case 'name': - $name = (string)$child; - break; - case 'primary': - $primary = $this->asBool($child); - break; - case 'unique': - $unique = $this->asBool($child); - break; - case 'field': - foreach ($child->children() as $field) { - /** - * @var \SimpleXMLElement $field - */ - switch ($field->getName()) { - case 'name': - $field_name = (string)$field; - $field_name = $this->platform->quoteIdentifier($field_name); - $fields[] = $field_name; - break; - case 'sorting': - break; - default: - throw new \DomainException('Unknown element: ' . $field->getName()); - - } - } - break; - default: - throw new \DomainException('Unknown element: ' . $child->getName()); - - } - } - if (!empty($fields)) { - if (isset($primary) && $primary) { - if ($table->hasPrimaryKey()) { - return; - } - $table->setPrimaryKey($fields, $name); - } else { - if (isset($unique) && $unique) { - $table->addUniqueIndex($fields, $name); - } else { - $table->addIndex($fields, $name); - } - } - } else { - throw new \DomainException('Empty index definition: ' . $name . ' options:' . print_r($fields, true)); - } - } - - /** - * @param \SimpleXMLElement|string $xml - * @return bool - */ - private function asBool($xml) { - $result = (string)$xml; - if ($result == 'true') { - $result = true; - } elseif ($result == 'false') { - $result = false; - } - return (bool)$result; - } - -} diff --git a/lib/private/db/mdb2schemawriter.php b/lib/private/db/mdb2schemawriter.php deleted file mode 100644 index 7dc3bd223a7..00000000000 --- a/lib/private/db/mdb2schemawriter.php +++ /dev/null @@ -1,178 +0,0 @@ - - * @author Jörn Friedrich Dreyer - * @author Morris Jobke - * @author tbelau666 - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -use Doctrine\DBAL\Schema\Column; -use Doctrine\DBAL\Schema\Index; - -class MDB2SchemaWriter { - - /** - * @param string $file - * @param \OC\DB\Connection $conn - * @return bool - */ - static public function saveSchemaToFile($file, \OC\DB\Connection $conn) { - $config = \OC::$server->getConfig(); - - $xml = new \SimpleXMLElement(''); - $xml->addChild('name', $config->getSystemValue('dbname', 'owncloud')); - $xml->addChild('create', 'true'); - $xml->addChild('overwrite', 'false'); - $xml->addChild('charset', 'utf8'); - - // FIX ME: bloody work around - if ($config->getSystemValue('dbtype', 'sqlite') === 'oci') { - $filterExpression = '/^"' . preg_quote($conn->getPrefix()) . '/'; - } else { - $filterExpression = '/^' . preg_quote($conn->getPrefix()) . '/'; - } - $conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); - - foreach ($conn->getSchemaManager()->listTables() as $table) { - self::saveTable($table, $xml->addChild('table')); - } - file_put_contents($file, $xml->asXML()); - return true; - } - - /** - * @param \Doctrine\DBAL\Schema\Table $table - * @param \SimpleXMLElement $xml - */ - private static function saveTable($table, $xml) { - $xml->addChild('name', $table->getName()); - $declaration = $xml->addChild('declaration'); - foreach($table->getColumns() as $column) { - self::saveColumn($column, $declaration->addChild('field')); - } - foreach($table->getIndexes() as $index) { - if ($index->getName() == 'PRIMARY') { - $autoincrement = false; - foreach($index->getColumns() as $column) { - if ($table->getColumn($column)->getAutoincrement()) { - $autoincrement = true; - } - } - if ($autoincrement) { - continue; - } - } - self::saveIndex($index, $declaration->addChild('index')); - } - } - - /** - * @param Column $column - * @param \SimpleXMLElement $xml - */ - private static function saveColumn($column, $xml) { - $xml->addChild('name', $column->getName()); - switch($column->getType()) { - case 'SmallInt': - case 'Integer': - case 'BigInt': - $xml->addChild('type', 'integer'); - $default = $column->getDefault(); - if (is_null($default) && $column->getAutoincrement()) { - $default = '0'; - } - $xml->addChild('default', $default); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - if ($column->getAutoincrement()) { - $xml->addChild('autoincrement', '1'); - } - if ($column->getUnsigned()) { - $xml->addChild('unsigned', 'true'); - } - $length = '4'; - if ($column->getType() == 'SmallInt') { - $length = '2'; - } - elseif ($column->getType() == 'BigInt') { - $length = '8'; - } - $xml->addChild('length', $length); - break; - case 'String': - $xml->addChild('type', 'text'); - $default = trim($column->getDefault()); - if ($default === '') { - $default = false; - } - $xml->addChild('default', $default); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - $xml->addChild('length', $column->getLength()); - break; - case 'Text': - $xml->addChild('type', 'clob'); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - break; - case 'Decimal': - $xml->addChild('type', 'decimal'); - $xml->addChild('default', $column->getDefault()); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - $xml->addChild('length', '15'); - break; - case 'Boolean': - $xml->addChild('type', 'integer'); - $xml->addChild('default', $column->getDefault()); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - $xml->addChild('length', '1'); - break; - case 'DateTime': - $xml->addChild('type', 'timestamp'); - $xml->addChild('default', $column->getDefault()); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - break; - - } - } - - /** - * @param Index $index - * @param \SimpleXMLElement $xml - */ - private static function saveIndex($index, $xml) { - $xml->addChild('name', $index->getName()); - if ($index->isPrimary()) { - $xml->addChild('primary', 'true'); - } - elseif ($index->isUnique()) { - $xml->addChild('unique', 'true'); - } - foreach($index->getColumns() as $column) { - $field = $xml->addChild('field'); - $field->addChild('name', $column); - $field->addChild('sorting', 'ascending'); - - } - } - - private static function toBool($bool) { - return $bool ? 'true' : 'false'; - } -} diff --git a/lib/private/db/migrationexception.php b/lib/private/db/migrationexception.php deleted file mode 100644 index 57e4c5b8334..00000000000 --- a/lib/private/db/migrationexception.php +++ /dev/null @@ -1,40 +0,0 @@ - - * @author Robin Appelman - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - - -class MigrationException extends \Exception { - private $table; - - public function __construct($table, $message) { - $this->table = $table; - parent::__construct($message); - } - - /** - * @return string - */ - public function getTable() { - return $this->table; - } -} diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php deleted file mode 100644 index 8b8a34d9865..00000000000 --- a/lib/private/db/migrator.php +++ /dev/null @@ -1,292 +0,0 @@ - - * @author Morris Jobke - * @author Robin Appelman - * @author tbelau666 - * @author Thomas Müller - * @author Victor Dubiniuk - * @author Vincent Petry - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -use \Doctrine\DBAL\DBALException; -use \Doctrine\DBAL\Schema\Index; -use \Doctrine\DBAL\Schema\Table; -use \Doctrine\DBAL\Schema\Schema; -use \Doctrine\DBAL\Schema\SchemaConfig; -use \Doctrine\DBAL\Schema\Comparator; -use OCP\IConfig; -use OCP\Security\ISecureRandom; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\GenericEvent; - -class Migrator { - - /** - * @var \Doctrine\DBAL\Connection $connection - */ - protected $connection; - - /** - * @var ISecureRandom - */ - private $random; - - /** @var IConfig */ - protected $config; - - /** @var EventDispatcher */ - private $dispatcher; - - /** @var bool */ - private $noEmit = false; - - /** - * @param \Doctrine\DBAL\Connection|Connection $connection - * @param ISecureRandom $random - * @param IConfig $config - * @param EventDispatcher $dispatcher - */ - public function __construct(\Doctrine\DBAL\Connection $connection, - ISecureRandom $random, - IConfig $config, - EventDispatcher $dispatcher = null) { - $this->connection = $connection; - $this->random = $random; - $this->config = $config; - $this->dispatcher = $dispatcher; - } - - /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema - */ - public function migrate(Schema $targetSchema) { - $this->noEmit = true; - $this->applySchema($targetSchema); - } - - /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema - * @return string - */ - public function generateChangeScript(Schema $targetSchema) { - $schemaDiff = $this->getDiff($targetSchema, $this->connection); - - $script = ''; - $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform()); - foreach ($sqls as $sql) { - $script .= $this->convertStatementToScript($sql); - } - - return $script; - } - - /** - * @param Schema $targetSchema - * @throws \OC\DB\MigrationException - */ - public function checkMigrate(Schema $targetSchema) { - $this->noEmit = true; - /**@var \Doctrine\DBAL\Schema\Table[] $tables */ - $tables = $targetSchema->getTables(); - $filterExpression = $this->getFilterExpression(); - $this->connection->getConfiguration()-> - setFilterSchemaAssetsExpression($filterExpression); - $existingTables = $this->connection->getSchemaManager()->listTableNames(); - - $step = 0; - foreach ($tables as $table) { - if (strpos($table->getName(), '.')) { - list(, $tableName) = explode('.', $table->getName()); - } else { - $tableName = $table->getName(); - } - $this->emitCheckStep($tableName, $step++, count($tables)); - // don't need to check for new tables - if (array_search($tableName, $existingTables) !== false) { - $this->checkTableMigrate($table); - } - } - } - - /** - * Create a unique name for the temporary table - * - * @param string $name - * @return string - */ - protected function generateTemporaryTableName($name) { - return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); - } - - /** - * Check the migration of a table on a copy so we can detect errors before messing with the real table - * - * @param \Doctrine\DBAL\Schema\Table $table - * @throws \OC\DB\MigrationException - */ - protected function checkTableMigrate(Table $table) { - $name = $table->getName(); - $tmpName = $this->generateTemporaryTableName($name); - - $this->copyTable($name, $tmpName); - - //create the migration schema for the temporary table - $tmpTable = $this->renameTableSchema($table, $tmpName); - $schemaConfig = new SchemaConfig(); - $schemaConfig->setName($this->connection->getDatabase()); - $schema = new Schema(array($tmpTable), array(), $schemaConfig); - - try { - $this->applySchema($schema); - $this->dropTable($tmpName); - } catch (DBALException $e) { - // pgsql needs to commit it's failed transaction before doing anything else - if ($this->connection->isTransactionActive()) { - $this->connection->commit(); - } - $this->dropTable($tmpName); - throw new MigrationException($table->getName(), $e->getMessage()); - } - } - - /** - * @param \Doctrine\DBAL\Schema\Table $table - * @param string $newName - * @return \Doctrine\DBAL\Schema\Table - */ - protected function renameTableSchema(Table $table, $newName) { - /** - * @var \Doctrine\DBAL\Schema\Index[] $indexes - */ - $indexes = $table->getIndexes(); - $newIndexes = array(); - foreach ($indexes as $index) { - if ($index->isPrimary()) { - // do not rename primary key - $indexName = $index->getName(); - } else { - // avoid conflicts in index names - $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER); - } - $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary()); - } - - // foreign keys are not supported so we just set it to an empty array - return new Table($newName, $table->getColumns(), $newIndexes, array(), 0, $table->getOptions()); - } - - protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { - $filterExpression = $this->getFilterExpression(); - $this->connection->getConfiguration()-> - setFilterSchemaAssetsExpression($filterExpression); - $sourceSchema = $connection->getSchemaManager()->createSchema(); - - // remove tables we don't know about - /** @var $table \Doctrine\DBAL\Schema\Table */ - foreach ($sourceSchema->getTables() as $table) { - if (!$targetSchema->hasTable($table->getName())) { - $sourceSchema->dropTable($table->getName()); - } - } - // remove sequences we don't know about - foreach ($sourceSchema->getSequences() as $table) { - if (!$targetSchema->hasSequence($table->getName())) { - $sourceSchema->dropSequence($table->getName()); - } - } - - $comparator = new Comparator(); - return $comparator->compare($sourceSchema, $targetSchema); - } - - /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema - * @param \Doctrine\DBAL\Connection $connection - */ - protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { - if (is_null($connection)) { - $connection = $this->connection; - } - - $schemaDiff = $this->getDiff($targetSchema, $connection); - - $connection->beginTransaction(); - $sqls = $schemaDiff->toSql($connection->getDatabasePlatform()); - $step = 0; - foreach ($sqls as $sql) { - $this->emit($sql, $step++, count($sqls)); - $connection->query($sql); - } - $connection->commit(); - } - - /** - * @param string $sourceName - * @param string $targetName - */ - protected function copyTable($sourceName, $targetName) { - $quotedSource = $this->connection->quoteIdentifier($sourceName); - $quotedTarget = $this->connection->quoteIdentifier($targetName); - - $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')'); - $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource); - } - - /** - * @param string $name - */ - protected function dropTable($name) { - $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name)); - } - - /** - * @param $statement - * @return string - */ - protected function convertStatementToScript($statement) { - $script = $statement . ';'; - $script .= PHP_EOL; - $script .= PHP_EOL; - return $script; - } - - protected function getFilterExpression() { - return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; - } - - protected function emit($sql, $step, $max) { - if ($this->noEmit) { - return; - } - if(is_null($this->dispatcher)) { - return; - } - $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max])); - } - - private function emitCheckStep($tableName, $step, $max) { - if(is_null($this->dispatcher)) { - return; - } - $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step+1, $max])); - } -} diff --git a/lib/private/db/mysqlmigrator.php b/lib/private/db/mysqlmigrator.php deleted file mode 100644 index 1b3f70a817d..00000000000 --- a/lib/private/db/mysqlmigrator.php +++ /dev/null @@ -1,51 +0,0 @@ - - * @author Robin Appelman - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -use Doctrine\DBAL\Schema\Schema; - -class MySQLMigrator extends Migrator { - /** - * @param Schema $targetSchema - * @param \Doctrine\DBAL\Connection $connection - * @return \Doctrine\DBAL\Schema\SchemaDiff - */ - protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { - $platform = $connection->getDatabasePlatform(); - $platform->registerDoctrineTypeMapping('enum', 'string'); - $platform->registerDoctrineTypeMapping('bit', 'string'); - - $schemaDiff = parent::getDiff($targetSchema, $connection); - - // identifiers need to be quoted for mysql - foreach ($schemaDiff->changedTables as $tableDiff) { - $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); - foreach ($tableDiff->changedColumns as $column) { - $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); - } - } - - return $schemaDiff; - } -} diff --git a/lib/private/db/nocheckmigrator.php b/lib/private/db/nocheckmigrator.php deleted file mode 100644 index 23afae11816..00000000000 --- a/lib/private/db/nocheckmigrator.php +++ /dev/null @@ -1,38 +0,0 @@ - - * @author Robin Appelman - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -use Doctrine\DBAL\Schema\Schema; - -/** - * migrator for database platforms that don't support the upgrade check - * - * @package OC\DB - */ -class NoCheckMigrator extends Migrator { - /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema - * @throws \OC\DB\MigrationException - */ - public function checkMigrate(Schema $targetSchema) {} -} diff --git a/lib/private/db/ocsqliteplatform.php b/lib/private/db/ocsqliteplatform.php deleted file mode 100644 index fe68bc3cc91..00000000000 --- a/lib/private/db/ocsqliteplatform.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -class OCSqlitePlatform extends \Doctrine\DBAL\Platforms\SqlitePlatform { - /** - * {@inheritDoc} - */ - public function getColumnDeclarationSQL($name, array $field) { - $def = parent::getColumnDeclarationSQL($name, $field); - if (!empty($field['autoincrement'])) { - $def .= ' PRIMARY KEY AUTOINCREMENT'; - } - return $def; - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = array()){ - // if auto increment is set the column is already defined as primary key - foreach ($columns as $column) { - if (!empty($column['autoincrement'])) { - $options['primary'] = null; - } - } - return parent::_getCreateTableSQL($name, $columns, $options); - } -} diff --git a/lib/private/db/oracleconnection.php b/lib/private/db/oracleconnection.php deleted file mode 100644 index a95f37a8e6c..00000000000 --- a/lib/private/db/oracleconnection.php +++ /dev/null @@ -1,93 +0,0 @@ - - * @author Lukas Reschke - * @author Morris Jobke - * @author Robin Appelman - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -class OracleConnection extends Connection { - /** - * Quote the keys of the array - */ - private function quoteKeys(array $data) { - $return = array(); - foreach($data as $key => $value) { - $return[$this->quoteIdentifier($key)] = $value; - } - return $return; - } - - /** - * {@inheritDoc} - */ - public function insert($tableName, array $data, array $types = array()) { - $tableName = $this->quoteIdentifier($tableName); - $data = $this->quoteKeys($data); - return parent::insert($tableName, $data, $types); - } - - /** - * {@inheritDoc} - */ - public function update($tableName, array $data, array $identifier, array $types = array()) { - $tableName = $this->quoteIdentifier($tableName); - $data = $this->quoteKeys($data); - $identifier = $this->quoteKeys($identifier); - return parent::update($tableName, $data, $identifier, $types); - } - - /** - * {@inheritDoc} - */ - public function delete($tableExpression, array $identifier, array $types = array()) { - $tableName = $this->quoteIdentifier($tableExpression); - $identifier = $this->quoteKeys($identifier); - return parent::delete($tableName, $identifier); - } - - /** - * Drop a table from the database if it exists - * - * @param string $table table name without the prefix - */ - public function dropTable($table) { - $table = $this->tablePrefix . trim($table); - $table = $this->quoteIdentifier($table); - $schema = $this->getSchemaManager(); - if($schema->tablesExist(array($table))) { - $schema->dropTable($table); - } - } - - /** - * Check if a table exists - * - * @param string $table table name without the prefix - * @return bool - */ - public function tableExists($table){ - $table = $this->tablePrefix . trim($table); - $table = $this->quoteIdentifier($table); - $schema = $this->getSchemaManager(); - return $schema->tablesExist(array($table)); - } -} diff --git a/lib/private/db/oraclemigrator.php b/lib/private/db/oraclemigrator.php deleted file mode 100644 index ceb89cf64d4..00000000000 --- a/lib/private/db/oraclemigrator.php +++ /dev/null @@ -1,74 +0,0 @@ - - * @author Robin Appelman - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -use Doctrine\DBAL\Schema\Schema; - -class OracleMigrator extends NoCheckMigrator { - /** - * @param Schema $targetSchema - * @param \Doctrine\DBAL\Connection $connection - * @return \Doctrine\DBAL\Schema\SchemaDiff - */ - protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { - $schemaDiff = parent::getDiff($targetSchema, $connection); - - // oracle forces us to quote the identifiers - foreach ($schemaDiff->changedTables as $tableDiff) { - $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); - foreach ($tableDiff->changedColumns as $column) { - $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); - } - } - - return $schemaDiff; - } - - /** - * @param string $name - * @return string - */ - protected function generateTemporaryTableName($name) { - return 'oc_' . uniqid(); - } - - /** - * @param $statement - * @return string - */ - protected function convertStatementToScript($statement) { - if (substr($statement, -1) === ';') { - return $statement . PHP_EOL . '/' . PHP_EOL; - } - $script = $statement . ';'; - $script .= PHP_EOL; - $script .= PHP_EOL; - return $script; - } - - protected function getFilterExpression() { - return '/^"' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; - } - -} diff --git a/lib/private/db/pgsqltools.php b/lib/private/db/pgsqltools.php deleted file mode 100644 index e9e507551e6..00000000000 --- a/lib/private/db/pgsqltools.php +++ /dev/null @@ -1,70 +0,0 @@ - - * @author Morris Jobke - * @author tbelau666 - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; -use OCP\IConfig; - -/** -* Various PostgreSQL specific helper functions. -*/ -class PgSqlTools { - - /** @var \OCP\IConfig */ - private $config; - - /** - * @param \OCP\IConfig $config - */ - public function __construct(IConfig $config) { - $this->config = $config; - } - - /** - * @brief Resynchronizes all sequences of a database after using INSERTs - * without leaving out the auto-incremented column. - * @param \OC\DB\Connection $conn - * @return null - */ - public function resynchronizeDatabaseSequences(Connection $conn) { - $filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; - $databaseName = $conn->getDatabase(); - $conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); - - foreach ($conn->getSchemaManager()->listSequences() as $sequence) { - $sequenceName = $sequence->getName(); - $sqlInfo = 'SELECT table_schema, table_name, column_name - FROM information_schema.columns - WHERE column_default = ? AND table_catalog = ?'; - $sequenceInfo = $conn->fetchAssoc($sqlInfo, array( - "nextval('$sequenceName'::regclass)", - $databaseName - )); - $tableName = $sequenceInfo['table_name']; - $columnName = $sequenceInfo['column_name']; - $sqlMaxId = "SELECT MAX($columnName) FROM $tableName"; - $sqlSetval = "SELECT setval('$sequenceName', ($sqlMaxId))"; - $conn->executeQuery($sqlSetval); - } - } -} diff --git a/lib/private/db/querybuilder/compositeexpression.php b/lib/private/db/querybuilder/compositeexpression.php deleted file mode 100644 index 927dfe38378..00000000000 --- a/lib/private/db/querybuilder/compositeexpression.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB\QueryBuilder; - -use OCP\DB\QueryBuilder\ICompositeExpression; - -class CompositeExpression implements ICompositeExpression, \Countable { - /** @var \Doctrine\DBAL\Query\Expression\CompositeExpression */ - protected $compositeExpression; - - /** - * Constructor. - * - * @param \Doctrine\DBAL\Query\Expression\CompositeExpression $compositeExpression - */ - public function __construct(\Doctrine\DBAL\Query\Expression\CompositeExpression $compositeExpression) { - $this->compositeExpression = $compositeExpression; - } - - /** - * Adds multiple parts to composite expression. - * - * @param array $parts - * - * @return \OCP\DB\QueryBuilder\ICompositeExpression - */ - public function addMultiple(array $parts = array()) { - $this->compositeExpression->addMultiple($parts); - - return $this; - } - - /** - * Adds an expression to composite expression. - * - * @param mixed $part - * - * @return \OCP\DB\QueryBuilder\ICompositeExpression - */ - public function add($part) { - $this->compositeExpression->add($part); - - return $this; - } - - /** - * Retrieves the amount of expressions on composite expression. - * - * @return integer - */ - public function count() { - return $this->compositeExpression->count(); - } - - /** - * Returns the type of this composite expression (AND/OR). - * - * @return string - */ - public function getType() { - return $this->compositeExpression->getType(); - } - - /** - * Retrieves the string representation of this composite expression. - * - * @return string - */ - public function __toString() - { - return (string) $this->compositeExpression; - } -} diff --git a/lib/private/db/querybuilder/expressionbuilder/expressionbuilder.php b/lib/private/db/querybuilder/expressionbuilder/expressionbuilder.php deleted file mode 100644 index ce98816c42d..00000000000 --- a/lib/private/db/querybuilder/expressionbuilder/expressionbuilder.php +++ /dev/null @@ -1,370 +0,0 @@ - - * @author Robin Appelman - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB\QueryBuilder\ExpressionBuilder; - -use Doctrine\DBAL\Query\Expression\ExpressionBuilder as DoctrineExpressionBuilder; -use OC\DB\QueryBuilder\CompositeExpression; -use OC\DB\QueryBuilder\Literal; -use OC\DB\QueryBuilder\QueryFunction; -use OC\DB\QueryBuilder\QuoteHelper; -use OCP\DB\QueryBuilder\IExpressionBuilder; -use OCP\IDBConnection; - -class ExpressionBuilder implements IExpressionBuilder { - /** @var \Doctrine\DBAL\Query\Expression\ExpressionBuilder */ - protected $expressionBuilder; - - /** @var QuoteHelper */ - protected $helper; - - /** - * Initializes a new ExpressionBuilder. - * - * @param \OCP\IDBConnection $connection - */ - public function __construct(IDBConnection $connection) { - $this->helper = new QuoteHelper(); - $this->expressionBuilder = new DoctrineExpressionBuilder($connection); - } - - /** - * Creates a conjunction of the given boolean expressions. - * - * Example: - * - * [php] - * // (u.type = ?) AND (u.role = ?) - * $expr->andX('u.type = ?', 'u.role = ?')); - * - * @param mixed $x Optional clause. Defaults = null, but requires - * at least one defined when converting to string. - * - * @return \OCP\DB\QueryBuilder\ICompositeExpression - */ - public function andX($x = null) { - $arguments = func_get_args(); - $compositeExpression = call_user_func_array([$this->expressionBuilder, 'andX'], $arguments); - return new CompositeExpression($compositeExpression); - } - - /** - * Creates a disjunction of the given boolean expressions. - * - * Example: - * - * [php] - * // (u.type = ?) OR (u.role = ?) - * $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. - * - * @return \OCP\DB\QueryBuilder\ICompositeExpression - */ - public function orX($x = null) { - $arguments = func_get_args(); - $compositeExpression = call_user_func_array([$this->expressionBuilder, 'orX'], $arguments); - return new CompositeExpression($compositeExpression); - } - - /** - * Creates a comparison expression. - * - * @param mixed $x The left expression. - * @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 - * - * @return string - */ - public function comparison($x, $operator, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->comparison($x, $operator, $y); - } - - /** - * Creates an equality comparison expression with the given arguments. - * - * First argument is considered the left expression and the second is the right expression. - * When converted to string, it will generated a = . Example: - * - * [php] - * // u.id = ? - * $expr->eq('u.id', '?'); - * - * @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 - * - * @return string - */ - public function eq($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->eq($x, $y); - } - - /** - * Creates a non equality comparison expression with the given arguments. - * First argument is considered the left expression and the second is the right expression. - * When converted to string, it will generated a <> . Example: - * - * [php] - * // u.id <> 1 - * $q->where($q->expr()->neq('u.id', '1')); - * - * @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 - * - * @return string - */ - public function neq($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->neq($x, $y); - } - - /** - * Creates a lower-than comparison expression with the given arguments. - * First argument is considered the left expression and the second is the right expression. - * When converted to string, it will generated a < . Example: - * - * [php] - * // u.id < ? - * $q->where($q->expr()->lt('u.id', '?')); - * - * @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 - * - * @return string - */ - public function lt($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->lt($x, $y); - } - - /** - * Creates a lower-than-equal comparison expression with the given arguments. - * First argument is considered the left expression and the second is the right expression. - * When converted to string, it will generated a <= . Example: - * - * [php] - * // u.id <= ? - * $q->where($q->expr()->lte('u.id', '?')); - * - * @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 - * - * @return string - */ - public function lte($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->lte($x, $y); - } - - /** - * Creates a greater-than comparison expression with the given arguments. - * First argument is considered the left expression and the second is the right expression. - * When converted to string, it will generated a > . Example: - * - * [php] - * // u.id > ? - * $q->where($q->expr()->gt('u.id', '?')); - * - * @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 - * - * @return string - */ - public function gt($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->gt($x, $y); - } - - /** - * Creates a greater-than-equal comparison expression with the given arguments. - * First argument is considered the left expression and the second is the right expression. - * When converted to string, it will generated a >= . Example: - * - * [php] - * // u.id >= ? - * $q->where($q->expr()->gte('u.id', '?')); - * - * @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 - * - * @return string - */ - public function gte($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->gte($x, $y); - } - - /** - * Creates an IS NULL expression with the given arguments. - * - * @param string $x The field in string format to be restricted by IS NULL. - * - * @return string - */ - public function isNull($x) { - $x = $this->helper->quoteColumnName($x); - return $this->expressionBuilder->isNull($x); - } - - /** - * 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. - * - * @return string - */ - public function isNotNull($x) { - $x = $this->helper->quoteColumnName($x); - return $this->expressionBuilder->isNotNull($x); - } - - /** - * Creates a LIKE() comparison expression with the given arguments. - * - * @param 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 - * - * @return string - */ - public function like($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->like($x, $y); - } - - /** - * Creates a ILIKE() comparison expression with the given arguments. - * - * @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 - * - * @return string - * @since 9.0.0 - */ - public function iLike($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->comparison("LOWER($x)", 'LIKE', "LOWER($y)"); - } - - /** - * Creates a NOT LIKE() comparison expression with the given arguments. - * - * @param 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 - * - * @return string - */ - public function notLike($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->notLike($x, $y); - } - - /** - * Creates a IN () comparison expression with the given arguments. - * - * @param string $x The field in string format to be inspected by IN() comparison. - * @param 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 - * - * @return string - */ - public function in($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnNames($y); - return $this->expressionBuilder->in($x, $y); - } - - /** - * Creates a NOT IN () comparison expression with the given arguments. - * - * @param string $x The field in string format to be inspected by NOT IN() comparison. - * @param 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 - * - * @return string - */ - public function notIn($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnNames($y); - return $this->expressionBuilder->notIn($x, $y); - } - - /** - * Quotes a given input parameter. - * - * @param mixed $input The parameter to be quoted. - * @param mixed|null $type One of the IQueryBuilder::PARAM_* constants - * - * @return Literal - */ - public function literal($input, $type = null) { - return new Literal($this->expressionBuilder->literal($input, $type)); - } - - /** - * Returns a IQueryFunction that casts the column to the given type - * - * @param string $column - * @param mixed $type One of IQueryBuilder::PARAM_* - * @return string - */ - public function castColumn($column, $type) { - return new QueryFunction( - $this->helper->quoteColumnName($column) - ); - } -} diff --git a/lib/private/db/querybuilder/expressionbuilder/mysqlexpressionbuilder.php b/lib/private/db/querybuilder/expressionbuilder/mysqlexpressionbuilder.php deleted file mode 100644 index 0d34787d26a..00000000000 --- a/lib/private/db/querybuilder/expressionbuilder/mysqlexpressionbuilder.php +++ /dev/null @@ -1,40 +0,0 @@ - - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB\QueryBuilder\ExpressionBuilder; - - -use OC\DB\QueryBuilder\QueryFunction; -use OCP\DB\QueryBuilder\IQueryBuilder; - -class MySqlExpressionBuilder extends ExpressionBuilder { - - /** - * @inheritdoc - */ - public function iLike($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->comparison($x, ' COLLATE utf8_general_ci LIKE', $y); - } - -} diff --git a/lib/private/db/querybuilder/expressionbuilder/ociexpressionbuilder.php b/lib/private/db/querybuilder/expressionbuilder/ociexpressionbuilder.php deleted file mode 100644 index 5d615ae52e8..00000000000 --- a/lib/private/db/querybuilder/expressionbuilder/ociexpressionbuilder.php +++ /dev/null @@ -1,163 +0,0 @@ - - * @author Robin Appelman - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB\QueryBuilder\ExpressionBuilder; - - -use OC\DB\QueryBuilder\QueryFunction; -use OCP\DB\QueryBuilder\ILiteral; -use OCP\DB\QueryBuilder\IParameter; -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\DB\QueryBuilder\IQueryFunction; - -class OCIExpressionBuilder extends ExpressionBuilder { - - /** - * @param mixed $column - * @param mixed|null $type - * @return array|IQueryFunction|string - */ - protected function prepareColumn($column, $type) { - if ($type === IQueryBuilder::PARAM_STR && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) { - $column = $this->castColumn($column, $type); - } else { - $column = $this->helper->quoteColumnNames($column); - } - return $column; - } - - /** - * @inheritdoc - */ - public function comparison($x, $operator, $y, $type = null) { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->comparison($x, $operator, $y); - } - - /** - * @inheritdoc - */ - public function eq($x, $y, $type = null) { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->eq($x, $y); - } - - /** - * @inheritdoc - */ - public function neq($x, $y, $type = null) { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->neq($x, $y); - } - - /** - * @inheritdoc - */ - public function lt($x, $y, $type = null) { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->lt($x, $y); - } - - /** - * @inheritdoc - */ - public function lte($x, $y, $type = null) { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->lte($x, $y); - } - - /** - * @inheritdoc - */ - public function gt($x, $y, $type = null) { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->gt($x, $y); - } - - /** - * @inheritdoc - */ - public function gte($x, $y, $type = null) { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->gte($x, $y); - } - - /** - * @inheritdoc - */ - public function in($x, $y, $type = null) { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->in($x, $y); - } - - /** - * @inheritdoc - */ - public function notIn($x, $y, $type = null) { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->notIn($x, $y); - } - - /** - * Returns a IQueryFunction that casts the column to the given type - * - * @param string $column - * @param mixed $type One of IQueryBuilder::PARAM_* - * @return IQueryFunction - */ - public function castColumn($column, $type) { - if ($type === IQueryBuilder::PARAM_STR) { - $column = $this->helper->quoteColumnName($column); - return new QueryFunction('to_char(' . $column . ')'); - } - - return parent::castColumn($column, $type); - } - - /** - * @inheritdoc - */ - public function iLike($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return new QueryFunction('REGEXP_LIKE('.$x.', \'^\' || REPLACE('.$y.', \'%\', \'.*\') || \'$\', \'i\')'); - } -} diff --git a/lib/private/db/querybuilder/expressionbuilder/pgsqlexpressionbuilder.php b/lib/private/db/querybuilder/expressionbuilder/pgsqlexpressionbuilder.php deleted file mode 100644 index a8f1af87a45..00000000000 --- a/lib/private/db/querybuilder/expressionbuilder/pgsqlexpressionbuilder.php +++ /dev/null @@ -1,56 +0,0 @@ - - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB\QueryBuilder\ExpressionBuilder; - - -use OC\DB\QueryBuilder\QueryFunction; -use OCP\DB\QueryBuilder\IQueryBuilder; - -class PgSqlExpressionBuilder extends ExpressionBuilder { - - /** - * Returns a IQueryFunction that casts the column to the given type - * - * @param string $column - * @param mixed $type One of IQueryBuilder::PARAM_* - * @return string - */ - public function castColumn($column, $type) { - if ($type === IQueryBuilder::PARAM_INT) { - $column = $this->helper->quoteColumnName($column); - return new QueryFunction('CAST(' . $column . ' AS INT)'); - } - - return parent::castColumn($column, $type); - } - - /** - * @inheritdoc - */ - public function iLike($x, $y, $type = null) { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); - return $this->expressionBuilder->comparison($x, 'ILIKE', $y); - } - -} diff --git a/lib/private/db/querybuilder/literal.php b/lib/private/db/querybuilder/literal.php deleted file mode 100644 index 0cc96ab48b4..00000000000 --- a/lib/private/db/querybuilder/literal.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB\QueryBuilder; - -use OCP\DB\QueryBuilder\ILiteral; - -class Literal implements ILiteral{ - /** @var mixed */ - protected $literal; - - public function __construct($literal) { - $this->literal = $literal; - } - - /** - * @return string - */ - public function __toString() { - return (string) $this->literal; - } -} diff --git a/lib/private/db/querybuilder/parameter.php b/lib/private/db/querybuilder/parameter.php deleted file mode 100644 index 1c233c83f1d..00000000000 --- a/lib/private/db/querybuilder/parameter.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB\QueryBuilder; - -use OCP\DB\QueryBuilder\IParameter; - -class Parameter implements IParameter { - /** @var mixed */ - protected $name; - - public function __construct($name) { - $this->name = $name; - } - - /** - * @return string - */ - public function __toString() { - return (string) $this->name; - } -} diff --git a/lib/private/db/querybuilder/querybuilder.php b/lib/private/db/querybuilder/querybuilder.php deleted file mode 100644 index bb463e43a75..00000000000 --- a/lib/private/db/querybuilder/querybuilder.php +++ /dev/null @@ -1,1124 +0,0 @@ - - * @author Robin Appelman - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB\QueryBuilder; - -use Doctrine\DBAL\Platforms\MySqlPlatform; -use Doctrine\DBAL\Platforms\PostgreSqlPlatform; -use OC\DB\OracleConnection; -use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder; -use OC\DB\QueryBuilder\ExpressionBuilder\MySqlExpressionBuilder; -use OC\DB\QueryBuilder\ExpressionBuilder\OCIExpressionBuilder; -use OC\DB\QueryBuilder\ExpressionBuilder\PgSqlExpressionBuilder; -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\DB\QueryBuilder\IQueryFunction; -use OCP\DB\QueryBuilder\IParameter; -use OCP\IDBConnection; - -class QueryBuilder implements IQueryBuilder { - - /** @var \OCP\IDBConnection */ - private $connection; - - /** @var \Doctrine\DBAL\Query\QueryBuilder */ - private $queryBuilder; - - /** @var QuoteHelper */ - private $helper; - - /** @var bool */ - private $automaticTablePrefix = true; - - /** @var string */ - protected $lastInsertedTable; - - /** - * Initializes a new QueryBuilder. - * - * @param \OCP\IDBConnection $connection - */ - public function __construct(IDBConnection $connection) { - $this->connection = $connection; - $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection); - $this->helper = new QuoteHelper(); - } - - /** - * 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. - * @since 8.2.0 - */ - public function automaticTablePrefix($enabled) { - $this->automaticTablePrefix = (bool) $enabled; - } - - /** - * Gets an ExpressionBuilder used for object-oriented construction of query expressions. - * This producer method is intended for convenient inline usage. Example: - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where($qb->expr()->eq('u.id', 1)); - * - * - * For more complex expression construction, consider storing the expression - * builder object in a local variable. - * - * @return \OCP\DB\QueryBuilder\IExpressionBuilder - */ - public function expr() { - if ($this->connection instanceof OracleConnection) { - return new OCIExpressionBuilder($this->connection); - } else if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { - return new PgSqlExpressionBuilder($this->connection); - } else if ($this->connection->getDatabasePlatform() instanceof MySqlPlatform) { - return new MySqlExpressionBuilder($this->connection); - } else { - return new ExpressionBuilder($this->connection); - } - } - - /** - * Gets the type of the currently built query. - * - * @return integer - */ - public function getType() { - return $this->queryBuilder->getType(); - } - - /** - * Gets the associated DBAL Connection for this query builder. - * - * @return \OCP\IDBConnection - */ - public function getConnection() { - return $this->connection; - } - - /** - * Gets the state of this query builder instance. - * - * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. - */ - public function getState() { - return $this->queryBuilder->getState(); - } - - /** - * Executes this query using the bound parameters and their types. - * - * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} - * for insert, update and delete statements. - * - * @return \Doctrine\DBAL\Driver\Statement|int - */ - public function execute() { - return $this->queryBuilder->execute(); - } - - /** - * Gets the complete SQL string formed by the current specifications of this QueryBuilder. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('User', 'u') - * echo $qb->getSQL(); // SELECT u FROM User u - * - * - * @return string The SQL query string. - */ - public function getSQL() { - return $this->queryBuilder->getSQL(); - } - - /** - * Sets a query parameter for the query being constructed. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where('u.id = :user_id') - * ->setParameter(':user_id', 1); - * - * - * @param string|integer $key The parameter position or name. - * @param mixed $value The parameter value. - * @param string|null $type One of the IQueryBuilder::PARAM_* constants. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function setParameter($key, $value, $type = null) { - $this->queryBuilder->setParameter($key, $value, $type); - - return $this; - } - - /** - * Sets a collection of query parameters for the query being constructed. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where('u.id = :user_id1 OR u.id = :user_id2') - * ->setParameters(array( - * ':user_id1' => 1, - * ':user_id2' => 2 - * )); - * - * - * @param array $params The query parameters to set. - * @param array $types The query parameters types to set. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function setParameters(array $params, array $types = array()) { - $this->queryBuilder->setParameters($params, $types); - - return $this; - } - - /** - * Gets all defined query parameters for the query being constructed indexed by parameter index or name. - * - * @return array The currently defined query parameters indexed by parameter index or name. - */ - public function getParameters() { - return $this->queryBuilder->getParameters(); - } - - /** - * Gets a (previously set) query parameter of the query being constructed. - * - * @param mixed $key The key (index or name) of the bound parameter. - * - * @return mixed The value of the bound parameter. - */ - public function getParameter($key) { - return $this->queryBuilder->getParameter($key); - } - - /** - * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. - * - * @return array The currently defined query parameter types indexed by parameter index or name. - */ - public function getParameterTypes() { - return $this->queryBuilder->getParameterTypes(); - } - - /** - * Gets a (previously set) query parameter type of the query being constructed. - * - * @param mixed $key The key (index or name) of the bound parameter type. - * - * @return mixed The value of the bound parameter type. - */ - public function getParameterType($key) { - return $this->queryBuilder->getParameterType($key); - } - - /** - * Sets the position of the first result to retrieve (the "offset"). - * - * @param integer $firstResult The first result to return. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function setFirstResult($firstResult) { - $this->queryBuilder->setFirstResult($firstResult); - - return $this; - } - - /** - * 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. - * - * @return integer The position of the first result. - */ - public function getFirstResult() { - return $this->queryBuilder->getFirstResult(); - } - - /** - * Sets the maximum number of results to retrieve (the "limit"). - * - * NOTE: Setting max results to "0" will cause mixed behaviour. While most - * of the databases will just return an empty result set, Oracle will return - * all entries. - * - * @param integer $maxResults The maximum number of results to retrieve. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function setMaxResults($maxResults) { - $this->queryBuilder->setMaxResults($maxResults); - - return $this; - } - - /** - * Gets the maximum number of results the query object was set to retrieve (the "limit"). - * Returns NULL if {@link setMaxResults} was not applied to this query builder. - * - * @return integer The maximum number of results. - */ - public function getMaxResults() { - return $this->queryBuilder->getMaxResults(); - } - - /** - * Specifies an item that is to be returned in the query result. - * Replaces any previously specified selections, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.id', 'p.id') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); - * - * - * @param mixed $select The selection expressions. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function select($select = null) { - $selects = is_array($select) ? $select : func_get_args(); - - $this->queryBuilder->select( - $this->helper->quoteColumnNames($selects) - ); - - return $this; - } - - /** - * Specifies an item that is to be returned with a different name in the query result. - * - * - * $qb = $conn->getQueryBuilder() - * ->selectAlias('u.id', 'user_id') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); - * - * - * @param mixed $select The selection expressions. - * @param string $alias The column alias used in the constructed query. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function selectAlias($select, $alias) { - - $this->queryBuilder->addSelect( - $this->helper->quoteColumnName($select) . ' AS ' . $this->helper->quoteColumnName($alias) - ); - - return $this; - } - - /** - * Specifies an item that is to be returned uniquely in the query result. - * - * - * $qb = $conn->getQueryBuilder() - * ->selectDistinct('type') - * ->from('users'); - * - * - * @param mixed $select The selection expressions. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function selectDistinct($select) { - - $this->queryBuilder->addSelect( - 'DISTINCT ' . $this->helper->quoteColumnName($select) - ); - - return $this; - } - - /** - * Adds an item that is to be returned in the query result. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.id') - * ->addSelect('p.id') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); - * - * - * @param mixed $select The selection expression. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function addSelect($select = null) { - $selects = is_array($select) ? $select : func_get_args(); - - $this->queryBuilder->addSelect( - $this->helper->quoteColumnNames($selects) - ); - - return $this; - } - - /** - * Turns the query being built into a bulk delete query that ranges over - * a certain table. - * - * - * $qb = $conn->getQueryBuilder() - * ->delete('users', 'u') - * ->where('u.id = :user_id'); - * ->setParameter(':user_id', 1); - * - * - * @param string $delete The table whose rows are subject to the deletion. - * @param string $alias The table alias used in the constructed query. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function delete($delete = null, $alias = null) { - $this->queryBuilder->delete( - $this->getTableName($delete), - $alias - ); - - return $this; - } - - /** - * Turns the query being built into a bulk update query that ranges over - * a certain table - * - * - * $qb = $conn->getQueryBuilder() - * ->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where('u.id = ?'); - * - * - * @param string $update The table whose rows are subject to the update. - * @param string $alias The table alias used in the constructed query. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function update($update = null, $alias = null) { - $this->queryBuilder->update( - $this->getTableName($update), - $alias - ); - - return $this; - } - - /** - * Turns the query being built into an insert query that inserts into - * a certain table - * - * - * $qb = $conn->getQueryBuilder() - * ->insert('users') - * ->values( - * array( - * 'name' => '?', - * 'password' => '?' - * ) - * ); - * - * - * @param string $insert The table into which the rows should be inserted. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function insert($insert = null) { - $this->queryBuilder->insert( - $this->getTableName($insert) - ); - - $this->lastInsertedTable = $insert; - - return $this; - } - - /** - * Creates and adds a query root corresponding to the table identified by the - * given alias, forming a cartesian product with any existing query roots. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.id') - * ->from('users', 'u') - * - * - * @param string $from The table. - * @param string|null $alias The alias of the table. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function from($from, $alias = null) { - $this->queryBuilder->from( - $this->getTableName($from), - $alias - ); - - return $this; - } - - /** - * Creates and adds a join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @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. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function join($fromAlias, $join, $alias, $condition = null) { - $this->queryBuilder->join( - $fromAlias, - $this->getTableName($join), - $alias, - $condition - ); - - return $this; - } - - /** - * Creates and adds a join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @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. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function innerJoin($fromAlias, $join, $alias, $condition = null) { - $this->queryBuilder->innerJoin( - $fromAlias, - $this->getTableName($join), - $alias, - $condition - ); - - return $this; - } - - /** - * Creates and adds a left join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @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. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function leftJoin($fromAlias, $join, $alias, $condition = null) { - $this->queryBuilder->leftJoin( - $fromAlias, - $this->getTableName($join), - $alias, - $condition - ); - - return $this; - } - - /** - * Creates and adds a right join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @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. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function rightJoin($fromAlias, $join, $alias, $condition = null) { - $this->queryBuilder->rightJoin( - $fromAlias, - $this->getTableName($join), - $alias, - $condition - ); - - return $this; - } - - /** - * Sets a new value for a column in a bulk update query. - * - * - * $qb = $conn->getQueryBuilder() - * ->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where('u.id = ?'); - * - * - * @param string $key The column to set. - * @param string $value The value, expression, placeholder, etc. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function set($key, $value) { - $this->queryBuilder->set( - $this->helper->quoteColumnName($key), - $this->helper->quoteColumnName($value) - ); - - return $this; - } - - /** - * Specifies one or more restrictions to the query result. - * Replaces any previously specified restrictions, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->where('u.id = ?'); - * - * // You can optionally programatically 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)); - * - * $qb->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where($or); - * - * - * @param mixed $predicates The restriction predicates. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function where($predicates) { - call_user_func_array( - [$this->queryBuilder, 'where'], - func_get_args() - ); - - return $this; - } - - /** - * Adds one or more restrictions to the query results, forming a logical - * conjunction with any previously specified restrictions. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where('u.username LIKE ?') - * ->andWhere('u.is_active = 1'); - * - * - * @param mixed $where The query restrictions. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - * - * @see where() - */ - public function andWhere($where) { - call_user_func_array( - [$this->queryBuilder, 'andWhere'], - func_get_args() - ); - - return $this; - } - - /** - * Adds one or more restrictions to the query results, forming a logical - * disjunction with any previously specified restrictions. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->where('u.id = 1') - * ->orWhere('u.id = 2'); - * - * - * @param mixed $where The WHERE statement. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - * - * @see where() - */ - public function orWhere($where) { - call_user_func_array( - [$this->queryBuilder, 'orWhere'], - func_get_args() - ); - - return $this; - } - - /** - * Specifies a grouping over the results of the query. - * Replaces any previously specified groupings, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->groupBy('u.id'); - * - * - * @param mixed $groupBy The grouping expression. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function groupBy($groupBy) { - $groupBys = is_array($groupBy) ? $groupBy : func_get_args(); - - call_user_func_array( - [$this->queryBuilder, 'groupBy'], - $this->helper->quoteColumnNames($groupBys) - ); - - return $this; - } - - /** - * Adds a grouping expression to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->groupBy('u.lastLogin'); - * ->addGroupBy('u.createdAt') - * - * - * @param mixed $groupBy The grouping expression. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function addGroupBy($groupBy) { - $groupBys = is_array($groupBy) ? $groupBy : func_get_args(); - - call_user_func_array( - [$this->queryBuilder, 'addGroupBy'], - $this->helper->quoteColumnNames($groupBys) - ); - - return $this; - } - - /** - * Sets a value for a column in an insert query. - * - * - * $qb = $conn->getQueryBuilder() - * ->insert('users') - * ->values( - * array( - * 'name' => '?' - * ) - * ) - * ->setValue('password', '?'); - * - * - * @param string $column The column into which the value should be inserted. - * @param string $value The value that should be inserted into the column. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function setValue($column, $value) { - $this->queryBuilder->setValue( - $this->helper->quoteColumnName($column), - $value - ); - - return $this; - } - - /** - * Specifies values for an insert query indexed by column names. - * Replaces any previous values, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->insert('users') - * ->values( - * array( - * 'name' => '?', - * 'password' => '?' - * ) - * ); - * - * - * @param array $values The values to specify for the insert query indexed by column names. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function values(array $values) { - $quotedValues = []; - foreach ($values as $key => $value) { - $quotedValues[$this->helper->quoteColumnName($key)] = $value; - } - - $this->queryBuilder->values($quotedValues); - - return $this; - } - - /** - * Specifies a restriction over the groups of the query. - * Replaces any previous having restrictions, if any. - * - * @param mixed $having The restriction over the groups. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function having($having) { - call_user_func_array( - [$this->queryBuilder, 'having'], - func_get_args() - ); - - return $this; - } - - /** - * Adds a restriction over the groups of the query, forming a logical - * conjunction with any existing having restrictions. - * - * @param mixed $having The restriction to append. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function andHaving($having) { - call_user_func_array( - [$this->queryBuilder, 'andHaving'], - func_get_args() - ); - - return $this; - } - - /** - * Adds a restriction over the groups of the query, forming a logical - * disjunction with any existing having restrictions. - * - * @param mixed $having The restriction to add. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function orHaving($having) { - call_user_func_array( - [$this->queryBuilder, 'orHaving'], - func_get_args() - ); - - return $this; - } - - /** - * Specifies an ordering for the query results. - * Replaces any previously specified orderings, if any. - * - * @param string $sort The ordering expression. - * @param string $order The ordering direction. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function orderBy($sort, $order = null) { - $this->queryBuilder->orderBy( - $this->helper->quoteColumnName($sort), - $order - ); - - return $this; - } - - /** - * Adds an ordering to the query results. - * - * @param string $sort The ordering expression. - * @param string $order The ordering direction. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function addOrderBy($sort, $order = null) { - $this->queryBuilder->addOrderBy( - $this->helper->quoteColumnName($sort), - $order - ); - - return $this; - } - - /** - * Gets a query part by its name. - * - * @param string $queryPartName - * - * @return mixed - */ - public function getQueryPart($queryPartName) { - return $this->queryBuilder->getQueryPart($queryPartName); - } - - /** - * Gets all query parts. - * - * @return array - */ - public function getQueryParts() { - return $this->queryBuilder->getQueryParts(); - } - - /** - * Resets SQL parts. - * - * @param array|null $queryPartNames - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function resetQueryParts($queryPartNames = null) { - $this->queryBuilder->resetQueryParts($queryPartNames); - - return $this; - } - - /** - * Resets a single SQL part. - * - * @param string $queryPartName - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. - */ - public function resetQueryPart($queryPartName) { - $this->queryBuilder->resetQueryPart($queryPartName); - - return $this; - } - - /** - * Creates a new named parameter and bind the value $value to it. - * - * This method provides a shortcut for PDOStatement::bindValue - * when using prepared statements. - * - * The parameter $value specifies the value that you want to bind. If - * $placeholder is not provided bindValue() will automatically create a - * placeholder for you. An automatic placeholder will be of the name - * ':dcValue1', ':dcValue2' etc. - * - * For more information see {@link http://php.net/pdostatement-bindparam} - * - * Example: - * - * $value = 2; - * $q->eq( 'id', $q->bindValue( $value ) ); - * $stmt = $q->executeQuery(); // executed with 'id = 2' - * - * - * @license New BSD License - * @link http://www.zetacomponents.org - * - * @param mixed $value - * @param mixed $type - * @param string $placeHolder The name to bind with. The string must start with a colon ':'. - * - * @return IParameter the placeholder name used. - */ - public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $placeHolder = null) { - return new Parameter($this->queryBuilder->createNamedParameter($value, $type, $placeHolder)); - } - - /** - * Creates a new positional parameter and bind the given value to it. - * - * Attention: If you are using positional parameters with the query builder you have - * to be very careful to bind all parameters in the order they appear in the SQL - * statement , otherwise they get bound in the wrong order which can lead to serious - * bugs in your code. - * - * Example: - * - * $qb = $conn->getQueryBuilder(); - * $qb->select('u.*') - * ->from('users', 'u') - * ->where('u.username = ' . $qb->createPositionalParameter('Foo', IQueryBuilder::PARAM_STR)) - * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', IQueryBuilder::PARAM_STR)) - * - * - * @param mixed $value - * @param integer $type - * - * @return IParameter - */ - public function createPositionalParameter($value, $type = IQueryBuilder::PARAM_STR) { - return new Parameter($this->queryBuilder->createPositionalParameter($value, $type)); - } - - /** - * Creates a new parameter - * - * Example: - * - * $qb = $conn->getQueryBuilder(); - * $qb->select('u.*') - * ->from('users', 'u') - * ->where('u.username = ' . $qb->createParameter('name')) - * ->setParameter('name', 'Bar', IQueryBuilder::PARAM_STR)) - * - * - * @param string $name - * - * @return IParameter - */ - public function createParameter($name) { - return new Parameter(':' . $name); - } - - /** - * Creates a new function - * - * Attention: Column names inside the call have to be quoted before hand - * - * Example: - * - * $qb = $conn->getQueryBuilder(); - * $qb->select($qb->createFunction('COUNT(*)')) - * ->from('users', 'u') - * echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u - * - * - * $qb = $conn->getQueryBuilder(); - * $qb->select($qb->createFunction('COUNT(`column`)')) - * ->from('users', 'u') - * echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u - * - * - * @param string $call - * - * @return IQueryFunction - */ - public function createFunction($call) { - return new QueryFunction($call); - } - - /** - * Used to get the id of the last inserted element - * @return int - * @throws \BadMethodCallException When being called before an insert query has been run. - */ - public function getLastInsertId() { - if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable) { - // lastInsertId() needs the prefix but no quotes - $table = $this->prefixTableName($this->lastInsertedTable); - return (int) $this->connection->lastInsertId($table); - } - - throw new \BadMethodCallException('Invalid call to getLastInsertId without using insert() before.'); - } - - /** - * Returns the table name quoted and with database prefix as needed by the implementation - * - * @param string $table - * @return string - */ - public function getTableName($table) { - $table = $this->prefixTableName($table); - return $this->helper->quoteColumnName($table); - } - - /** - * Returns the table name with database prefix as needed by the implementation - * - * @param string $table - * @return string - */ - protected function prefixTableName($table) { - if ($this->automaticTablePrefix === false || strpos($table, '*PREFIX*') === 0) { - return $table; - } - - return '*PREFIX*' . $table; - } - - /** - * Returns the column name quoted and with table alias prefix as needed by the implementation - * - * @param string $column - * @param string $tableAlias - * @return string - */ - public function getColumnName($column, $tableAlias = '') { - if ($tableAlias !== '') { - $tableAlias .= '.'; - } - - return $this->helper->quoteColumnName($tableAlias . $column); - } -} diff --git a/lib/private/db/querybuilder/queryfunction.php b/lib/private/db/querybuilder/queryfunction.php deleted file mode 100644 index ac6d73f3cbf..00000000000 --- a/lib/private/db/querybuilder/queryfunction.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB\QueryBuilder; - -use OCP\DB\QueryBuilder\IQueryFunction; - -class QueryFunction implements IQueryFunction { - /** @var string */ - protected $function; - - public function __construct($function) { - $this->function = $function; - } - - /** - * @return string - */ - public function __toString() { - return (string) $this->function; - } -} diff --git a/lib/private/db/querybuilder/quotehelper.php b/lib/private/db/querybuilder/quotehelper.php deleted file mode 100644 index fda243a3786..00000000000 --- a/lib/private/db/querybuilder/quotehelper.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB\QueryBuilder; - -use OCP\DB\QueryBuilder\ILiteral; -use OCP\DB\QueryBuilder\IParameter; -use OCP\DB\QueryBuilder\IQueryFunction; - -class QuoteHelper { - /** - * @param array|string|ILiteral|IParameter|IQueryFunction $strings string, Literal or Parameter - * @return array|string - */ - public function quoteColumnNames($strings) { - if (!is_array($strings)) { - return $this->quoteColumnName($strings); - } - - $return = []; - foreach ($strings as $string) { - $return[] = $this->quoteColumnName($string); - } - - return $return; - } - - /** - * @param string|ILiteral|IParameter|IQueryFunction $string string, Literal or Parameter - * @return string - */ - public function quoteColumnName($string) { - if ($string instanceof IParameter || $string instanceof ILiteral || $string instanceof IQueryFunction) { - return (string) $string; - } - - if ($string === null || $string === 'null' || $string === '*') { - return $string; - } - - if (!is_string($string)) { - throw new \InvalidArgumentException('Only strings, Literals and Parameters are allowed'); - } - - if (substr_count($string, '.')) { - list($alias, $columnName) = explode('.', $string, 2); - - if ($columnName === '*') { - return $string; - } - - return $alias . '.`' . $columnName . '`'; - } - - return '`' . $string . '`'; - } -} diff --git a/lib/private/db/sqlitemigrator.php b/lib/private/db/sqlitemigrator.php deleted file mode 100644 index 8ea32581011..00000000000 --- a/lib/private/db/sqlitemigrator.php +++ /dev/null @@ -1,81 +0,0 @@ - - * @author Robin Appelman - * @author Thomas Müller - * @author Victor Dubiniuk - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Schema\Schema; - -class SQLiteMigrator extends Migrator { - - /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema - * @throws \OC\DB\MigrationException - * - * For sqlite we simple make a copy of the entire database, and test the migration on that - */ - public function checkMigrate(\Doctrine\DBAL\Schema\Schema $targetSchema) { - $dbFile = $this->connection->getDatabase(); - $tmpFile = $this->buildTempDatabase(); - copy($dbFile, $tmpFile); - - $connectionParams = array( - 'path' => $tmpFile, - 'driver' => 'pdo_sqlite', - ); - $conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); - try { - $this->applySchema($targetSchema, $conn); - $conn->close(); - unlink($tmpFile); - } catch (DBALException $e) { - $conn->close(); - unlink($tmpFile); - throw new MigrationException('', $e->getMessage()); - } - } - - /** - * @return string - */ - private function buildTempDatabase() { - $dataDir = $this->config->getSystemValue("datadirectory", \OC::$SERVERROOT . '/data'); - $tmpFile = uniqid("oc_"); - return "$dataDir/$tmpFile.db"; - } - - /** - * @param Schema $targetSchema - * @param \Doctrine\DBAL\Connection $connection - * @return \Doctrine\DBAL\Schema\SchemaDiff - */ - protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { - $platform = $connection->getDatabasePlatform(); - $platform->registerDoctrineTypeMapping('tinyint unsigned', 'integer'); - $platform->registerDoctrineTypeMapping('smallint unsigned', 'integer'); - $platform->registerDoctrineTypeMapping('varchar ', 'string'); - - return parent::getDiff($targetSchema, $connection); - } -} diff --git a/lib/private/db/sqlitesessioninit.php b/lib/private/db/sqlitesessioninit.php deleted file mode 100644 index 0683c47d08e..00000000000 --- a/lib/private/db/sqlitesessioninit.php +++ /dev/null @@ -1,65 +0,0 @@ - - * @author Robin Appelman - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -namespace OC\DB; - -use Doctrine\DBAL\Event\ConnectionEventArgs; -use Doctrine\DBAL\Events; -use Doctrine\Common\EventSubscriber; - -class SQLiteSessionInit implements EventSubscriber { - /** - * @var bool - */ - private $caseSensitiveLike; - - /** - * @var string - */ - private $journalMode; - - /** - * Configure case sensitive like for each connection - * - * @param bool $caseSensitiveLike - * @param string $journalMode - */ - public function __construct($caseSensitiveLike, $journalMode) { - $this->caseSensitiveLike = $caseSensitiveLike; - $this->journalMode = $journalMode; - } - - /** - * @param ConnectionEventArgs $args - * @return void - */ - public function postConnect(ConnectionEventArgs $args) { - $sensitive = ($this->caseSensitiveLike) ? 'true' : 'false'; - $args->getConnection()->executeUpdate('PRAGMA case_sensitive_like = ' . $sensitive); - $args->getConnection()->executeUpdate('PRAGMA journal_mode = ' . $this->journalMode); - } - - public function getSubscribedEvents() { - return array(Events::postConnect); - } -} diff --git a/lib/private/db/statementwrapper.php b/lib/private/db/statementwrapper.php deleted file mode 100644 index 6422d8d5f1b..00000000000 --- a/lib/private/db/statementwrapper.php +++ /dev/null @@ -1,122 +0,0 @@ - - * @author Bart Visscher - * @author Jörn Friedrich Dreyer - * @author Morris Jobke - * @author Robin Appelman - * @author Robin McCorkell - * @author Thomas Müller - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ - -/** - * small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement - * - * @method boolean bindValue(mixed $param, mixed $value, integer $type = null); - * @method string errorCode(); - * @method array errorInfo(); - * @method integer rowCount(); - * @method array fetchAll(integer $fetchMode = null); - */ -class OC_DB_StatementWrapper { - /** - * @var \Doctrine\DBAL\Driver\Statement - */ - private $statement = null; - private $isManipulation = false; - private $lastArguments = array(); - - /** - * @param boolean $isManipulation - */ - public function __construct($statement, $isManipulation) { - $this->statement = $statement; - $this->isManipulation = $isManipulation; - } - - /** - * pass all other function directly to the \Doctrine\DBAL\Driver\Statement - */ - public function __call($name,$arguments) { - return call_user_func_array(array($this->statement,$name), $arguments); - } - - /** - * make execute return the result instead of a bool - * - * @param array $input - * @return \OC_DB_StatementWrapper|int - */ - public function execute($input=array()) { - if(\OC::$server->getSystemConfig()->getValue( "log_query", false)) { - $params_str = str_replace("\n", " ", var_export($input, true)); - \OCP\Util::writeLog('core', 'DB execute with arguments : '.$params_str, \OCP\Util::DEBUG); - } - $this->lastArguments = $input; - if (count($input) > 0) { - $result = $this->statement->execute($input); - } else { - $result = $this->statement->execute(); - } - - if ($result === false) { - return false; - } - if ($this->isManipulation) { - $count = $this->statement->rowCount(); - return $count; - } else { - return $this; - } - } - - /** - * provide an alias for fetch - * - * @return mixed - */ - public function fetchRow() { - return $this->statement->fetch(); - } - - /** - * Provide a simple fetchOne. - * - * fetch single column from the next row - * @param int $column the column number to fetch - * @return string - */ - public function fetchOne($column = 0) { - return $this->statement->fetchColumn($column); - } - - /** - * Binds a PHP variable to a corresponding named or question mark placeholder in the - * SQL statement that was use to prepare the statement. - * - * @param mixed $column Either the placeholder name or the 1-indexed placeholder index - * @param mixed $variable The variable to bind - * @param integer|null $type one of the PDO::PARAM_* constants - * @param integer|null $length max length when using an OUT bind - * @return boolean - */ - public function bindParam($column, &$variable, $type = null, $length = null){ - return $this->statement->bindParam($column, $variable, $type, $length); - } -} diff --git a/lib/private/legacy/db/statementwrapper.php b/lib/private/legacy/db/statementwrapper.php new file mode 100644 index 00000000000..6422d8d5f1b --- /dev/null +++ b/lib/private/legacy/db/statementwrapper.php @@ -0,0 +1,122 @@ + + * @author Bart Visscher + * @author Jörn Friedrich Dreyer + * @author Morris Jobke + * @author Robin Appelman + * @author Robin McCorkell + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +/** + * small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement + * + * @method boolean bindValue(mixed $param, mixed $value, integer $type = null); + * @method string errorCode(); + * @method array errorInfo(); + * @method integer rowCount(); + * @method array fetchAll(integer $fetchMode = null); + */ +class OC_DB_StatementWrapper { + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $statement = null; + private $isManipulation = false; + private $lastArguments = array(); + + /** + * @param boolean $isManipulation + */ + public function __construct($statement, $isManipulation) { + $this->statement = $statement; + $this->isManipulation = $isManipulation; + } + + /** + * pass all other function directly to the \Doctrine\DBAL\Driver\Statement + */ + public function __call($name,$arguments) { + return call_user_func_array(array($this->statement,$name), $arguments); + } + + /** + * make execute return the result instead of a bool + * + * @param array $input + * @return \OC_DB_StatementWrapper|int + */ + public function execute($input=array()) { + if(\OC::$server->getSystemConfig()->getValue( "log_query", false)) { + $params_str = str_replace("\n", " ", var_export($input, true)); + \OCP\Util::writeLog('core', 'DB execute with arguments : '.$params_str, \OCP\Util::DEBUG); + } + $this->lastArguments = $input; + if (count($input) > 0) { + $result = $this->statement->execute($input); + } else { + $result = $this->statement->execute(); + } + + if ($result === false) { + return false; + } + if ($this->isManipulation) { + $count = $this->statement->rowCount(); + return $count; + } else { + return $this; + } + } + + /** + * provide an alias for fetch + * + * @return mixed + */ + public function fetchRow() { + return $this->statement->fetch(); + } + + /** + * Provide a simple fetchOne. + * + * fetch single column from the next row + * @param int $column the column number to fetch + * @return string + */ + public function fetchOne($column = 0) { + return $this->statement->fetchColumn($column); + } + + /** + * Binds a PHP variable to a corresponding named or question mark placeholder in the + * SQL statement that was use to prepare the statement. + * + * @param mixed $column Either the placeholder name or the 1-indexed placeholder index + * @param mixed $variable The variable to bind + * @param integer|null $type one of the PDO::PARAM_* constants + * @param integer|null $length max length when using an OUT bind + * @return boolean + */ + public function bindParam($column, &$variable, $type = null, $length = null){ + return $this->statement->bindParam($column, $variable, $type, $length); + } +} -- cgit v1.2.3