]> source.dussan.org Git - nextcloud-server.git/commitdiff
Merge branch 'master' into doctrine
authorBart Visscher <bartv@thisnet.nl>
Thu, 11 Jul 2013 16:47:19 +0000 (18:47 +0200)
committerBart Visscher <bartv@thisnet.nl>
Sat, 13 Jul 2013 10:01:14 +0000 (12:01 +0200)
Conflicts:
3rdparty
lib/db.php
lib/setup.php
tests/lib/db.php
tests/lib/dbschema.php

1  2 
3rdparty
lib/db.php
lib/db/statementwrapper.php
tests/lib/db.php

diff --cc 3rdparty
index 691791a4f743aaa83546736928e3ce18574f3c03,217626723957161191572ea50172a3943c30696d..c8623cc80d47022cb25874b69849cd2f57fd4874
+++ b/3rdparty
@@@ -1,1 -1,1 +1,1 @@@
- Subproject commit 691791a4f743aaa83546736928e3ce18574f3c03
 -Subproject commit 217626723957161191572ea50172a3943c30696d
++Subproject commit c8623cc80d47022cb25874b69849cd2f57fd4874
diff --cc lib/db.php
index e38d464e75508b3233c0853f1a6c824e257dc180,6fec60e53ce85d3c364419a0d745550acbd81ead..d911e14804fd91e54569f3839e28403ae6814cc7
@@@ -199,19 -305,35 +199,20 @@@ class OC_DB 
         * @param string $query Query string
         * @param int $limit
         * @param int $offset
 -       * @return MDB2_Statement_Common prepared SQL query
+        * @param bool $isManipulation
 +       * @throws DatabaseException
 +       * @return \Doctrine\DBAL\Statement prepared SQL query
         *
 -       * SQL query via MDB2 prepare(), needs to be execute()'d!
 +       * SQL query via Doctrine prepare(), needs to be execute()'d!
         */
