]> source.dussan.org Git - nextcloud-server.git/commitdiff
merge master into doctrine-object
authorRobin Appelman <icewind@owncloud.com>
Tue, 6 Aug 2013 13:43:58 +0000 (15:43 +0200)
committerRobin Appelman <icewind@owncloud.com>
Tue, 6 Aug 2013 13:43:58 +0000 (15:43 +0200)
1  2 
lib/db.php
lib/db/connection.php
lib/db/mdb2schemamanager.php

diff --cc lib/db.php
index fec4eaaf93fd0cc038170e227fd94eceadffc9fc,a96f4697235256a4d3d168e0af600be477e7ad21..2b6cd59366a55d89cfd4b97150161b0540f4da11
@@@ -41,15 -41,35 +41,16 @@@ class DatabaseException extends Excepti
   * Doctrine with some adaptions.
   */
  class OC_DB {
 -      const BACKEND_DOCTRINE=2;
 -
 -      static private $preparedQueries = array();
 -      static private $cachingEnabled = true;
 -
        /**
 -       * @var \Doctrine\DBAL\Connection
 +       * @var \OC\DB\Connection $connection
         */
 -      static private $connection; //the preferred connection to use, only Doctrine
 -      static private $backend=null;
 -      /**
 -       * @var \Doctrine\DBAL\Connection
 -       */
 -      static private $DOCTRINE=null;
 +      static private $connection; //the prefered connection to use, only Doctrine
  
+       static private $prefix=null;
        static private $type=null;
  
 -      /**
 -       * check which backend we should use
 -       * @return int BACKEND_DOCTRINE
 -       */
 -      private static function getDBBackend() {
 -              return self::BACKEND_DOCTRINE;
 -      }
 -
        /**
         * @brief connects to the database
 -       * @param int $backend
         * @return bool true if connection can be established or false on error
         *
         * Connects to the database as specified in config.php
         * the current check allows some whitespace but does not work with IF EXISTS or other more complex statements
         * 
         * @param string $sql
++       * @return bool
         */
        static public function isManipulation( $sql ) {
--              $selectOccurence = stripos ($sql, "SELECT");
--              if ($selectOccurence !== false && $selectOccurence < 10) {
++              $selectOccurrence = stripos ($sql, "SELECT");
++              if ($selectOccurrence !== false && $selectOccurrence < 10) {
                        return false;
                }
--              $insertOccurence = stripos ($sql, "INSERT");
--              if ($insertOccurence !== false && $insertOccurence < 10) {
++              $insertOccurrence = stripos ($sql, "INSERT");
++              if ($insertOccurrence !== false && $insertOccurrence < 10) {
                        return true;
                }
--              $updateOccurence = stripos ($sql, "UPDATE");
--              if ($updateOccurence !== false && $updateOccurence < 10) {
++              $updateOccurrence = stripos ($sql, "UPDATE");
++              if ($updateOccurrence !== false && $updateOccurrence < 10) {
                        return true;
                }
--              $deleteOccurance = stripos ($sql, "DELETE");
--              if ($deleteOccurance !== false && $deleteOccurance < 10) {
++              $deleteOccurrence = stripos ($sql, "DELETE");
++              if ($deleteOccurrence !== false && $deleteOccurrence < 10) {
                        return true;
                }
                return false;
         */
        public static function insertid($table=null) {
                self::connect();
 -              $type = OC_Config::getValue( "dbtype", "sqlite" );
 -              if( $type === 'pgsql' ) {
 -                      $result = self::executeAudited('SELECT lastval() AS id');
 -                      $row = $result->fetchRow();
 -                      self::raiseExceptionOnError($row, 'fetching row for insertid failed');
 -                      return (int)$row['id'];
 -              } else if( $type === 'mssql') {
 -                      if($table !== null) {
 -                              $prefix = OC_Config::getValue( "dbtableprefix", "oc_" );
 -                              $table = str_replace( '*PREFIX*', $prefix, $table );
 -                      }
 -                      return self::$connection->lastInsertId($table);
 -              }
 -              if( $type === 'oci' ) {
 -                      if($table !== null) {
 -                              $prefix = OC_Config::getValue( "dbtableprefix", "oc_" );
 -                              $suffix = '_SEQ';
 -                              $table = '"'.str_replace( '*PREFIX*', $prefix, $table ).$suffix.'"';
 -                      }
 -                      return self::$connection->lastInsertId($table);
 -              } else {
 -                      if($table !== null) {
 -                              $prefix = OC_Config::getValue( "dbtableprefix", "oc_" );
 -                              $suffix = OC_Config::getValue( "dbsequencesuffix", "_id_seq" );
 -                              $table = str_replace( '*PREFIX*', $prefix, $table ).$suffix;
 -                      }
 -                      $result = self::$connection->lastInsertId($table);
 -              }
 -              self::raiseExceptionOnError($result, 'insertid failed');
 -              return (int)$result;
 +              return self::$connection->lastInsertId($table);
 +      }
 +
 +      /**
 +       * @brief Insert a row if a matching row doesn't exists.
 +       * @param string $table. The table to insert into in the form '*PREFIX*tableName'
 +       * @param array $input. An array of fieldname/value pairs
-        * @returns int number of updated rows
++       * @return int number of updated rows
 +       */
 +      public static function insertIfNotExist($table, $input) {
 +              self::connect();
 +              return self::$connection->insertIfNotExist($table, $input);
 +      }
 +
 +      /**
 +       * Start a transaction
 +       */
 +      public static function beginTransaction() {
 +              self::connect();
 +              self::$connection->beginTransaction();
 +      }
 +
 +      /**
 +       * Commit the database changes done during a transaction that is in progress
 +       */
 +      public static function commit() {
 +              self::connect();
 +              self::$connection->commit();
        }
  
        /**
        public static function disconnect() {
                // Cut connection if required
                if(self::$connection) {
 -                      self::$connection=false;
 -                      self::$DOCTRINE=false;
 +                      self::$connection->close();
                }
 -
 -              return true;
        }
  
--      /** else {
-        * @brief saves database scheme to xml file
++      /**
+        * @brief saves database schema to xml file
         * @param string $file name of file
         * @param int $mode
         * @return bool
         *
         * TODO: write more documentation
         */
--      public static function getDbStructure( $file, $mode=MDB2_SCHEMA_DUMP_STRUCTURE) {
-               self::connect();
-               return OC_DB_Schema::getDbStructure(self::$connection, $file);
++      public static function getDbStructure( $file, $mode = 0) {
+               $schemaManager = self::getMDB2SchemaManager();
+               return $schemaManager->getDbStructure($file);
        }
  
        /**
         * @param $file string path to the MDB2 xml db export file
         */
        public static function replaceDB( $file ) {
-               self::connect();
-               OC_DB_Schema::replaceDB(self::$connection, $file);
+               $schemaManager = self::getMDB2SchemaManager();
+               $schemaManager->replaceDB($file);
        }
  
 -      /**
 -       * Start a transaction
 -       */
 -      public static function beginTransaction() {
 -              self::connect();
 -              self::$connection->beginTransaction();
 -      }
 -
 -      /**
 -       * Commit the database changes done during a transaction that is in progress
 -       */
 -      public static function commit() {
 -              self::connect();
 -              self::$connection->commit();
 -      }
 -
        /**
         * check if a result is an error, works with Doctrine
         * @param mixed $result
index 920fe144a8aaa9d1106f3dcd9a1dc9b62d0ec46d,0000000000000000000000000000000000000000..7f207ff76ecdb02cb20ec1bc3acc352c0bf9231b
mode 100644,000000..100644
--- /dev/null
@@@ -1,197 -1,0 +1,196 @@@
-               $rawQuery = $statement;
 +<?php
 +/**
 + * Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl>
 + * This file is licensed under the Affero General Public License version 3 or
 + * later.
 + * See the COPYING-README file.
 + */
 +
 +namespace OC\DB;
 +use Doctrine\DBAL\Driver;
 +use Doctrine\DBAL\Configuration;
 +use Doctrine\DBAL\Cache\QueryCacheProfile;
 +use Doctrine\Common\EventManager;
 +
 +class Connection extends \Doctrine\DBAL\Connection {
 +      /**
 +       * @var string $table_prefix
 +       */
 +      protected $table_prefix;
 +
 +      /**
 +       * @var \OC\DB\Adapter $adapter
 +       */
 +      protected $adapter;
 +
 +      /**
 +       * @var \Doctrine\DBAL\Driver\Statement[] $preparedQueries
 +       */
 +      protected $preparedQueries = array();
 +
 +      protected $cachingQueryStatementEnabled = true;
 +
 +      /**
 +       * 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['table_prefix'])) {
 +                      throw new \Exception('table_prefix not set');
 +              }
 +              parent::__construct($params, $driver, $config, $eventManager);
 +              $this->adapter = new $params['adapter']($this);
 +              $this->table_prefix = $params['table_prefix'];
 +      }
 +
 +      /**
 +       * 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 ) {
 +              $statement = $this->replaceTablePrefix($statement);
 +              $statement = $this->adapter->fixupStatement($statement);
 +
 +              if ($limit === -1) {
 +                      $limit = null;
 +              }
 +              if (!is_null($limit)) {
 +                      $platform = $this->getDatabasePlatform();
 +                      $statement = $platform->modifyLimitQuery($statement, $limit, $offset);
 +              } else {
 +                      if (isset($this->preparedQueries[$statement]) && $this->cachingQueryStatementEnabled) {
 +                              return $this->preparedQueries[$statement];
 +                      }
 +              }
-                       $this->preparedQueries[$rawQuery] = $result;
 +              if(\OC_Config::getValue( "log_query", false)) {
 +                      \OC_Log::write('core', 'DB prepare : '.$statement, \OC_Log::DEBUG);
 +              }
 +              $result = parent::prepare($statement);
 +              if (is_null($limit) && $this->cachingQueryStatementEnabled) {
++                      $this->preparedQueries[$statement] = $result;
 +              }
 +              return $result;
 +      }
 +
 +      /**
 +       * Executes an, optionally parameterized, SQL query.
 +       *
 +       * If the query is parameterized, 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 QueryCacheProfile $qcp
 +       * @return \Doctrine\DBAL\Driver\Statement The executed statement.
 +       * @internal PERF: Directly prepares a driver statement, not a wrapper.
 +       */
 +      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.
 +       * @internal PERF: Directly prepares a driver statement, not a wrapper.
 +       */
 +      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);
 +      }
 +
 +      /**
 +       * @brief Insert a row if a matching row doesn't exists.
 +       * @param string $table. The table to insert into in the form '*PREFIX*tableName'
 +       * @param array $input. An array of fieldname/value pairs
 +       * @return bool The return value from execute()
 +       */
 +      public function insertIfNotExist($table, $input) {
 +              return $this->adapter->insertIfNotExist($table, $input);
 +      }
 +
 +      /**
 +       * 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;
 +      }
 +
 +      // internal use
 +      /**
 +       * @param string $statement
 +       * @return string
 +       */
 +      protected function replaceTablePrefix($statement) {
 +              return str_replace( '*PREFIX*', $this->table_prefix, $statement );
 +      }
 +
 +      public function enableQueryStatementCaching() {
 +              $this->cachingQueryStatementEnabled = true;
 +      }
 +
 +      public function disableQueryStatementCaching() {
 +              $this->cachingQueryStatementEnabled = false;
 +              $this->preparedQueries = array();
 +      }
 +}
index 0000000000000000000000000000000000000000,a34bc9dae7509d8f53e5bb16f8de591543b91ef3..8e76f46c78fba481265e22a8cab6c92204ae5382
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,150 +1,150 @@@
 -       * @var \Doctrine\DBAL\Connection $conn
+ <?php
+ /**
+  * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+  * This file is licensed under the Affero General Public License version 3 or
+  * later.
+  * See the COPYING-README file.
+  */
+ namespace OC\DB;
+ class MDB2SchemaManager {
+       /**
 -       * @param \Doctrine\DBAL\Connection $conn
++       * @var \OC\DB\Connection $conn
+        */
+       protected $conn;
+       /**
 -              $this->conn->execute($sql);
++       * @param \OC\DB\Connection $conn
+        */
+       public function __construct($conn) {
+               $this->conn = $conn;
+       }
+       /**
+        * @brief saves database scheme to xml file
+        * @param string $file name of file
+        * @param int|string $mode
+        * @return bool
+        *
+        * TODO: write more documentation
+        */
+       public function getDbStructure( $file, $mode = MDB2_SCHEMA_DUMP_STRUCTURE) {
+               $sm = $this->conn->getSchemaManager();
+               return \OC_DB_MDB2SchemaWriter::saveSchemaToFile($file, $sm);
+       }
+       /**
+        * @brief 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_Config::getObject(), $this->conn->getDatabasePlatform());
+               $toSchema = $schemaReader->loadSchemaFromFile($file);
+               return $this->executeSchemaChange($toSchema);
+       }
+       /**
+        * @brief update the database scheme
+        * @param string $file file to read structure from
+        * @return bool
+        */
+       public function updateDbFromStructure($file) {
+               $sm = $this->conn->getSchemaManager();
+               $fromSchema = $sm->createSchema();
+               $schemaReader = new MDB2SchemaReader(\OC_Config::getObject(), $this->conn->getDatabasePlatform());
+               $toSchema = $schemaReader->loadSchemaFromFile($file);
+               // remove tables we don't know about
+               foreach($fromSchema->getTables() as $table) {
+                       if (!$toSchema->hasTable($table->getName())) {
+                               $fromSchema->dropTable($table->getName());
+                       }
+               }
+               // remove sequences we don't know about
+               foreach($fromSchema->getSequences() as $table) {
+                       if (!$toSchema->hasSequence($table->getName())) {
+                               $fromSchema->dropSequence($table->getName());
+                       }
+               }
+               $comparator = new \Doctrine\DBAL\Schema\Comparator();
+               $schemaDiff = $comparator->compare($fromSchema, $toSchema);
+               $platform = $this->conn->getDatabasePlatform();
+               $tables = $schemaDiff->newTables + $schemaDiff->changedTables + $schemaDiff->removedTables;
+               foreach($tables as $tableDiff) {
+                       $tableDiff->name = $platform->quoteIdentifier($tableDiff->name);
+               }
+               return $this->executeSchemaChange($schemaDiff);
+       }
+       /**
+        * @brief drop a table
+        * @param string $tableName the table to drop
+        */
+       public function dropTable($tableName) {
+               $sm = $this->conn->getSchemaManager();
+               $fromSchema = $sm->createSchema();
+               $toSchema = clone $fromSchema;
+               $toSchema->dropTable($tableName);
+               $sql = $fromSchema->getMigrateToSql($toSchema, $this->conn->getDatabasePlatform());
 -              $this->removeDBStructure( OC::$SERVERROOT . '/db_structure.xml' );
++              $this->conn->executeQuery($sql);
+       }
+       /**
+        * 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_Config::getObject(), $this->conn->getDatabasePlatform());
+               $fromSchema = $schemaReader->loadSchemaFromFile($file);
+               $toSchema = clone $fromSchema;
+               foreach($toSchema->getTables() as $table) {
+                       $toSchema->dropTable($table->getName());
+               }
+               $comparator = new \Doctrine\DBAL\Schema\Comparator();
+               $schemaDiff = $comparator->compare($fromSchema, $toSchema);
+               $this->executeSchemaChange($schemaDiff);
+       }
+       /**
+        * @brief replaces the ownCloud tables with a new set
+        * @param $file string path to the MDB2 xml db export file
+        */
+       public function replaceDB( $file ) {
+               $apps = \OC_App::getAllApps();
+               $this->conn->beginTransaction();
+               // Delete the old tables
++              $this->removeDBStructure( \OC::$SERVERROOT . '/db_structure.xml' );
+               foreach($apps as $app) {
+                       $path = \OC_App::getAppPath($app).'/appinfo/database.xml';
+                       if(file_exists($path)) {
+                               $this->removeDBStructure( $path );
+                       }
+               }
+               // Create new tables
+               $this->conn->commit();
+       }
+       /**
+        * @param \Doctrine\DBAL\Schema\Schema $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();
+               return true;
+       }
+ }