- Subproject commit 691791a4f743aaa83546736928e3ce18574f3c03
-Subproject commit 217626723957161191572ea50172a3943c30696d
++Subproject commit c8623cc80d47022cb25874b69849cd2f57fd4874
* @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
--- /dev/null
- 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);
+ }
+}
$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);
$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() {