-       static public function prepare( $query , $limit=null, $offset=null ) {
+       static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) {
  
                if (!is_null($limit) && $limit != -1) {
 -                      if (self::$backend == self::BACKEND_MDB2) {
 -                              //MDB2 uses or emulates limits & offset internally
 -                              self::$MDB2->setLimit($limit, $offset);
 -                      } else {
 -                              //PDO does not handle limit and offset.
 -                              //FIXME: check limit notation for other dbs
 -                              //the following sql thus might needs to take into account db ways of representing it
 -                              //(oracle has no LIMIT / OFFSET)
 -                              $limit = (int)$limit;
 -                              $limitsql = ' LIMIT ' . $limit;
 -                              if (!is_null($offset)) {
 -                                      $offset = (int)$offset;
 -                                      $limitsql .= ' OFFSET ' . $offset;
 -                              }
 -                              //insert limitsql
 -                              if (substr($query, -1) == ';') { //if query ends with ;
 -                                      $query = substr($query, 0, -1) . $limitsql . ';';
 -                              } else {
 -                                      $query.=$limitsql;
 -                              }
 +                      if ($limit === -1) {
 +                              $limit = null;
                        }
 +                      $platform = self::$connection->getDatabasePlatform();
 +                      $query = $platform->modifyLimitQuery($query, $limit, $offset);
                } else {
                        if (isset(self::$preparedQueries[$query]) and self::$cachingEnabled) {
                                return self::$preparedQueries[$query];
                        OC_Log::write('core', 'DB prepare : '.$query, OC_Log::DEBUG);
                }
                self::connect();
+               
+               if ($isManipulation === null) {
+                       //try to guess, so we return the number of rows on manipulations
+                       $isManipulation = self::isManipulation($query);
+               }
+               
                // return the result
 -              if(self::$backend==self::BACKEND_MDB2) {
 -                      // differentiate between query and manipulation
 -                      if ($isManipulation) {
 -                              $result = self::$connection->prepare( $query, null, MDB2_PREPARE_MANIP );
 -                      } else {
 -                              $result = self::$connection->prepare( $query, null, MDB2_PREPARE_RESULT );
 -                      }
 -
 -                      // Die if we have an error (error means: bad query, not 0 results!)
 -                      if( self::isError($result)) {
 -                              throw new DatabaseException($result->getMessage(), $query);
 -                      }
 -              }else{
 -                      try{
 +              if (self::$backend == self::BACKEND_DOCTRINE) {
 +                      try {
                                $result=self::$connection->prepare($query);
 -                      }catch(PDOException $e) {
 -                              throw new DatabaseException($e->getMessage(), $query);
 +                      } catch(\Doctrine\DBAL\DBALException $e) {
 +                              throw new \DatabaseException($e->getMessage(), $query);
                        }
-                       $result=new OC_DB_StatementWrapper($result);
+                       // differentiate between query and manipulation
 -                      $result = new PDOStatementWrapper($result, $isManipulation);
++                      $result=new OC_DB_StatementWrapper($result, $isManipulation);
                }
                if ((is_null($limit) || $limit == -1) and self::$cachingEnabled ) {
                        $type = OC_Config::getValue( "dbtype", "sqlite" );
                }
                return $result;
        }
+       
+       /**
+        * tries to guess the type of statement based on the first 10 characters
+        * the current check allows some whitespace but does not work with IF EXISTS or other more complex statements
+        * 
+        * @param string $sql
+        */
+       static public function isManipulation( $sql ) {
+               $selectOccurence = stripos ($sql, "SELECT");
+               if ($selectOccurence !== false && $selectOccurence < 10) {
+                       return false;
+               }
+               $insertOccurence = stripos ($sql, "INSERT");
+               if ($insertOccurence !== false && $insertOccurence < 10) {
+                       return true;
+               }
+               $updateOccurence = stripos ($sql, "UPDATE");
+               if ($updateOccurence !== false && $updateOccurence < 10) {
+                       return true;
+               }
+               $deleteOccurance = stripos ($sql, "DELETE");
+               if ($deleteOccurance !== false && $deleteOccurance < 10) {
+                       return true;
+               }
+               return false;
+       }
+       
        /**
         * @brief execute a prepared statement, on error write log and throw exception
 -       * @param mixed $stmt PDOStatementWrapper | MDB2_Statement_Common ,
 +       * @param mixed $stmt OC_DB_StatementWrapper,
         *                                        an array with 'sql' and optionally 'limit' and 'offset' keys
         *                                      .. or a simple sql query string
         * @param array $parameters
         * @return bool
         */
        public static function isError($result) {
-               if(!$result) {
 -              //MDB2 returns an MDB2_Error object
 -              if (class_exists('PEAR') === true && PEAR::isError($result)) {
--                      return true;
-               } else {
-                       return false;
 -              }
 -              //PDO returns false on error (and throws an exception)
 -              if (self::$backend===self::BACKEND_PDO and $result === false) {
 -                      return true;
--              }
 -
 -              return false;
++              //Doctrine returns false on error (and throws an exception)
++              return $result === false;
        }
        /**
 -       * check if a result is an error and throws an exception, works with MDB2 and PDOException
 +       * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException
         * @param mixed $result
         * @param string $message
         * @return void
index 0d6501864122aeb51be15f9a19d5c968d66f3721,0000000000000000000000000000000000000000..f7bc45e068f8385fcbabbfee5602ddd4039fdcab
mode 100644,000000..100644
--- /dev/null
@@@ -1,186 -1,0 +1,191 @@@
-       private $statement=null;
-       private $lastArguments=array();
 +<?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.
 + */
 +
 +/**
 + * small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement
 + */
 +class OC_DB_StatementWrapper {
 +      /**
 +       * @var \Doctrine\DBAL\Driver\Statement
 +       */
-       public function __construct($statement) {
-               $this->statement=$statement;
++      private $statement = null;
++      private $isManipulation = false;
++      private $lastArguments = array();
 +
-                       $result=$this->statement->execute($input);
++      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);
 +      }
 +
 +      /**
 +       * provide numRows
 +       */
 +      public function numRows() {
 +              $type = OC_Config::getValue( "dbtype", "sqlite" );
 +              if ($type == 'oci') {
 +                      // OCI doesn't have a queryString, just do a rowCount for now
 +                      return $this->statement->rowCount();
 +              }
 +              $regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/i';
 +              $queryString = $this->statement->getWrappedStatement()->queryString;
 +              if (preg_match($regex, $queryString, $output) > 0) {
 +                      $query = OC_DB::prepare("SELECT COUNT(*) FROM {$output[1]}");
 +                      return $query->execute($this->lastArguments)->fetchColumn();
 +              }else{
 +                      return $this->statement->rowCount();
 +              }
 +      }
 +
 +      /**
 +       * make execute return the result instead of a bool
 +       */
 +      public function execute($input=array()) {
 +              if(OC_Config::getValue( "log_query", false)) {
 +                      $params_str = str_replace("\n"," ",var_export($input,true));
 +                      OC_Log::write('core', 'DB execute with arguments : '.$params_str, OC_Log::DEBUG);
 +              }
 +              $this->lastArguments = $input;
 +              if (count($input) > 0) {
 +
 +                      if (!isset($type)) {
 +                              $type = OC_Config::getValue( "dbtype", "sqlite" );
 +                      }
 +
 +                      if ($type == 'mssql') {
 +                              $input = $this->tryFixSubstringLastArgumentDataForMSSQL($input);
 +                      }
 +
-                       $result=$this->statement->execute();
++                      $result = $this->statement->execute($input);
 +              } else {
-               if ($result) {
-                       return $this;
-               } else {
++                      $result = $this->statement->execute();
 +              }
 +              
++              if ($result === false) {
 +                      return false;
 +              }
++              if ($this->isManipulation) {
++                      return $this->statement->rowCount();
++              } else {
++                      return $this;
++              }
 +      }
 +
 +      private function tryFixSubstringLastArgumentDataForMSSQL($input) {
 +              $query = $this->statement->getWrappedStatement()->queryString;
 +              $pos = stripos ($query, 'SUBSTRING');
 +
 +              if ( $pos === false) {
 +                      return $input;
 +              }
 +
 +              try {
 +                      $newQuery = '';
 +
 +                      $cArg = 0;
 +
 +                      $inSubstring = false;
 +
 +                      // Create new query
 +                      for ($i = 0; $i < strlen ($query); $i++) {
 +                              if ($inSubstring == false) {
 +                                      // Defines when we should start inserting values
 +                                      if (substr ($query, $i, 9) == 'SUBSTRING') {
 +                                              $inSubstring = true;
 +                                      }
 +                              } else {
 +                                      // Defines when we should stop inserting values
 +                                      if (substr ($query, $i, 1) == ')') {
 +                                              $inSubstring = false;
 +                                      }
 +                              }
 +
 +                              if (substr ($query, $i, 1) == '?') {
 +                                      // We found a question mark
 +                                      if ($inSubstring) {
 +                                              $newQuery .= $input[$cArg];
 +
 +                                              //
 +                                              // Remove from input array
 +                                              //
 +                                              array_splice ($input, $cArg, 1);
 +                                      } else {
 +                                              $newQuery .= substr ($query, $i, 1);
 +                                              $cArg++;
 +                                      }
 +                              } else {
 +                                      $newQuery .= substr ($query, $i, 1);
 +                              }
 +                      }
 +
 +                      // The global data we need
 +                      $name = OC_Config::getValue( "dbname", "owncloud" );
 +                      $host = OC_Config::getValue( "dbhost", "" );
 +                      $user = OC_Config::getValue( "dbuser", "" );
 +                      $pass = OC_Config::getValue( "dbpassword", "" );
 +                      if (strpos($host,':')) {
 +                              list($host, $port) = explode(':', $host, 2);
 +                      } else {
 +                              $port = false;
 +                      }
 +                      $opts = array();
 +
 +                      if ($port) {
 +                              $dsn = 'sqlsrv:Server='.$host.','.$port.';Database='.$name;
 +                      } else {
 +                              $dsn = 'sqlsrv:Server='.$host.';Database='.$name;
 +                      }
 +
 +                      $PDO = new PDO($dsn, $user, $pass, $opts);
 +                      $PDO->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
 +                      $PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 +
 +                      $this->statement = $PDO->prepare($newQuery);
 +
 +                      $this->lastArguments = $input;
 +
 +                      return $input;
 +              } catch (PDOException $e){
 +                      $entry = 'PDO DB Error: "'.$e->getMessage().'"<br />';
 +                      $entry .= 'Offending command was: '.$this->statement->queryString .'<br />';
 +                      $entry .= 'Input parameters: ' .print_r($input, true).'<br />';
 +                      $entry .= 'Stack trace: ' .$e->getTraceAsString().'<br />';
 +                      OC_Log::write('core', $entry, OC_Log::FATAL);
 +                      OC_User::setUserId(null);
 +
 +                      // send http status 503
 +                      header('HTTP/1.1 503 Service Temporarily Unavailable');
 +                      header('Status: 503 Service Temporarily Unavailable');
 +                      OC_Template::printErrorPage('Failed to connect to database');
 +                      die ($entry);
 +              }
 +      }
 +    
 +      /**
 +       * provide an alias for fetch
 +       */
 +      public function fetchRow() {
 +              return $this->statement->fetch();
 +      }
 +
 +      /**
 +       * Provide a simple fetchOne.
 +       * fetch single column from the next row
 +       * @param int $colnum the column number to fetch
 +       * @return string
 +       */
 +      public function fetchOne($colnum = 0) {
 +              return $this->statement->fetchColumn($colnum);
 +      }
 +}
index 18b7340ec9b32f37653a16ce952d7824c6bfe8a3,69e3542f9263f4a29cdf2b309cdb014fb6089464..e817a2db5edc3dd9ca53ccdfb0d6654be639f244
@@@ -37,10 -37,10 +37,10 @@@ class Test_DB extends PHPUnit_Framework
                $result = $query->execute(array('uri_1'));
                $this->assertTrue((bool)$result);
                $row = $result->fetchRow();
 -              $this->assertFalse((bool)$row); //PDO returns false, MDB2 returns null
 +              $this->assertFalse($row);
                $query = OC_DB::prepare('INSERT INTO `*PREFIX*'.$this->table2.'` (`fullname`,`uri`) VALUES (?,?)');
                $result = $query->execute(array('fullname test', 'uri_1'));
-               $this->assertTrue((bool)$result);
+               $this->assertEquals(1, $result);
                $query = OC_DB::prepare('SELECT `fullname`,`uri` FROM `*PREFIX*'.$this->table2.'` WHERE `uri` = ?');
                $result = $query->execute(array('uri_1'));
                $this->assertTrue((bool)$result);
@@@ -94,7 -94,7 +94,7 @@@
                $query = OC_DB::prepare('SELECT * FROM `*PREFIX*'.$this->table3.'`');
                $result = $query->execute();
                $this->assertTrue((bool)$result);
-               $this->assertEquals('4', count($result->fetchAll()));
 -              $this->assertEquals(4, $result->numRows());
++              $this->assertEquals(4, count($result->fetchAll()));
        }
  
        public function testinsertIfNotExistDontOverwrite() {