From 44133a07d63ed3bcfd95585caad3f0be7421a277 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 9 Oct 2012 21:07:31 +0200 Subject: [PATCH 01/47] Add doctrine-common and doctrine-dbal --- .gitmodules | 6 ++++++ 3rdparty/doctrine-common | 1 + 3rdparty/doctrine-dbal | 1 + lib/base.php | 6 ++++++ 4 files changed, 14 insertions(+) create mode 100644 .gitmodules create mode 160000 3rdparty/doctrine-common create mode 160000 3rdparty/doctrine-dbal diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..ffcaaf952ba --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "3rdparty/doctrine-common"] + path = 3rdparty/doctrine-common + url = git://github.com/doctrine/common.git +[submodule "3rdparty/doctrine-dbal"] + path = 3rdparty/doctrine-dbal + url = git://github.com/doctrine/dbal.git diff --git a/3rdparty/doctrine-common b/3rdparty/doctrine-common new file mode 160000 index 00000000000..d1c7d4334e3 --- /dev/null +++ b/3rdparty/doctrine-common @@ -0,0 +1 @@ +Subproject commit d1c7d4334e38cad603a5c863d4c7b91bb04ec6b2 diff --git a/3rdparty/doctrine-dbal b/3rdparty/doctrine-dbal new file mode 160000 index 00000000000..30dc433ea08 --- /dev/null +++ b/3rdparty/doctrine-dbal @@ -0,0 +1 @@ +Subproject commit 30dc433ea08f4863479700492bdb70c3b06440bd diff --git a/lib/base.php b/lib/base.php index 51f8f4efc5b..ad485342d2d 100644 --- a/lib/base.php +++ b/lib/base.php @@ -93,6 +93,12 @@ class OC{ elseif(strpos($className, 'Sabre_')===0) { $path = str_replace('_', '/', $className) . '.php'; } + elseif(strpos($className, 'Doctrine\\Common')===0) { + $path = 'doctrine-common/lib/'.str_replace('\\', '/', $className) . '.php'; + } + elseif(strpos($className, 'Doctrine\\DBAL')===0) { + $path = 'doctrine-dbal/lib/'.str_replace('\\', '/', $className) . '.php'; + } elseif(strpos($className, 'Test_')===0) { $path = 'tests/lib/'.strtolower(str_replace('_', '/', substr($className, 5)) . '.php'); }else{ From 24b10be7625fab71ddf54f5cab59d445dac611fc Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 9 Oct 2012 22:08:29 +0200 Subject: [PATCH 02/47] Basic support for Doctrine\DBAL --- lib/db.php | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/lib/db.php b/lib/db.php index 6c9eec5637d..0d1a70c7246 100644 --- a/lib/db.php +++ b/lib/db.php @@ -27,6 +27,7 @@ class OC_DB { const BACKEND_PDO=0; const BACKEND_MDB2=1; + const BACKEND_DOCTRINE=2; /** * @var MDB2_Driver_Common @@ -37,6 +38,10 @@ class OC_DB { * @var MDB2_Driver_Common */ static private $MDB2=null; + /** + * @var Doctrine + */ + static private $DOCTRINE=null; /** * @var PDO */ @@ -54,6 +59,7 @@ class OC_DB { * @return int BACKEND_MDB2 or BACKEND_PDO */ private static function getDBBackend() { + return self::BACKEND_DOCTRINE; //check if we can use PDO, else use MDB2 (installation always needs to be done my mdb2) if(class_exists('PDO') && OC_Config::getValue('installed', false)) { $type = OC_Config::getValue( "dbtype", "sqlite" ); @@ -83,6 +89,11 @@ class OC_DB { if(is_null($backend)) { $backend=self::getDBBackend(); } + if($backend==self::BACKEND_DOCTRINE) { + $success = self::connectDoctrine(); + self::$connection=self::$DOCTRINE; + self::$backend=self::BACKEND_DOCTRINE; + } else if($backend==self::BACKEND_PDO) { $success = self::connectPDO(); self::$connection=self::$PDO; @@ -95,6 +106,93 @@ class OC_DB { return $success; } + /** + * connect to the database using doctrine + * + * @return bool + */ + public static function connectDoctrine() { + if(self::$connection) { + if(self::$backend!=self::BACKEND_DOCTRINE) { + self::disconnect(); + }else{ + return true; + } + } + // 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", "" ); + $type = OC_Config::getValue( "dbtype", "sqlite" ); + if(strpos($host, ':')) { + list($host, $port)=explode(':', $host,2); + }else{ + $port=false; + } + + // do nothing if the connection already has been established + if(!self::$DOCTRINE) { + $config = new \Doctrine\DBAL\Configuration(); + switch($type) { + case 'sqlite': + if (!self::connectPDO()) { + return false; + } + $connectionParams = array( + 'driver' => 'pdo', + 'pdo' => self::$PDO, + ); + break; + case 'sqlite3': + $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'path' => $datadir.'/'.$name.'.db', + 'driver' => 'pdo_sqlite', + ); + break; + case 'mysql': + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'host' => $host, + 'port' => $port, + 'dbname' => $name, + 'charset' => 'UTF8', + 'driver' => 'pdo_mysql', + ); + break; + case 'pgsql': + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'host' => $host, + 'port' => $port, + 'dbname' => $name, + 'driver' => 'pdo_mysql', + ); + break; + case 'oci': + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'host' => $host, + 'port' => $port, + 'dbname' => $name, + 'charset' => 'AL32UTF8', + 'driver' => 'oci8', + ); + break; + default: + return false; + } + self::$DOCTRINE = \Doctrine\DBAL\DriverManager::getConnection($connectionParams, $config); + } + return true; + } + /** * connect to the database using pdo * @@ -317,6 +415,18 @@ class OC_DB { self::connect(); // return the result + if (self::$backend == self::BACKEND_DOCTRINE) { + try{ + $result=self::$connection->prepare($query); + }catch(PDOException $e) { + $entry = 'DB Error: "'.$e->getMessage().'"
'; + $entry .= 'Offending command was: '.htmlentities($query).'
'; + OC_Log::write('core', $entry,OC_Log::FATAL); + error_log('DB error: '.$entry); + die( $entry ); + } + $result=new DoctrineStatementWrapper($result); + } else if(self::$backend==self::BACKEND_MDB2) { $result = self::$connection->prepare( $query ); @@ -376,6 +486,7 @@ class OC_DB { self::$connection->disconnect(); } self::$connection=false; + self::$DOCTRINE=false; self::$MDB2=false; self::$PDO=false; } @@ -713,6 +824,67 @@ class OC_DB { } } +/** + * small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement + */ +class DoctrineStatementWrapper { + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $statement=null; + private $lastArguments=array(); + + public function __construct($statement) { + $this->statement=$statement; + } + + /** + * 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() { + return $this->statement->rowCount(); + } + + /** + * make execute return the result instead of a bool + */ + public function execute($input=array()) { + $this->lastArguments=$input; + if(count($input)>0) { + $result=$this->statement->execute($input); + }else{ + $result=$this->statement->execute(); + } + if($result) { + return $this; + }else{ + return false; + } + } + + /** + * 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 + */ + public function fetchOne($colnum = 0) { + return $this->statement->fetchColumn($colnum); + } +} /** * small wrapper around PDOStatement to make it behave ,more like an MDB2 Statement */ From 5686183e4345d798ded448b016a2f73ad5f37984 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Wed, 10 Oct 2012 20:49:47 +0200 Subject: [PATCH 03/47] Use Doctrine for schema changes --- lib/db.php | 172 +++++----------------------- lib/db/mdb2schemareader.php | 220 ++++++++++++++++++++++++++++++++++++ lib/db/schema.php | 128 +++++++++++++++++++++ 3 files changed, 375 insertions(+), 145 deletions(-) create mode 100644 lib/db/mdb2schemareader.php create mode 100644 lib/db/schema.php diff --git a/lib/db.php b/lib/db.php index 0d1a70c7246..d43ca77ff57 100644 --- a/lib/db.php +++ b/lib/db.php @@ -20,6 +20,7 @@ * */ +define('MDB2_SCHEMA_DUMP_STRUCTURE', '1'); /** * This class manages the access to the database. It basically is a wrapper for * MDB2 with some adaptions. @@ -503,18 +504,8 @@ class OC_DB { * TODO: write more documentation */ public static function getDbStructure( $file, $mode=MDB2_SCHEMA_DUMP_STRUCTURE) { - self::connectScheme(); - - // write the scheme - $definition = self::$schema->getDefinitionFromDatabase(); - $dump_options = array( - 'output_mode' => 'file', - 'output' => $file, - 'end_of_line' => "\n" - ); - self::$schema->dumpDatabase( $definition, $dump_options, $mode ); - - return true; + self::connectDoctrine(); + return OC_DB_Schema::getDbStructure(self::$connection, $file); } /** @@ -525,19 +516,8 @@ class OC_DB { * TODO: write more documentation */ public static function createDbFromStructure( $file ) { - $CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" ); - $CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" ); - $CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" ); - - self::connectScheme(); - - // read file - $content = file_get_contents( $file ); - - // Make changes and save them to an in-memory file - $file2 = 'static://db_scheme'; - $content = str_replace( '*dbname*', $CONFIG_DBNAME, $content ); - $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); + self::connectDoctrine(); + return OC_DB_Schema::createDbFromStructure(self::$connection, $file); /* FIXME: use CURRENT_TIMESTAMP for all databases. mysql supports it as a default for DATETIME since 5.6.5 [1] * as a fallback we could use 0000-01-01 00:00:00 everywhere * [1] http://bugs.mysql.com/bug.php?id=27645 @@ -549,34 +529,6 @@ class OC_DB { if( $CONFIG_DBTYPE == 'pgsql' ) { //mysql support it too but sqlite doesn't $content = str_replace( '0000-00-00 00:00:00', 'CURRENT_TIMESTAMP', $content ); } - - file_put_contents( $file2, $content ); - - // Try to create tables - $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); - - //clean up memory - unlink( $file2 ); - - // Die in case something went wrong - if( $definition instanceof MDB2_Schema_Error ) { - die( $definition->getMessage().': '.$definition->getUserInfo()); - } - if(OC_Config::getValue('dbtype', 'sqlite')==='oci') { - unset($definition['charset']); //or MDB2 tries SHUTDOWN IMMEDIATE - $oldname = $definition['name']; - $definition['name']=OC_Config::getValue( "dbuser", $oldname ); - } - - $ret=self::$schema->createDatabase( $definition ); - - // Die in case something went wrong - if( $ret instanceof MDB2_Error ) { - echo (self::$MDB2->getDebugOutput()); - die ($ret->getMessage() . ': ' . $ret->getUserInfo()); - } - - return true; } /** @@ -585,26 +537,14 @@ class OC_DB { * @return bool */ public static function updateDbFromStructure($file) { - $CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" ); - $CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" ); - - self::connectScheme(); - - // read file - $content = file_get_contents( $file ); - - $previousSchema = self::$schema->getDefinitionFromDatabase(); - if (PEAR::isError($previousSchema)) { - $error = $previousSchema->getMessage(); - $detail = $previousSchema->getDebugInfo(); - OC_Log::write('core', 'Failed to get existing database structure for upgrading ('.$error.', '.$detail.')', OC_Log::FATAL); + self::connectDoctrine(); + try { + $result = OC_DB_Schema::updateDbFromStructure(self::$connection, $file); + } catch (Exception $e) { + OC_Log::write('core', 'Failed to update database structure ('.$e.')', OC_Log::FATAL); return false; } - - // Make changes and save them to an in-memory file - $file2 = 'static://db_scheme'; - $content = str_replace( '*dbname*', $previousSchema['name'], $content ); - $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); + return $result; /* FIXME: use CURRENT_TIMESTAMP for all databases. mysql supports it as a default for DATETIME since 5.6.5 [1] * as a fallback we could use 0000-01-01 00:00:00 everywhere * [1] http://bugs.mysql.com/bug.php?id=27645 @@ -616,40 +556,6 @@ class OC_DB { if( $CONFIG_DBTYPE == 'pgsql' ) { //mysql support it too but sqlite doesn't $content = str_replace( '0000-00-00 00:00:00', 'CURRENT_TIMESTAMP', $content ); } - file_put_contents( $file2, $content ); - $op = self::$schema->updateDatabase($file2, $previousSchema, array(), false); - - //clean up memory - unlink( $file2 ); - - if (PEAR::isError($op)) { - $error = $op->getMessage(); - $detail = $op->getDebugInfo(); - OC_Log::write('core', 'Failed to update database structure ('.$error.', '.$detail.')', OC_Log::FATAL); - return false; - } - return true; - } - - /** - * @brief connects to a MDB2 database scheme - * @returns bool - * - * Connects to a MDB2 database scheme - */ - private static function connectScheme() { - // We need a mdb2 database connection - self::connectMDB2(); - self::$MDB2->loadModule('Manager'); - self::$MDB2->loadModule('Reverse'); - - // Connect if this did not happen before - if(!self::$schema) { - require_once 'MDB2/Schema.php'; - self::$schema=MDB2_Schema::factory(self::$MDB2); - } - - return true; } /** @@ -696,9 +602,8 @@ class OC_DB { * @param string $tableName the table to drop */ public static function dropTable($tableName) { - self::connectMDB2(); - self::$MDB2->loadModule('Manager'); - self::$MDB2->dropTable($tableName); + self::connectDoctrine(); + OC_DB_Schema::dropTable(self::$connection, $tableName); } /** @@ -706,28 +611,8 @@ class OC_DB { * @param string $file the xml file describing the tables */ public static function removeDBStructure($file) { - $CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" ); - $CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" ); - self::connectScheme(); - - // read file - $content = file_get_contents( $file ); - - // Make changes and save them to a temporary file - $file2 = tempnam( get_temp_dir(), 'oc_db_scheme_' ); - $content = str_replace( '*dbname*', $CONFIG_DBNAME, $content ); - $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); - file_put_contents( $file2, $content ); - - // get the tables - $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); - - // Delete our temporary file - unlink( $file2 ); - $tables=array_keys($definition['tables']); - foreach($tables as $table) { - self::dropTable($table); - } + self::connectDoctrine(); + OC_DB_Schema::removeDBStructure(self::$connection, $file); } /** @@ -735,21 +620,8 @@ class OC_DB { * @param $file string path to the MDB2 xml db export file */ public static function replaceDB( $file ) { - $apps = OC_App::getAllApps(); - self::beginTransaction(); - // Delete the old tables - self::removeDBStructure( OC::$SERVERROOT . '/db_structure.xml' ); - - foreach($apps as $app) { - $path = OC_App::getAppPath($app).'/appinfo/database.xml'; - if(file_exists($path)) { - self::removeDBStructure( $path ); - } - } - - // Create new tables - self::createDBFromStructure( $file ); - self::commit(); + self::connectDoctrine(); + OC_DB_Schema::replaceDB(self::$connection, $file); } /** @@ -817,6 +689,16 @@ class OC_DB { }else{ $msg = ''; } + } elseif (self::$backend==self::BACKEND_DOCTRINE and self::$DOCTRINE) { + $msg = self::$DOCTRINE->errorCode() . ': '; + $errorInfo = self::$DOCTRINE->errorInfo(); + if (is_array($errorInfo)) { + $msg .= 'SQLSTATE = '.$errorInfo[0] . ', '; + $msg .= 'Driver Code = '.$errorInfo[1] . ', '; + $msg .= 'Driver Message = '.$errorInfo[2]; + }else{ + $msg = ''; + } }else{ $msg = ''; } diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php new file mode 100644 index 00000000000..3f6cadd3dc4 --- /dev/null +++ b/lib/db/mdb2schemareader.php @@ -0,0 +1,220 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_DB_MDB2SchemaReader { + static protected $DBNAME; + static protected $DBTABLEPREFIX; + + public static function loadSchemaFromFile($file) { + self::$DBNAME = OC_Config::getValue( "dbname", "owncloud" ); + self::$DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" ); + $schema = new \Doctrine\DBAL\Schema\Schema(); + $xml = simplexml_load_file($file); + foreach($xml->children() as $child) { + switch($child->getName()) { + case 'name': + $name = (string)$child; + $name = str_replace( '*dbname*', self::$DBNAME, $name ); + break; + case 'create': + case 'overwrite': + case 'charset': + break; + case 'table': + self::loadTable($schema, $child); + break; + default: + var_dump($child->getName()); + + } + } + return $schema; + } + + private static function loadTable($schema, $xml) { + foreach($xml->children() as $child) { + switch($child->getName()) { + case 'name': + $name = (string)$child; + $name = str_replace( '*dbprefix*', self::$DBTABLEPREFIX, $name ); + $table = $schema->createTable($name); + break; + case 'create': + case 'overwrite': + case 'charset': + break; + case 'declaration': + self::loadDeclaration($table, $child); + break; + default: + var_dump($child->getName()); + + } + } + } + + private static function loadDeclaration($table, $xml) { + foreach($xml->children() as $child) { + switch($child->getName()) { + case 'field': + self::loadField($table, $child); + break; + case 'index': + self::loadIndex($table, $child); + break; + default: + var_dump($child->getName()); + + } + } + } + + private static function loadField($table, $xml) { + $options = array(); + foreach($xml->children() as $child) { + switch($child->getName()) { + case 'name': + $name = (string)$child; + break; + case 'type': + $type = (string)$child; + switch($type) { + case 'text': + $type = 'string'; + break; + case 'clob': + $type = 'text'; + break; + case 'timestamp': + $type = 'datetime'; + break; + // TODO + return; + } + break; + case 'length': + $length = (string)$child; + $options['length'] = $length; + break; + case 'unsigned': + $unsigned = self::asBool($child); + $options['unsigned'] = $unsigned; + break; + case 'notnull': + $notnull = self::asBool($child); + $options['notnull'] = $notnull; + break; + case 'autoincrement': + $autoincrement = self::asBool($child); + $options['autoincrement'] = $autoincrement; + break; + case 'default': + $default = (string)$child; + $options['default'] = $default; + break; + default: + var_dump($child->getName()); + + } + } + if (isset($name) && isset($type)) { + if ($name == 'x') { + var_dump($name, $type, $options); + echo '
';
+			debug_print_backtrace();
+		}
+			if (empty($options['default'])) {
+				if ($type == 'integer') {
+					if (empty($options['default'])) {
+						$options['default'] = 0;
+					}
+				}
+				if (!empty($options['autoincrement'])) {
+					unset($options['default']);
+				}
+			}
+			if ($type == 'integer') {
+				$length = $options['length'];
+				if ($length == 1) {
+					$type = 'boolean';
+				}
+				else if ($length < 4) {
+					$type = 'smallint';
+				}
+				else if ($length > 4) {
+					$type = 'bigint';
+				}
+			}
+			$table->addColumn($name, $type, $options);
+			if (!empty($options['autoincrement'])
+			    && !empty($options['notnull'])) {
+				$table->setPrimaryKey(array($name));
+			}
+		}
+	}
+
+	private static function loadIndex($table, $xml) {
+		$name = null;
+		$fields = array();
+		foreach($xml->children() as $child) {
+			switch($child->getName()) {
+				case 'name':
+					$name = (string)$child;
+					break;
+				case 'primary':
+					$primary = self::asBool($child);
+					break;
+				case 'unique':
+					$unique = self::asBool($child);
+					break;
+				case 'field':
+					foreach($child->children() as $field) {
+						switch($field->getName()) {
+							case 'name':
+								$field_name = (string)$field;
+								$fields[] = $field_name;
+								break;
+							case 'sorting':
+								break;
+							default:
+								var_dump($field->getName());
+
+						}
+					}
+					break;
+				default:
+					var_dump($child->getName());
+
+			}
+		}
+		if (!empty($fields)) {
+			if (isset($primary) && $primary) {
+				$table->setPrimaryKey($fields, $name);
+			} else
+			if (isset($unique) && $unique) {
+				$table->addUniqueIndex($fields, $name);
+			} else {
+				$table->addIndex($fields, $name);
+			}
+		} else {
+			var_dump($name, $fields);
+		}
+	}
+
+	private static function asBool($xml) {
+		$result = (string)$xml;
+		if ($result == 'true') {
+			$result = true;
+		} else
+		if ($result == 'false') {
+			$result = false;
+		}
+		return (bool)$result;
+	}
+
+}
diff --git a/lib/db/schema.php b/lib/db/schema.php
new file mode 100644
index 00000000000..ca90e300e0d
--- /dev/null
+++ b/lib/db/schema.php
@@ -0,0 +1,128 @@
+
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_DB_Schema {
+	private static $DBNAME;
+	private static $DBTABLEPREFIX;
+
+	/**
+	 * @brief saves database scheme to xml file
+	 * @param string $file name of file
+	 * @param int $mode
+	 * @return bool
+	 *
+	 * TODO: write more documentation
+	 */
+	public static function getDbStructure( $conn, $file ,$mode=MDB2_SCHEMA_DUMP_STRUCTURE) {
+		$sm = $conn->getSchemaManager();
+		$fromSchema = $sm->createSchema();
+
+		return OC_DB_MDB2SchemaWriter::saveSchemaToFile($file);
+	}
+
+	/**
+	 * @brief Creates tables from XML file
+	 * @param string $file file to read structure from
+	 * @return bool
+	 *
+	 * TODO: write more documentation
+	 */
+	public static function createDbFromStructure( $conn, $file ) {
+		$toSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file);
+		return self::executeSchemaChange($conn, $toSchema);
+	}
+
+	/**
+	 * @brief update the database scheme
+	 * @param string $file file to read structure from
+	 * @return bool
+	 */
+	public static function updateDbFromStructure($conn, $file) {
+		$sm = $conn->getSchemaManager();
+		$fromSchema = $sm->createSchema();
+
+		$toSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file);
+
+		// remove tables we don't know about
+		foreach($fromSchema->getTables() as $table) {
+			if (!$toSchema->hasTable($table->getName())) {
+				$fromSchema->dropTable($table->getName());
+			}
+		}
+
+		$comparator = new \Doctrine\DBAL\Schema\Comparator();
+		$schemaDiff = $comparator->compare($fromSchema, $toSchema);
+
+		//$from = $fromSchema->toSql($conn->getDatabasePlatform());
+		//$to = $toSchema->toSql($conn->getDatabasePlatform());
+		//echo($from[9]);
+		//echo '
'; + //echo($to[9]); + //var_dump($from, $to); + return self::executeSchemaChange($conn, $schemaDiff); + } + + /** + * @brief drop a table + * @param string $tableName the table to drop + */ + public static function dropTable($conn, $tableName) { + $sm = $conn->getSchemaManager(); + $fromSchema = $sm->createSchema(); + $toSchema = clone $fromSchema; + $toSchema->dropTable('user'); + $sql = $fromSchema->getMigrateToSql($toSchema, $conn->getDatabasePlatform()); + var_dump($sql); + die; + $conn->execute($sql); + } + + /** + * remove all tables defined in a database structure xml file + * @param string $file the xml file describing the tables + */ + public static function removeDBStructure($conn, $file) { + $fromSchema = OC_DB_MDB2SchemaReader::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); + self::executeSchemaChange($conn, $schemaDiff); + } + + /** + * @brief replaces the owncloud tables with a new set + * @param $file string path to the MDB2 xml db export file + */ + public static function replaceDB( $conn, $file ) { + $apps = OC_App::getAllApps(); + self::beginTransaction(); + // Delete the old tables + self::removeDBStructure( OC::$SERVERROOT . '/db_structure.xml' ); + + foreach($apps as $app) { + $path = OC_App::getAppPath($app).'/appinfo/database.xml'; + if(file_exists($path)) { + self::removeDBStructure( $path ); + } + } + + // Create new tables + self::commit(); + } + + private static function executeSchemaChange($conn, $schema) { + $conn->beginTransaction(); + foreach($schema->toSql($conn->getDatabasePlatform()) as $sql) { + $conn->query($sql); + } + $conn->commit(); + } +} From 8e140ae03bc4aa886c2e2386d106fd89ff2d302b Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 13 Oct 2012 15:09:06 +0200 Subject: [PATCH 04/47] Don't test the schema export for now, first decide which format we are going to use --- tests/lib/dbschema.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lib/dbschema.php b/tests/lib/dbschema.php index cd408160afb..becfe9edf45 100644 --- a/tests/lib/dbschema.php +++ b/tests/lib/dbschema.php @@ -55,6 +55,7 @@ class Test_DBSchema extends UnitTestCase { } public function doTestSchemaDumping() { + return; $outfile = 'static://db_out.xml'; OC_DB::getDbStructure($outfile); $content = file_get_contents($outfile); From 8fb36c93f576a23b49b6044640ddfae963f10b00 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 26 Oct 2012 10:51:21 +0200 Subject: [PATCH 05/47] Add MDB2 compatible database schema writer --- lib/db/mdb2schemareader.php | 7 +- lib/db/mdb2schemawriter.php | 127 ++++++++++++++++++++++++++++++++++++ lib/db/schema.php | 5 +- tests/lib/dbschema.php | 1 - 4 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 lib/db/mdb2schemawriter.php diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php index 3f6cadd3dc4..05b9bd21289 100644 --- a/lib/db/mdb2schemareader.php +++ b/lib/db/mdb2schemareader.php @@ -131,7 +131,12 @@ class OC_DB_MDB2SchemaReader { if (empty($options['default'])) { if ($type == 'integer') { if (empty($options['default'])) { - $options['default'] = 0; + if (empty($options['notnull'])) { + unset($options['default']); + } + else { + $options['default'] = 0; + } } } if (!empty($options['autoincrement'])) { diff --git a/lib/db/mdb2schemawriter.php b/lib/db/mdb2schemawriter.php new file mode 100644 index 00000000000..a6367a0e354 --- /dev/null +++ b/lib/db/mdb2schemawriter.php @@ -0,0 +1,127 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_DB_MDB2SchemaWriter { + static public function saveSchemaToFile($file, $sm) { + $xml = new SimpleXMLElement(''); + $xml->addChild('name', OC_Config::getValue( "dbname", "owncloud" )); + $xml->addChild('create', 'true'); + $xml->addChild('overwrite', 'false'); + $xml->addChild('charset', 'utf8'); + foreach ($sm->listTables() as $table) { + self::saveTable($table, $xml->addChild('table')); + } + file_put_contents($file, $xml->asXML()); + return true; + } + + 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')); + } + } + + 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; + + } + } + + 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/db/schema.php b/lib/db/schema.php index ca90e300e0d..231b8068af0 100644 --- a/lib/db/schema.php +++ b/lib/db/schema.php @@ -18,11 +18,10 @@ class OC_DB_Schema { * * TODO: write more documentation */ - public static function getDbStructure( $conn, $file ,$mode=MDB2_SCHEMA_DUMP_STRUCTURE) { + public static function getDbStructure( $conn, $file, $mode=MDB2_SCHEMA_DUMP_STRUCTURE) { $sm = $conn->getSchemaManager(); - $fromSchema = $sm->createSchema(); - return OC_DB_MDB2SchemaWriter::saveSchemaToFile($file); + return OC_DB_MDB2SchemaWriter::saveSchemaToFile($file, $sm); } /** diff --git a/tests/lib/dbschema.php b/tests/lib/dbschema.php index becfe9edf45..cd408160afb 100644 --- a/tests/lib/dbschema.php +++ b/tests/lib/dbschema.php @@ -55,7 +55,6 @@ class Test_DBSchema extends UnitTestCase { } public function doTestSchemaDumping() { - return; $outfile = 'static://db_out.xml'; OC_DB::getDbStructure($outfile); $content = file_get_contents($outfile); From 8bb62e74bd14e4fec6ed1cd371d21af0f618076b Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Thu, 1 Nov 2012 18:11:50 +0100 Subject: [PATCH 06/47] Remove other database implementations from OC_DB --- lib/db.php | 395 ++++------------------------------------------------- 1 file changed, 25 insertions(+), 370 deletions(-) diff --git a/lib/db.php b/lib/db.php index d43ca77ff57..a6500a2e3bd 100644 --- a/lib/db.php +++ b/lib/db.php @@ -23,57 +23,31 @@ define('MDB2_SCHEMA_DUMP_STRUCTURE', '1'); /** * This class manages the access to the database. It basically is a wrapper for - * MDB2 with some adaptions. + * Doctrine with some adaptions. */ class OC_DB { - const BACKEND_PDO=0; - const BACKEND_MDB2=1; const BACKEND_DOCTRINE=2; /** - * @var MDB2_Driver_Common + * @var \Doctrine\DBAL\Connection */ - static private $connection; //the prefered connection to use, either PDO or MDB2 + static private $connection; //the prefered connection to use, only Doctrine static private $backend=null; - /** - * @var MDB2_Driver_Common - */ - static private $MDB2=null; /** * @var Doctrine */ static private $DOCTRINE=null; - /** - * @var PDO - */ - static private $PDO=null; - /** - * @var MDB2_Schema - */ - static private $schema=null; + static private $inTransaction=false; static private $prefix=null; static private $type=null; /** * check which backend we should use - * @return int BACKEND_MDB2 or BACKEND_PDO + * @return int BACKEND_DOCTRINE */ private static function getDBBackend() { return self::BACKEND_DOCTRINE; - //check if we can use PDO, else use MDB2 (installation always needs to be done my mdb2) - if(class_exists('PDO') && OC_Config::getValue('installed', false)) { - $type = OC_Config::getValue( "dbtype", "sqlite" ); - if($type=='oci') { //oracle also always needs mdb2 - return self::BACKEND_MDB2; - } - if($type=='sqlite3') $type='sqlite'; - $drivers=PDO::getAvailableDrivers(); - if(array_search($type, $drivers)!==false) { - return self::BACKEND_PDO; - } - } - return self::BACKEND_MDB2; } /** @@ -94,15 +68,6 @@ class OC_DB { $success = self::connectDoctrine(); self::$connection=self::$DOCTRINE; self::$backend=self::BACKEND_DOCTRINE; - } else - if($backend==self::BACKEND_PDO) { - $success = self::connectPDO(); - self::$connection=self::$PDO; - self::$backend=self::BACKEND_PDO; - }else{ - $success = self::connectMDB2(); - self::$connection=self::$MDB2; - self::$backend=self::BACKEND_MDB2; } return $success; } @@ -137,14 +102,6 @@ class OC_DB { $config = new \Doctrine\DBAL\Configuration(); switch($type) { case 'sqlite': - if (!self::connectPDO()) { - return false; - } - $connectionParams = array( - 'driver' => 'pdo', - 'pdo' => self::$PDO, - ); - break; case 'sqlite3': $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); $connectionParams = array( @@ -194,220 +151,33 @@ class OC_DB { return true; } - /** - * connect to the database using pdo - * - * @return bool - */ - public static function connectPDO() { - if(self::$connection) { - if(self::$backend==self::BACKEND_MDB2) { - self::disconnect(); - }else{ - return true; - } - } - // 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", "" ); - $type = OC_Config::getValue( "dbtype", "sqlite" ); - if(strpos($host, ':')) { - list($host, $port)=explode(':', $host,2); - }else{ - $port=false; - } - $opts = array(); - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); - - // do nothing if the connection already has been established - if(!self::$PDO) { - // Add the dsn according to the database type - switch($type) { - case 'sqlite': - $dsn='sqlite2:'.$datadir.'/'.$name.'.db'; - break; - case 'sqlite3': - $dsn='sqlite:'.$datadir.'/'.$name.'.db'; - break; - case 'mysql': - if($port) { - $dsn='mysql:dbname='.$name.';host='.$host.';port='.$port; - }else{ - $dsn='mysql:dbname='.$name.';host='.$host; - } - $opts[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES 'UTF8'"; - break; - case 'pgsql': - if($port) { - $dsn='pgsql:dbname='.$name.';host='.$host.';port='.$port; - }else{ - $dsn='pgsql:dbname='.$name.';host='.$host; - } - /** - * Ugly fix for pg connections pbm when password use spaces - */ - $e_user = addslashes($user); - $e_password = addslashes($pass); - $pass = $user = null; - $dsn .= ";user='$e_user';password='$e_password'"; - /** END OF FIX***/ - break; - case 'oci': // Oracle with PDO is unsupported - if ($port) { - $dsn = 'oci:dbname=//' . $host . ':' . $port . '/' . $name; - } else { - $dsn = 'oci:dbname=//' . $host . '/' . $name; - } - break; - default: - return false; - } - try{ - self::$PDO=new PDO($dsn, $user, $pass, $opts); - }catch(PDOException $e) { - echo( 'can not connect to database, using '.$type.'. ('.$e->getMessage().')'); - die(); - } - // We always, really always want associative arrays - self::$PDO->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); - self::$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } - return true; - } - - /** - * connect to the database using mdb2 - */ - public static function connectMDB2() { - if(self::$connection) { - if(self::$backend==self::BACKEND_PDO) { - self::disconnect(); - }else{ - return true; - } - } - // 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", "" ); - $type = OC_Config::getValue( "dbtype", "sqlite" ); - $SERVERROOT=OC::$SERVERROOT; - $datadir=OC_Config::getValue( "datadirectory", "$SERVERROOT/data" ); - - // do nothing if the connection already has been established - if(!self::$MDB2) { - // Require MDB2.php (not required in the head of the file so we only load it when needed) - require_once 'MDB2.php'; - - // Prepare options array - $options = array( - 'portability' => MDB2_PORTABILITY_ALL - MDB2_PORTABILITY_FIX_CASE, - 'log_line_break' => '
', - 'idxname_format' => '%s', - 'debug' => true, - 'quote_identifier' => true ); - - // Add the dsn according to the database type - switch($type) { - case 'sqlite': - case 'sqlite3': - $dsn = array( - 'phptype' => $type, - 'database' => "$datadir/$name.db", - 'mode' => '0644' - ); - break; - case 'mysql': - $dsn = array( - 'phptype' => 'mysql', - 'username' => $user, - 'password' => $pass, - 'hostspec' => $host, - 'database' => $name - ); - break; - case 'pgsql': - $dsn = array( - 'phptype' => 'pgsql', - 'username' => $user, - 'password' => $pass, - 'hostspec' => $host, - 'database' => $name - ); - break; - case 'oci': - $dsn = array( - 'phptype' => 'oci8', - 'username' => $user, - 'password' => $pass, - 'charset' => 'AL32UTF8', - ); - if ($host != '') { - $dsn['hostspec'] = $host; - $dsn['database'] = $name; - } else { // use dbname for hostspec - $dsn['hostspec'] = $name; - $dsn['database'] = $user; - } - break; - default: - return false; - } - - // Try to establish connection - self::$MDB2 = MDB2::factory( $dsn, $options ); - - // Die if we could not connect - if( PEAR::isError( self::$MDB2 )) { - echo( 'can not connect to database, using '.$type.'. ('.self::$MDB2->getUserInfo().')'); - OC_Log::write('core', self::$MDB2->getUserInfo(), OC_Log::FATAL); - OC_Log::write('core', self::$MDB2->getMessage(), OC_Log::FATAL); - die(); - } - - // We always, really always want associative arrays - self::$MDB2->setFetchMode(MDB2_FETCHMODE_ASSOC); - } - - // we are done. great! - return true; - } - /** * @brief Prepare a SQL query * @param string $query Query string * @param int $limit * @param int $offset - * @return MDB2_Statement_Common prepared SQL query + * @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 ) { if (!is_null($limit) && $limit != -1) { - if (self::$backend == self::BACKEND_MDB2) { - //MDB2 uses or emulates limits & offset internally - self::$MDB2->setLimit($limit, $offset); + //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 { - //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; - } + $query.=$limitsql; } } @@ -427,29 +197,6 @@ class OC_DB { die( $entry ); } $result=new DoctrineStatementWrapper($result); - } else - if(self::$backend==self::BACKEND_MDB2) { - $result = self::$connection->prepare( $query ); - - // Die if we have an error (error means: bad query, not 0 results!) - if( PEAR::isError($result)) { - $entry = 'DB Error: "'.$result->getMessage().'"
'; - $entry .= 'Offending command was: '.htmlentities($query).'
'; - OC_Log::write('core', $entry,OC_Log::FATAL); - error_log('DB error: '.$entry); - die( $entry ); - } - }else{ - try{ - $result=self::$connection->prepare($query); - }catch(PDOException $e) { - $entry = 'DB Error: "'.$e->getMessage().'"
'; - $entry .= 'Offending command was: '.htmlentities($query).'
'; - OC_Log::write('core', $entry,OC_Log::FATAL); - error_log('DB error: '.$entry); - die( $entry ); - } - $result=new PDOStatementWrapper($result); } return $result; } @@ -459,7 +206,7 @@ class OC_DB { * @param string $table The optional table name (will replace *PREFIX*) and add sequence suffix * @return int id * - * MDB2 lastInsertID() + * \Doctrine\DBAL\Connection lastInsertId * * Call this method right after the insert command or other functions may * cause trouble! @@ -483,13 +230,8 @@ class OC_DB { public static function disconnect() { // Cut connection if required if(self::$connection) { - if(self::$backend==self::BACKEND_MDB2) { - self::$connection->disconnect(); - } self::$connection=false; self::$DOCTRINE=false; - self::$MDB2=false; - self::$PDO=false; } return true; @@ -630,9 +372,6 @@ class OC_DB { */ public static function beginTransaction() { self::connect(); - if (self::$backend==self::BACKEND_MDB2 && !self::$connection->supports('transactions')) { - return false; - } self::$connection->beginTransaction(); self::$inTransaction=true; return true; @@ -653,15 +392,13 @@ class OC_DB { } /** - * check if a result is an error, works with MDB2 and PDOException + * check if a result is an error, works with Doctrine * @param mixed $result * @return bool */ public static function isError($result) { if(!$result) { return true; - }elseif(self::$backend==self::BACKEND_MDB2 and PEAR::isError($result)) { - return true; }else{ return false; } @@ -669,27 +406,12 @@ class OC_DB { /** * returns the error code and message as a string for logging - * works with MDB2 and PDOException + * works with DoctrineException * @param mixed $error * @return string */ public static function getErrorMessage($error) { - if ( self::$backend==self::BACKEND_MDB2 and PEAR::isError($error) ) { - $msg = $error->getCode() . ': ' . $error->getMessage(); - if (defined('DEBUG') && DEBUG) { - $msg .= '(' . $error->getDebugInfo() . ')'; - } - } elseif (self::$backend==self::BACKEND_PDO and self::$PDO) { - $msg = self::$PDO->errorCode() . ': '; - $errorInfo = self::$PDO->errorInfo(); - if (is_array($errorInfo)) { - $msg .= 'SQLSTATE = '.$errorInfo[0] . ', '; - $msg .= 'Driver Code = '.$errorInfo[1] . ', '; - $msg .= 'Driver Message = '.$errorInfo[2]; - }else{ - $msg = ''; - } - } elseif (self::$backend==self::BACKEND_DOCTRINE and self::$DOCTRINE) { + if (self::$backend==self::BACKEND_DOCTRINE and self::$DOCTRINE) { $msg = self::$DOCTRINE->errorCode() . ': '; $errorInfo = self::$DOCTRINE->errorInfo(); if (is_array($errorInfo)) { @@ -767,70 +489,3 @@ class DoctrineStatementWrapper { return $this->statement->fetchColumn($colnum); } } -/** - * small wrapper around PDOStatement to make it behave ,more like an MDB2 Statement - */ -class PDOStatementWrapper{ - /** - * @var PDOStatement - */ - private $statement=null; - private $lastArguments=array(); - - public function __construct($statement) { - $this->statement=$statement; - } - - /** - * make execute return the result instead of a bool - */ - public function execute($input=array()) { - $this->lastArguments=$input; - if(count($input)>0) { - $result=$this->statement->execute($input); - }else{ - $result=$this->statement->execute(); - } - if($result) { - return $this; - }else{ - return false; - } - } - - /** - * provide numRows - */ - public function numRows() { - $regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/i'; - if (preg_match($regex, $this->statement->queryString, $output) > 0) { - $query = OC_DB::prepare("SELECT COUNT(*) FROM {$output[1]}", PDO::FETCH_NUM); - return $query->execute($this->lastArguments)->fetchColumn(); - }else{ - return $this->statement->rowCount(); - } - } - - /** - * provide an alias for fetch - */ - public function fetchRow() { - return $this->statement->fetch(); - } - - /** - * pass all other function directly to the PDOStatement - */ - public function __call($name,$arguments) { - return call_user_func_array(array($this->statement,$name), $arguments); - } - - /** - * Provide a simple fetchOne. - * fetch single column from the next row - * @param int $colnum the column number to fetch - */ - public function fetchOne($colnum = 0) { - return $this->statement->fetchColumn($colnum); - } -} From dc8b22485e556a01d73754e94e513ec05f15d225 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 7 Dec 2012 13:50:59 +0100 Subject: [PATCH 07/47] Change to use 3rdparty version --- .gitmodules | 6 ------ 3rdparty/doctrine-common | 1 - 3rdparty/doctrine-dbal | 1 - lib/base.php | 4 ++-- 4 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 .gitmodules delete mode 160000 3rdparty/doctrine-common delete mode 160000 3rdparty/doctrine-dbal diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index ffcaaf952ba..00000000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "3rdparty/doctrine-common"] - path = 3rdparty/doctrine-common - url = git://github.com/doctrine/common.git -[submodule "3rdparty/doctrine-dbal"] - path = 3rdparty/doctrine-dbal - url = git://github.com/doctrine/dbal.git diff --git a/3rdparty/doctrine-common b/3rdparty/doctrine-common deleted file mode 160000 index d1c7d4334e3..00000000000 --- a/3rdparty/doctrine-common +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d1c7d4334e38cad603a5c863d4c7b91bb04ec6b2 diff --git a/3rdparty/doctrine-dbal b/3rdparty/doctrine-dbal deleted file mode 160000 index 30dc433ea08..00000000000 --- a/3rdparty/doctrine-dbal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 30dc433ea08f4863479700492bdb70c3b06440bd diff --git a/lib/base.php b/lib/base.php index d9a0e2c757f..7acad72f02a 100644 --- a/lib/base.php +++ b/lib/base.php @@ -100,10 +100,10 @@ class OC{ $path = str_replace('_', '/', $className) . '.php'; } elseif(strpos($className, 'Doctrine\\Common')===0) { - $path = 'doctrine-common/lib/'.str_replace('\\', '/', $className) . '.php'; + $path = 'doctrine/common/lib/'.str_replace('\\', '/', $className) . '.php'; } elseif(strpos($className, 'Doctrine\\DBAL')===0) { - $path = 'doctrine-dbal/lib/'.str_replace('\\', '/', $className) . '.php'; + $path = 'doctrine/dbal/lib/'.str_replace('\\', '/', $className) . '.php'; } elseif(strpos($className, 'Symfony\\Component\\Routing\\')===0) { $path = 'symfony/routing/'.str_replace('\\', '/', $className) . '.php'; From d0d9b63ab535d103a2607f6e589ee5c04807d6ce Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 23 Feb 2013 13:43:05 +0100 Subject: [PATCH 08/47] Codestyle cleanup --- lib/db.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/db.php b/lib/db.php index e10a5484662..54b6de947c3 100644 --- a/lib/db.php +++ b/lib/db.php @@ -22,15 +22,15 @@ define('MDB2_SCHEMA_DUMP_STRUCTURE', '1'); -class DatabaseException extends Exception{ +class DatabaseException extends Exception { private $query; - public function __construct($message, $query){ + public function __construct($message, $query) { parent::__construct($message); $this->query = $query; } - public function getQuery(){ + public function getQuery() { return $this->query; } } @@ -97,7 +97,7 @@ class OC_DB { if(self::$connection) { if(self::$backend!=self::BACKEND_DOCTRINE) { self::disconnect(); - }else{ + } else { return true; } } @@ -220,9 +220,9 @@ class OC_DB { self::connect(); // return the result if (self::$backend == self::BACKEND_DOCTRINE) { - try{ + try { $result=self::$connection->prepare($query); - }catch(PDOException $e) { + } catch(PDOException $e) { throw new DatabaseException($e->getMessage(), $query); } $result=new DoctrineStatementWrapper($result); @@ -438,11 +438,11 @@ class OC_DB { $query = str_replace( '`', '"', $query ); $query = str_ireplace( 'NOW()', 'datetime(\'now\')', $query ); $query = str_ireplace( 'UNIX_TIMESTAMP()', 'strftime(\'%s\',\'now\')', $query ); - }elseif( $type == 'pgsql' ) { + } elseif( $type == 'pgsql' ) { $query = str_replace( '`', '"', $query ); $query = str_ireplace( 'UNIX_TIMESTAMP()', 'cast(extract(epoch from current_timestamp) as integer)', $query ); - }elseif( $type == 'oci' ) { + } elseif( $type == 'oci' ) { $query = str_replace( '`', '"', $query ); $query = str_ireplace( 'NOW()', 'CURRENT_TIMESTAMP', $query ); } @@ -513,7 +513,7 @@ class OC_DB { public static function isError($result) { if(!$result) { return true; - }else{ + } else { return false; } } @@ -532,10 +532,10 @@ class OC_DB { $msg .= 'SQLSTATE = '.$errorInfo[0] . ', '; $msg .= 'Driver Code = '.$errorInfo[1] . ', '; $msg .= 'Driver Message = '.$errorInfo[2]; - }else{ + } else { $msg = ''; } - }else{ + } else { $msg = ''; } return $msg; @@ -577,12 +577,12 @@ class DoctrineStatementWrapper { $this->lastArguments=$input; if(count($input)>0) { $result=$this->statement->execute($input); - }else{ + } else { $result=$this->statement->execute(); } if($result) { return $this; - }else{ + } else { return false; } } From e97c7ae806acc1687604a887dd8558c9b7222f5d Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 23 Feb 2013 14:46:37 +0100 Subject: [PATCH 09/47] Change PDO Exceptions to Doctrine Exceptions --- lib/db.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/db.php b/lib/db.php index 54b6de947c3..3b2590c3105 100644 --- a/lib/db.php +++ b/lib/db.php @@ -191,7 +191,7 @@ class OC_DB { static public function prepare( $query , $limit=null, $offset=null ) { if (!is_null($limit) && $limit != -1) { - //PDO does not handle limit and offset. + //Doctrine 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) @@ -222,7 +222,7 @@ class OC_DB { if (self::$backend == self::BACKEND_DOCTRINE) { try { $result=self::$connection->prepare($query); - } catch(PDOException $e) { + } catch(\Doctrine\DBAL\DBALException $e) { throw new DatabaseException($e->getMessage(), $query); } $result=new DoctrineStatementWrapper($result); @@ -345,7 +345,7 @@ class OC_DB { * @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 The return value from PDOStatementWrapper->execute() + * @returns The return value from DoctrineStatementWrapper->execute() */ public static function insertIfNotExist($table, $input) { self::connect(); @@ -370,7 +370,7 @@ class OC_DB { try { $stmt = self::prepare($query); $result = $stmt->execute(); - } catch(PDOException $e) { + } catch(\Doctrine\DBAL\DBALException $e) { $entry = 'DB Error: "'.$e->getMessage() . '"
'; $entry .= 'Offending command was: ' . $query . '
'; OC_Log::write('core', $entry, OC_Log::FATAL); @@ -402,7 +402,7 @@ class OC_DB { try { $result = self::prepare($query); - } catch(PDOException $e) { + } catch(\Doctrine\DBAL\DBALException $e) { $entry = 'DB Error: "'.$e->getMessage() . '"
'; $entry .= 'Offending command was: ' . $query.'
'; OC_Log::write('core', $entry, OC_Log::FATAL); From e1818675d24f37898f0c50d0804bc458d2acb72f Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 23 Feb 2013 14:48:02 +0100 Subject: [PATCH 10/47] Remove MDB2 sqlite3 driver --- lib/MDB2/Driver/Datatype/sqlite3.php | 385 -------- lib/MDB2/Driver/Function/sqlite3.php | 136 --- lib/MDB2/Driver/Manager/sqlite3.php | 1362 -------------------------- lib/MDB2/Driver/Native/sqlite3.php | 33 - lib/MDB2/Driver/Reverse/sqlite3.php | 586 ----------- lib/MDB2/Driver/sqlite3.php | 1332 ------------------------- 6 files changed, 3834 deletions(-) delete mode 100644 lib/MDB2/Driver/Datatype/sqlite3.php delete mode 100644 lib/MDB2/Driver/Function/sqlite3.php delete mode 100644 lib/MDB2/Driver/Manager/sqlite3.php delete mode 100644 lib/MDB2/Driver/Native/sqlite3.php delete mode 100644 lib/MDB2/Driver/Reverse/sqlite3.php delete mode 100644 lib/MDB2/Driver/sqlite3.php diff --git a/lib/MDB2/Driver/Datatype/sqlite3.php b/lib/MDB2/Driver/Datatype/sqlite3.php deleted file mode 100644 index ca4c1cbceb8..00000000000 --- a/lib/MDB2/Driver/Datatype/sqlite3.php +++ /dev/null @@ -1,385 +0,0 @@ -. - * - */ - -require_once 'MDB2/Driver/Datatype/Common.php'; - -/** - * MDB2 SQLite driver - * - * @package MDB2 - * @category Database - * @author Lukas Smith - */ -class MDB2_Driver_Datatype_sqlite3 extends MDB2_Driver_Datatype_Common -{ - // {{{ _getCollationFieldDeclaration() - - /** - * Obtain DBMS specific SQL code portion needed to set the COLLATION - * of a field declaration to be used in statements like CREATE TABLE. - * - * @param string $collation name of the collation - * - * @return string DBMS specific SQL code portion needed to set the COLLATION - * of a field declaration. - */ - function _getCollationFieldDeclaration($collation) - { - return 'COLLATE '.$collation; - } - - // }}} - // {{{ getTypeDeclaration() - - /** - * Obtain DBMS specific SQL code portion needed to declare an text type - * field to be used in statements like CREATE TABLE. - * - * @param array $field associative array with the name of the properties - * of the field being declared as array indexes. Currently, the types - * of supported field properties are as follows: - * - * length - * Integer value that determines the maximum length of the text - * field. If this argument is missing the field should be - * declared to have the longest length allowed by the DBMS. - * - * default - * Text value to be used as default for this field. - * - * notnull - * Boolean flag that indicates whether this field is constrained - * to not be set to null. - * @return string DBMS specific SQL code portion that should be used to - * declare the specified field. - * @access public - */ - function getTypeDeclaration($field) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - switch ($field['type']) { - case 'text': - $length = !empty($field['length']) - ? $field['length'] : false; - $fixed = !empty($field['fixed']) ? $field['fixed'] : false; - return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')') - : ($length ? 'VARCHAR('.$length.')' : 'TEXT'); - case 'clob': - if (!empty($field['length'])) { - $length = $field['length']; - if ($length <= 255) { - return 'TINYTEXT'; - } elseif ($length <= 65532) { - return 'TEXT'; - } elseif ($length <= 16777215) { - return 'MEDIUMTEXT'; - } - } - return 'LONGTEXT'; - case 'blob': - if (!empty($field['length'])) { - $length = $field['length']; - if ($length <= 255) { - return 'TINYBLOB'; - } elseif ($length <= 65532) { - return 'BLOB'; - } elseif ($length <= 16777215) { - return 'MEDIUMBLOB'; - } - } - return 'LONGBLOB'; - case 'integer': - if (!empty($field['length'])) { - $length = $field['length']; - if ($length <= 2) { - return 'SMALLINT'; - } elseif ($length == 3 || $length == 4) { - return 'INTEGER'; - } elseif ($length > 4) { - return 'BIGINT'; - } - } - return 'INTEGER'; - case 'boolean': - return 'BOOLEAN'; - case 'date': - return 'DATE'; - case 'time': - return 'TIME'; - case 'timestamp': - return 'DATETIME'; - case 'float': - return 'DOUBLE'.($db->options['fixed_float'] ? '('. - ($db->options['fixed_float']+2).','.$db->options['fixed_float'].')' : ''); - case 'decimal': - $length = !empty($field['length']) ? $field['length'] : 18; - $scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places']; - return 'DECIMAL('.$length.','.$scale.')'; - } - return ''; - } - - // }}} - // {{{ _getIntegerDeclaration() - - /** - * Obtain DBMS specific SQL code portion needed to declare an integer type - * field to be used in statements like CREATE TABLE. - * - * @param string $name name the field to be declared. - * @param string $field associative array with the name of the properties - * of the field being declared as array indexes. - * Currently, the types of supported field - * properties are as follows: - * - * unsigned - * Boolean flag that indicates whether the field - * should be declared as unsigned integer if - * possible. - * - * default - * Integer value to be used as default for this - * field. - * - * notnull - * Boolean flag that indicates whether this field is - * constrained to not be set to null. - * @return string DBMS specific SQL code portion that should be used to - * declare the specified field. - * @access protected - */ - function _getIntegerDeclaration($name, $field) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $default = $autoinc = ''; - if (!empty($field['autoincrement'])) { - $autoinc = ' PRIMARY KEY AUTOINCREMENT'; - } elseif (array_key_exists('default', $field)) { - if ($field['default'] === '') { - $field['default'] = empty($field['notnull']) ? null : 0; - } - $default = ' DEFAULT '.$this->quote($field['default'], 'integer'); - } - - $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; - $unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED'; - $name = $db->quoteIdentifier($name, true); - if($autoinc) { - return $name.' '.$this->getTypeDeclaration($field).$autoinc; - }else{ - return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc; - } - } - - // }}} - // {{{ matchPattern() - - /** - * build a pattern matching string - * - * @access public - * - * @param array $pattern even keys are strings, odd are patterns (% and _) - * @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future) - * @param string $field optional field name that is being matched against - * (might be required when emulating ILIKE) - * - * @return string SQL pattern - */ - function matchPattern($pattern, $operator = null, $field = null) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $match = ''; - if (!is_null($operator)) { - $field = is_null($field) ? '' : $field.' '; - $operator = strtoupper($operator); - switch ($operator) { - // case insensitive - case 'ILIKE': - $match = $field.'LIKE '; - break; - // case sensitive - case 'LIKE': - $match = $field.'LIKE '; - break; - default: - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'not a supported operator type:'. $operator, __FUNCTION__); - } - } - $match.= "'"; - foreach ($pattern as $key => $value) { - if ($key % 2) { - $match.= $value; - } else { - $match.= $db->escapePattern($db->escape($value)); - } - } - $match.= "'"; - $match.= $this->patternEscapeString(); - return $match; - } - - // }}} - // {{{ _mapNativeDatatype() - - /** - * Maps a native array description of a field to a MDB2 datatype and length - * - * @param array $field native field description - * @return array containing the various possible types, length, sign, fixed - * @access public - */ - function _mapNativeDatatype($field) - { - $db_type = strtolower($field['type']); - $length = !empty($field['length']) ? $field['length'] : null; - $unsigned = !empty($field['unsigned']) ? $field['unsigned'] : null; - $fixed = null; - $type = array(); - switch ($db_type) { - case 'boolean': - $type[] = 'boolean'; - break; - case 'tinyint': - $type[] = 'integer'; - $type[] = 'boolean'; - if (preg_match('/^(is|has)/', $field['name'])) { - $type = array_reverse($type); - } - $unsigned = preg_match('/ unsigned/i', $field['type']); - $length = 1; - break; - case 'smallint': - $type[] = 'integer'; - $unsigned = preg_match('/ unsigned/i', $field['type']); - $length = 2; - break; - case 'mediumint': - $type[] = 'integer'; - $unsigned = preg_match('/ unsigned/i', $field['type']); - $length = 3; - break; - case 'int': - case 'integer': - case 'serial': - $type[] = 'integer'; - $unsigned = preg_match('/ unsigned/i', $field['type']); - $length = 4; - break; - case 'bigint': - case 'bigserial': - $type[] = 'integer'; - $unsigned = preg_match('/ unsigned/i', $field['type']); - $length = 8; - break; - case 'clob': - $type[] = 'clob'; - $fixed = false; - break; - case 'tinytext': - case 'mediumtext': - case 'longtext': - case 'text': - case 'varchar': - case 'varchar2': - $fixed = false; - case 'char': - $type[] = 'text'; - if ($length == '1') { - $type[] = 'boolean'; - if (preg_match('/^(is|has)/', $field['name'])) { - $type = array_reverse($type); - } - } elseif (strstr($db_type, 'text')) { - $type[] = 'clob'; - $type = array_reverse($type); - } - if ($fixed !== false) { - $fixed = true; - } - break; - case 'date': - $type[] = 'date'; - $length = null; - break; - case 'datetime': - case 'timestamp': - $type[] = 'timestamp'; - $length = null; - break; - case 'time': - $type[] = 'time'; - $length = null; - break; - case 'float': - case 'double': - case 'real': - $type[] = 'float'; - break; - case 'decimal': - case 'numeric': - $type[] = 'decimal'; - $length = $length.','.$field['decimal']; - break; - case 'tinyblob': - case 'mediumblob': - case 'longblob': - case 'blob': - $type[] = 'blob'; - $length = null; - break; - case 'year': - $type[] = 'integer'; - $type[] = 'date'; - $length = null; - break; - default: - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'unknown database attribute type: '.$db_type, __FUNCTION__); - } - - if ((int)$length <= 0) { - $length = null; - } - - return array($type, $length, $unsigned, $fixed); - } - - // }}} -} diff --git a/lib/MDB2/Driver/Function/sqlite3.php b/lib/MDB2/Driver/Function/sqlite3.php deleted file mode 100644 index 4147a48199f..00000000000 --- a/lib/MDB2/Driver/Function/sqlite3.php +++ /dev/null @@ -1,136 +0,0 @@ -. - * - */ - -require_once 'MDB2/Driver/Function/Common.php'; - -/** - * MDB2 SQLite driver for the function modules - * - * @package MDB2 - * @category Database - * @author Lukas Smith - */ -class MDB2_Driver_Function_sqlite3 extends MDB2_Driver_Function_Common -{ - // {{{ constructor - - /** - * Constructor - */ - function __construct($db_index) - { - parent::__construct($db_index); - // create all sorts of UDFs - } - - // {{{ now() - - /** - * Return string to call a variable with the current timestamp inside an SQL statement - * There are three special variables for current date and time. - * - * @return string to call a variable with the current timestamp - * @access public - */ - function now($type = 'timestamp') - { - switch ($type) { - case 'time': - return 'CURRENT_TIME'; - case 'date': - return 'CURRENT_DATE'; - case 'timestamp': - default: - return 'CURRENT_TIMESTAMP'; - } - } - - // }}} - // {{{ unixtimestamp() - - /** - * return string to call a function to get the unix timestamp from a iso timestamp - * - * @param string $expression - * - * @return string to call a variable with the timestamp - * @access public - */ - function unixtimestamp($expression) - { - return 'strftime("%s",'. $expression.', "utc")'; - } - - // }}} - // {{{ substring() - - /** - * return string to call a function to get a substring inside an SQL statement - * - * @return string to call a function to get a substring - * @access public - */ - function substring($value, $position = 1, $length = null) - { - if (!is_null($length)) { - return "substr($value, $position, $length)"; - } - return "substr($value, $position, length($value))"; - } - - // }}} - // {{{ random() - - /** - * return string to call a function to get random value inside an SQL statement - * - * @return return string to generate float between 0 and 1 - * @access public - */ - function random() - { - return '((RANDOM()+2147483648)/4294967296)'; - } - - // }}} - // {{{ replace() - - /** - * return string to call a function to get a replacement inside an SQL statement. - * - * @return string to call a function to get a replace - * @access public - */ - function replace($str, $from_str, $to_str) - { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'method not implemented', __FUNCTION__); - return $error; - } - - // }}} -} diff --git a/lib/MDB2/Driver/Manager/sqlite3.php b/lib/MDB2/Driver/Manager/sqlite3.php deleted file mode 100644 index 921153c17dd..00000000000 --- a/lib/MDB2/Driver/Manager/sqlite3.php +++ /dev/null @@ -1,1362 +0,0 @@ -. - * - */ - -require_once 'MDB2/Driver/Manager/Common.php'; - -/** - * MDB2 SQLite driver for the management modules - * - * @package MDB2 - * @category Database - * @author Lukas Smith - * @author Lorenzo Alberton - */ -class MDB2_Driver_Manager_sqlite3 extends MDB2_Driver_Manager_Common -{ - // {{{ createDatabase() - - /** - * create a new database - * - * @param string $name name of the database that should be created - * @param array $options array with charset info - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function createDatabase($name, $options = array()) - { - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $database_file = $db->_getDatabaseFile($name); - if (file_exists($database_file)) { - return $db->raiseError(MDB2_ERROR_ALREADY_EXISTS, null, null, - 'database already exists', __FUNCTION__); - } - $php_errormsg = ''; - $database_file="$datadir/$database_file.db"; - $handle=new SQLite3($database_file); - if (!$handle) { - return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, - (isset($php_errormsg) ? $php_errormsg : 'could not create the database file'), __FUNCTION__); - } - //sqlite doesn't support the latin1 we use -// if (!empty($options['charset'])) { -// $query = 'PRAGMA encoding = ' . $db->quote($options['charset'], 'text'); -// $handle->exec($query); -// } - $handle->close(); - return MDB2_OK; - } - - // }}} - // {{{ dropDatabase() - - /** - * drop an existing database - * - * @param string $name name of the database that should be dropped - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function dropDatabase($name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $database_file = $db->_getDatabaseFile($name); - if (!@file_exists($database_file)) { - return $db->raiseError(MDB2_ERROR_CANNOT_DROP, null, null, - 'database does not exist', __FUNCTION__); - } - $result = @unlink($database_file); - if (!$result) { - return $db->raiseError(MDB2_ERROR_CANNOT_DROP, null, null, - (isset($php_errormsg) ? $php_errormsg : 'could not remove the database file'), __FUNCTION__); - } - return MDB2_OK; - } - - // }}} - // {{{ _getAdvancedFKOptions() - - /** - * Return the FOREIGN KEY query section dealing with non-standard options - * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... - * - * @param array $definition - * @return string - * @access protected - */ - function _getAdvancedFKOptions($definition) - { - $query = ''; - if (!empty($definition['match'])) { - $query .= ' MATCH '.$definition['match']; - } - if (!empty($definition['onupdate']) && (strtoupper($definition['onupdate']) != 'NO ACTION')) { - $query .= ' ON UPDATE '.$definition['onupdate']; - } - if (!empty($definition['ondelete']) && (strtoupper($definition['ondelete']) != 'NO ACTION')) { - $query .= ' ON DELETE '.$definition['ondelete']; - } - if (!empty($definition['deferrable'])) { - $query .= ' DEFERRABLE'; - } else { - $query .= ' NOT DEFERRABLE'; - } - if (!empty($definition['initiallydeferred'])) { - $query .= ' INITIALLY DEFERRED'; - } else { - $query .= ' INITIALLY IMMEDIATE'; - } - return $query; - } - - // }}} - // {{{ _getCreateTableQuery() - - /** - * Create a basic SQL query for a new table creation - * @param string $name Name of the database that should be created - * @param array $fields Associative array that contains the definition of each field of the new table - * @param array $options An associative array of table options - * @return mixed string (the SQL query) on success, a MDB2 error on failure - * @see createTable() - */ - function _getCreateTableQuery($name, $fields, $options = array()) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - if (!$name) { - return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, - 'no valid table name specified', __FUNCTION__); - } - if (empty($fields)) { - return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, - 'no fields specified for table "'.$name.'"', __FUNCTION__); - } - $query_fields = $this->getFieldDeclarationList($fields); - if (PEAR::isError($query_fields)) { - return $query_fields; - } - if (!empty($options['foreign_keys'])) { - foreach ($options['foreign_keys'] as $fkname => $fkdef) { - if (empty($fkdef)) { - continue; - } - $query_fields.= ', CONSTRAINT '.$fkname.' FOREIGN KEY ('.implode(', ', array_keys($fkdef['fields'])).')'; - $query_fields.= ' REFERENCES '.$fkdef['references']['table'].' ('.implode(', ', array_keys($fkdef['references']['fields'])).')'; - $query_fields.= $this->_getAdvancedFKOptions($fkdef); - } - } - - $name = $db->quoteIdentifier($name, true); - $result = 'CREATE '; - if (!empty($options['temporary'])) { - $result .= $this->_getTemporaryTableQuery(); - } - $result .= " TABLE $name ($query_fields)"; - return $result; - } - - // }}} - // {{{ createTable() - - /** - * create a new table - * - * @param string $name Name of the database that should be created - * @param array $fields Associative array that contains the definition - * of each field of the new table - * @param array $options An associative array of table options - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function createTable($name, $fields, $options = array()) - { - $result = parent::createTable($name, $fields, $options); - if (PEAR::isError($result)) { - return $result; - } - // create triggers to enforce FOREIGN KEY constraints - if (!empty($options['foreign_keys'])) { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - foreach ($options['foreign_keys'] as $fkname => $fkdef) { - if (empty($fkdef)) { - continue; - } - //set actions to default if not set - $fkdef['onupdate'] = empty($fkdef['onupdate']) ? $db->options['default_fk_action_onupdate'] : strtoupper($fkdef['onupdate']); - $fkdef['ondelete'] = empty($fkdef['ondelete']) ? $db->options['default_fk_action_ondelete'] : strtoupper($fkdef['ondelete']); - - $trigger_names = array( - 'insert' => $fkname.'_insert_trg', - 'update' => $fkname.'_update_trg', - 'pk_update' => $fkname.'_pk_update_trg', - 'pk_delete' => $fkname.'_pk_delete_trg', - ); - - //create the [insert|update] triggers on the FK table - $table_fields = array_keys($fkdef['fields']); - $referenced_fields = array_keys($fkdef['references']['fields']); - $query = 'CREATE TRIGGER %s BEFORE %s ON '.$name - .' FOR EACH ROW BEGIN' - .' SELECT RAISE(ROLLBACK, \'%s on table "'.$name.'" violates FOREIGN KEY constraint "'.$fkname.'"\')' - .' WHERE (SELECT '; - $aliased_fields = array(); - foreach ($referenced_fields as $field) { - $aliased_fields[] = $fkdef['references']['table'] .'.'.$field .' AS '.$field; - } - $query .= implode(',', $aliased_fields) - .' FROM '.$fkdef['references']['table'] - .' WHERE '; - $conditions = array(); - for ($i=0; $iexec(sprintf($query, $trigger_names['insert'], 'INSERT', 'insert')); - if (PEAR::isError($result)) { - return $result; - } - - $result = $db->exec(sprintf($query, $trigger_names['update'], 'UPDATE', 'update')); - if (PEAR::isError($result)) { - return $result; - } - - //create the ON [UPDATE|DELETE] triggers on the primary table - $restrict_action = 'SELECT RAISE(ROLLBACK, \'%s on table "'.$name.'" violates FOREIGN KEY constraint "'.$fkname.'"\')' - .' WHERE (SELECT '; - $aliased_fields = array(); - foreach ($table_fields as $field) { - $aliased_fields[] = $name .'.'.$field .' AS '.$field; - } - $restrict_action .= implode(',', $aliased_fields) - .' FROM '.$name - .' WHERE '; - $conditions = array(); - $new_values = array(); - $null_values = array(); - for ($i=0; $i OLD.'.$referenced_fields[$i]; - } - $restrict_action .= implode(' AND ', $conditions).') IS NOT NULL' - .' AND (' .implode(' OR ', $conditions2) .')'; - - $cascade_action_update = 'UPDATE '.$name.' SET '.implode(', ', $new_values) .' WHERE '.implode(' AND ', $conditions); - $cascade_action_delete = 'DELETE FROM '.$name.' WHERE '.implode(' AND ', $conditions); - $setnull_action = 'UPDATE '.$name.' SET '.implode(', ', $null_values).' WHERE '.implode(' AND ', $conditions); - - if ('SET DEFAULT' == $fkdef['onupdate'] || 'SET DEFAULT' == $fkdef['ondelete']) { - $db->loadModule('Reverse', null, true); - $default_values = array(); - foreach ($table_fields as $table_field) { - $field_definition = $db->reverse->getTableFieldDefinition($name, $field); - if (PEAR::isError($field_definition)) { - return $field_definition; - } - $default_values[] = $table_field .' = '. $field_definition[0]['default']; - } - $setdefault_action = 'UPDATE '.$name.' SET '.implode(', ', $default_values).' WHERE '.implode(' AND ', $conditions); - } - - $query = 'CREATE TRIGGER %s' - .' %s ON '.$fkdef['references']['table'] - .' FOR EACH ROW BEGIN '; - - if ('CASCADE' == $fkdef['onupdate']) { - $sql_update = sprintf($query, $trigger_names['pk_update'], 'AFTER UPDATE', 'update') . $cascade_action_update. '; END;'; - } elseif ('SET NULL' == $fkdef['onupdate']) { - $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setnull_action. '; END;'; - } elseif ('SET DEFAULT' == $fkdef['onupdate']) { - $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setdefault_action. '; END;'; - } elseif ('NO ACTION' == $fkdef['onupdate']) { - $sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'AFTER UPDATE', 'update') . '; END;'; - } elseif ('RESTRICT' == $fkdef['onupdate']) { - $sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . '; END;'; - } - if ('CASCADE' == $fkdef['ondelete']) { - $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete') . $cascade_action_delete. '; END;'; - } elseif ('SET NULL' == $fkdef['ondelete']) { - $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setnull_action. '; END;'; - } elseif ('SET DEFAULT' == $fkdef['ondelete']) { - $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setdefault_action. '; END;'; - } elseif ('NO ACTION' == $fkdef['ondelete']) { - $sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete') . '; END;'; - } elseif ('RESTRICT' == $fkdef['ondelete']) { - $sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . '; END;'; - } - - if (PEAR::isError($result)) { - return $result; - } - $result = $db->exec($sql_delete); - if (PEAR::isError($result)) { - return $result; - } - $result = $db->exec($sql_update); - if (PEAR::isError($result)) { - return $result; - } - } - } - if (PEAR::isError($result)) { - return $result; - } - return MDB2_OK; - } - - // }}} - // {{{ dropTable() - - /** - * drop an existing table - * - * @param string $name name of the table that should be dropped - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function dropTable($name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - //delete the triggers associated to existing FK constraints - $constraints = $this->listTableConstraints($name); - if (!PEAR::isError($constraints) && !empty($constraints)) { - $db->loadModule('Reverse', null, true); - foreach ($constraints as $constraint) { - $definition = $db->reverse->getTableConstraintDefinition($name, $constraint); - if (!PEAR::isError($definition) && !empty($definition['foreign'])) { - $result = $this->_dropFKTriggers($name, $constraint, $definition['references']['table']); - if (PEAR::isError($result)) { - return $result; - } - } - } - } - - $name = $db->quoteIdentifier($name, true); - return $db->exec("DROP TABLE $name"); - } - - // }}} - // {{{ vacuum() - - /** - * Optimize (vacuum) all the tables in the db (or only the specified table) - * and optionally run ANALYZE. - * - * @param string $table table name (all the tables if empty) - * @param array $options an array with driver-specific options: - * - timeout [int] (in seconds) [mssql-only] - * - analyze [boolean] [pgsql and mysql] - * - full [boolean] [pgsql-only] - * - freeze [boolean] [pgsql-only] - * - * @return mixed MDB2_OK success, a MDB2 error on failure - * @access public - */ - function vacuum($table = null, $options = array()) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = 'VACUUM'; - if (!empty($table)) { - $query .= ' '.$db->quoteIdentifier($table, true); - } - return $db->exec($query); - } - - // }}} - // {{{ alterTable() - - /** - * alter an existing table - * - * @param string $name name of the table that is intended to be changed. - * @param array $changes associative array that contains the details of each type - * of change that is intended to be performed. The types of - * changes that are currently supported are defined as follows: - * - * name - * - * New name for the table. - * - * add - * - * Associative array with the names of fields to be added as - * indexes of the array. The value of each entry of the array - * should be set to another associative array with the properties - * of the fields to be added. The properties of the fields should - * be the same as defined by the MDB2 parser. - * - * - * remove - * - * Associative array with the names of fields to be removed as indexes - * of the array. Currently the values assigned to each entry are ignored. - * An empty array should be used for future compatibility. - * - * rename - * - * Associative array with the names of fields to be renamed as indexes - * of the array. The value of each entry of the array should be set to - * another associative array with the entry named name with the new - * field name and the entry named Declaration that is expected to contain - * the portion of the field declaration already in DBMS specific SQL code - * as it is used in the CREATE TABLE statement. - * - * change - * - * Associative array with the names of the fields to be changed as indexes - * of the array. Keep in mind that if it is intended to change either the - * name of a field and any other properties, the change array entries - * should have the new names of the fields as array indexes. - * - * The value of each entry of the array should be set to another associative - * array with the properties of the fields to that are meant to be changed as - * array entries. These entries should be assigned to the new values of the - * respective properties. The properties of the fields should be the same - * as defined by the MDB2 parser. - * - * Example - * array( - * 'name' => 'userlist', - * 'add' => array( - * 'quota' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * ) - * ), - * 'remove' => array( - * 'file_limit' => array(), - * 'time_limit' => array() - * ), - * 'change' => array( - * 'name' => array( - * 'length' => '20', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 20, - * ), - * ) - * ), - * 'rename' => array( - * 'sex' => array( - * 'name' => 'gender', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 1, - * 'default' => 'M', - * ), - * ) - * ) - * ) - * - * @param boolean $check indicates whether the function should just check if the DBMS driver - * can perform the requested table alterations if the value is true or - * actually perform them otherwise. - * @access public - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - */ - function alterTable($name, $changes, $check, $options = array()) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - foreach ($changes as $change_name => $change) { - switch ($change_name) { - case 'add': - case 'remove': - case 'change': - case 'name': - case 'rename': - break; - default: - return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null, - 'change type "'.$change_name.'" not yet supported', __FUNCTION__); - } - } - - if ($check) { - return MDB2_OK; - } - - if (empty($changes['remove']) and empty($changes['rename']) and empty($changes['change']) ) {//if only rename or add changes are required, we can use ALTER TABLE - $query = ''; - if (!empty($changes['name'])) { - $change_name = $db->quoteIdentifier($changes['name'], true); - $query = 'RENAME TO ' . $change_name; - $db->exec("ALTER TABLE $name $query"); - } - - if (!empty($changes['add']) && is_array($changes['add'])) { - foreach ($changes['add'] as $field_name => $field) { - $query= 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field); - $db->exec("ALTER TABLE $name $query"); - } - } - return MDB2_OK; - } - - $db->loadModule('Reverse', null, true); - - // for other operations we need to emulate them with sqlite3 - $fields = $db->manager->listTableFields($name); - if (PEAR::isError($fields)) { - return $fields; - } - - $fields = array_flip($fields); - foreach ($fields as $field => $value) { - $definition = $db->reverse->getTableFieldDefinition($name, $field); - if (PEAR::isError($definition)) { - return $definition; - } - $fields[$field] = $definition[0]; - } - - $indexes = $db->manager->listTableIndexes($name); - if (PEAR::isError($indexes)) { - return $indexes; - } - - $indexes = array_flip($indexes); - foreach ($indexes as $index => $value) { - $definition = $db->reverse->getTableIndexDefinition($name, $index); - if (PEAR::isError($definition)) { - return $definition; - } - $indexes[$index] = $definition; - } - - $constraints = $db->manager->listTableConstraints($name); - if (PEAR::isError($constraints)) { - return $constraints; - } - - if (!array_key_exists('foreign_keys', $options)) { - $options['foreign_keys'] = array(); - } - $constraints = array_flip($constraints); - foreach ($constraints as $constraint => $value) { - if (!empty($definition['primary'])) { - if (!array_key_exists('primary', $options)) { - $options['primary'] = $definition['fields']; - //remove from the $constraint array, it's already handled by createTable() - unset($constraints[$constraint]); - } - } else { - $c_definition = $db->reverse->getTableConstraintDefinition($name, $constraint); - if (PEAR::isError($c_definition)) { - return $c_definition; - } - if (!empty($c_definition['foreign'])) { - if (!array_key_exists($constraint, $options['foreign_keys'])) { - $options['foreign_keys'][$constraint] = $c_definition; - } - //remove from the $constraint array, it's already handled by createTable() - unset($constraints[$constraint]); - } else { - $constraints[$constraint] = $c_definition; - } - } - } - - $name_new = $name; - $create_order = $select_fields = array_keys($fields); - foreach ($changes as $change_name => $change) { - switch ($change_name) { - case 'add': - foreach ($change as $field_name => $field) { - $fields[$field_name] = $field; - $create_order[] = $field_name; - } - break; - case 'remove': - foreach ($change as $field_name => $field) { - unset($fields[$field_name]); - $select_fields = array_diff($select_fields, array($field_name)); - $create_order = array_diff($create_order, array($field_name)); - } - break; - case 'change': - foreach ($change as $field_name => $field) { - $fields[$field_name] = $field['definition']; - } - break; - case 'name': - $name_new = $change; - break; - case 'rename': - foreach ($change as $field_name => $field) { - unset($fields[$field_name]); - $fields[$field['name']] = $field['definition']; - $create_order[array_search($field_name, $create_order)] = $field['name']; - } - break; - default: - return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null, - 'change type "'.$change_name.'" not yet supported', __FUNCTION__); - } - } - - //rename the old table so we can create the new one - $db->exec("ALTER TABLE $name RENAME TO __$name"); - $data = null; - - - $result = $this->createTable($name_new, $fields, $options); - if (PEAR::isError($result)) { - return $result; - } - - //these seem to only give errors - -// foreach ($indexes as $index => $definition) { -// $this->createIndex($name_new, $index, $definition); -// } - -// foreach ($constraints as $constraint => $definition) { -// $this->createConstraint($name_new, $constraint, $definition); -// } - - //fill the new table with data from the old one - if (!empty($select_fields)) { - $query = 'INSERT INTO '.$db->quoteIdentifier($name_new, true); - $query.= '('.implode(', ', array_slice(array_keys($fields), 0, count($select_fields))).')'; - $query .= ' SELECT '.implode(', ', $select_fields).' FROM '.$db->quoteIdentifier('__'.$name, true); - $db->exec($query); - } - -// if (!empty($select_fields) && !empty($data)) { -// $query = 'INSERT INTO '.$db->quoteIdentifier($name_new, true); -// $query.= '('.implode(', ', array_slice(array_keys($fields), 0, count($select_fields))).')'; -// $query.=' VALUES (?'.str_repeat(', ?', (count($select_fields) - 1)).')'; -// $stmt =$db->prepare($query, null, MDB2_PREPARE_MANIP); -// if (PEAR::isError($stmt)) { -// return $stmt; -// } -// foreach ($data as $row) { -// $result = $stmt->execute($row); -// if (PEAR::isError($result)) { -// return $result; -// } -// } -// } - - //remove the old table - $result = $this->dropTable('__'.$name); - if (PEAR::isError($result)) { - return $result; - } - return MDB2_OK; - } - - // }}} - // {{{ listDatabases() - - /** - * list all databases - * - * @return mixed array of database names on success, a MDB2 error on failure - * @access public - */ - function listDatabases() - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'list databases is not supported', __FUNCTION__); - } - - // }}} - // {{{ listUsers() - - /** - * list all users - * - * @return mixed array of user names on success, a MDB2 error on failure - * @access public - */ - function listUsers() - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'list databases is not supported', __FUNCTION__); - } - - // }}} - // {{{ listViews() - - /** - * list all views in the current database - * - * @return mixed array of view names on success, a MDB2 error on failure - * @access public - */ - function listViews($dummy=null) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name FROM sqlite_master WHERE type='view' AND sql NOT NULL"; - $result = $db->queryCol($query); - if (PEAR::isError($result)) { - return $result; - } - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); - } - return $result; - } - - // }}} - // {{{ listTableViews() - - /** - * list the views in the database that reference a given table - * - * @param string table for which all referenced views should be found - * @return mixed array of view names on success, a MDB2 error on failure - * @access public - */ - function listTableViews($table) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; - $views = $db->queryAll($query, array('text', 'text'), MDB2_FETCHMODE_ASSOC); - if (PEAR::isError($views)) { - return $views; - } - $result = array(); - foreach ($views as $row) { - if (preg_match("/^create view .* \bfrom\b\s+\b{$table}\b /i", $row['sql'])) { - if (!empty($row['name'])) { - $result[$row['name']] = true; - } - } - } - - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_change_key_case($result, $db->options['field_case']); - } - return array_keys($result); - } - - // }}} - // {{{ listTables() - - /** - * list all tables in the current database - * - * @return mixed array of table names on success, a MDB2 error on failure - * @access public - */ - function listTables($dummy=null) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL AND name!='sqlite_sequence' ORDER BY name"; - $table_names = $db->queryCol($query); - if (PEAR::isError($table_names)) { - return $table_names; - } - $result = array(); - foreach ($table_names as $table_name) { - if (!$this->_fixSequenceName($table_name, true)) { - $result[] = $table_name; - } - } - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); - } - return $result; - } - - // }}} - // {{{ listTableFields() - - /** - * list all fields in a table in the current database - * - * @param string $table name of table that should be used in method - * @return mixed array of field names on success, a MDB2 error on failure - * @access public - */ - function listTableFields($table) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $result = $db->loadModule('Reverse', null, true); - if (PEAR::isError($result)) { - return $result; - } - $query = "SELECT sql FROM sqlite_master WHERE type='table' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text'); - } else { - $query.= 'name='.$db->quote($table, 'text'); - } - $sql = $db->queryOne($query); - if (PEAR::isError($sql)) { - return $sql; - } - $columns = $db->reverse->_getTableColumns($sql); - $fields = array(); - foreach ($columns as $column) { - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - if ($db->options['field_case'] == CASE_LOWER) { - $column['name'] = strtolower($column['name']); - } else { - $column['name'] = strtoupper($column['name']); - } - } else { - $column = array_change_key_case($column, $db->options['field_case']); - } - $fields[] = $column['name']; - } - return $fields; - } - - // }}} - // {{{ listTableTriggers() - - /** - * list all triggers in the database that reference a given table - * - * @param string table for which all referenced triggers should be found - * @return mixed array of trigger names on success, a MDB2 error on failure - * @access public - */ - function listTableTriggers($table = null) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name FROM sqlite_master WHERE type='trigger' AND sql NOT NULL"; - if (!is_null($table)) { - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= ' AND LOWER(tbl_name)='.$db->quote(strtolower($table), 'text'); - } else { - $query.= ' AND tbl_name='.$db->quote($table, 'text'); - } - } - $result = $db->queryCol($query); - if (PEAR::isError($result)) { - return $result; - } - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); - } - return $result; - } - - // }}} - // {{{ createIndex() - - /** - * Get the stucture of a field into an array - * - * @param string $table name of the table on which the index is to be created - * @param string $name name of the index to be created - * @param array $definition associative array that defines properties of the index to be created. - * Currently, only one property named FIELDS is supported. This property - * is also an associative with the names of the index fields as array - * indexes. Each entry of this array is set to another type of associative - * array that specifies properties of the index that are specific to - * each field. - * - * Currently, only the sorting property is supported. It should be used - * to define the sorting direction of the index. It may be set to either - * ascending or descending. - * - * Not all DBMS support index sorting direction configuration. The DBMS - * drivers of those that do not support it ignore this property. Use the - * function support() to determine whether the DBMS driver can manage indexes. - - * Example - * array( - * 'fields' => array( - * 'user_name' => array( - * 'sorting' => 'ascending' - * ), - * 'last_login' => array() - * ) - * ) - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function createIndex($table, $name, $definition) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $table = $db->quoteIdentifier($table, true); - $name = $db->getIndexName($name); - $query = "CREATE INDEX $name ON $table"; - $fields = array(); - foreach ($definition['fields'] as $field_name => $field) { - $field_string = $field_name; - if (!empty($field['sorting'])) { - switch ($field['sorting']) { - case 'ascending': - $field_string.= ' ASC'; - break; - case 'descending': - $field_string.= ' DESC'; - break; - } - } - $fields[] = $field_string; - } - $query .= ' ('.implode(', ', $fields) . ')'; - return $db->exec($query); - } - - // }}} - // {{{ dropIndex() - - /** - * drop existing index - * - * @param string $table name of table that should be used in method - * @param string $name name of the index to be dropped - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function dropIndex($table, $name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $name = $db->getIndexName($name); - return $db->exec("DROP INDEX $name"); - } - - // }}} - // {{{ listTableIndexes() - - /** - * list all indexes in a table - * - * @param string $table name of table that should be used in method - * @return mixed array of index names on success, a MDB2 error on failure - * @access public - */ - function listTableIndexes($table) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $table = $db->quote($table, 'text'); - $query = "SELECT sql FROM sqlite_master WHERE type='index' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(tbl_name)='.strtolower($table); - } else { - $query.= "tbl_name=$table"; - } - $query.= " AND sql NOT NULL ORDER BY name"; - $indexes = $db->queryCol($query, 'text'); - if (PEAR::isError($indexes)) { - return $indexes; - } - - $result = array(); - foreach ($indexes as $sql) { - if (preg_match("/^create index ([^ ]+) on /i", $sql, $tmp)) { - $index = $this->_fixIndexName($tmp[1]); - if (!empty($index)) { - $result[$index] = true; - } - } - } - - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_change_key_case($result, $db->options['field_case']); - } - return array_keys($result); - } - - // }}} - // {{{ createConstraint() - - /** - * create a constraint on a table - * - * @param string $table name of the table on which the constraint is to be created - * @param string $name name of the constraint to be created - * @param array $definition associative array that defines properties of the constraint to be created. - * Currently, only one property named FIELDS is supported. This property - * is also an associative with the names of the constraint fields as array - * constraints. Each entry of this array is set to another type of associative - * array that specifies properties of the constraint that are specific to - * each field. - * - * Example - * array( - * 'fields' => array( - * 'user_name' => array(), - * 'last_login' => array() - * ) - * ) - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function createConstraint($table, $name, $definition) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - if (!empty($definition['primary'])) { - return $db->manager->alterTable($table, array(), false, array('primary' => $definition['fields'])); - } - - if (!empty($definition['foreign'])) { - return $db->manager->alterTable($table, array(), false, array('foreign_keys' => array($name => $definition))); - } - - $table = $db->quoteIdentifier($table, true); - $name = $db->getIndexName($name); - $query = "CREATE UNIQUE INDEX $name ON $table"; - $fields = array(); - foreach ($definition['fields'] as $field_name => $field) { - $field_string = $field_name; - if (!empty($field['sorting'])) { - switch ($field['sorting']) { - case 'ascending': - $field_string.= ' ASC'; - break; - case 'descending': - $field_string.= ' DESC'; - break; - } - } - $fields[] = $field_string; - } - $query .= ' ('.implode(', ', $fields) . ')'; - return $db->exec($query); - } - - // }}} - // {{{ dropConstraint() - - /** - * drop existing constraint - * - * @param string $table name of table that should be used in method - * @param string $name name of the constraint to be dropped - * @param string $primary hint if the constraint is primary - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function dropConstraint($table, $name, $primary = false) - { - if ($primary || $name == 'PRIMARY') { - return $this->alterTable($table, array(), false, array('primary' => null)); - } - - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - //is it a FK constraint? If so, also delete the associated triggers - $db->loadModule('Reverse', null, true); - $definition = $db->reverse->getTableConstraintDefinition($table, $name); - if (!PEAR::isError($definition) && !empty($definition['foreign'])) { - //first drop the FK enforcing triggers - $result = $this->_dropFKTriggers($table, $name, $definition['references']['table']); - if (PEAR::isError($result)) { - return $result; - } - //then drop the constraint itself - return $this->alterTable($table, array(), false, array('foreign_keys' => array($name => null))); - } - - $name = $db->getIndexName($name); - return $db->exec("DROP INDEX $name"); - } - - // }}} - // {{{ _dropFKTriggers() - - /** - * Drop the triggers created to enforce the FOREIGN KEY constraint on the table - * - * @param string $table table name - * @param string $fkname FOREIGN KEY constraint name - * @param string $referenced_table referenced table name - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access private - */ - function _dropFKTriggers($table, $fkname, $referenced_table) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $triggers = $this->listTableTriggers($table); - $triggers2 = $this->listTableTriggers($referenced_table); - if (!PEAR::isError($triggers2) && !PEAR::isError($triggers)) { - $triggers = array_merge($triggers, $triggers2); - $pattern = '/^'.$fkname.'(_pk)?_(insert|update|delete)_trg$/i'; - foreach ($triggers as $trigger) { - if (preg_match($pattern, $trigger)) { - $result = $db->exec('DROP TRIGGER '.$trigger); - if (PEAR::isError($result)) { - return $result; - } - } - } - } - return MDB2_OK; - } - - // }}} - // {{{ listTableConstraints() - - /** - * list all constraints in a table - * - * @param string $table name of table that should be used in method - * @return mixed array of constraint names on success, a MDB2 error on failure - * @access public - */ - function listTableConstraints($table) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $table = $db->quote($table, 'text'); - $query = "SELECT sql FROM sqlite_master WHERE type='index' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(tbl_name)='.strtolower($table); - } else { - $query.= "tbl_name=$table"; - } - $query.= " AND sql NOT NULL ORDER BY name"; - $indexes = $db->queryCol($query, 'text'); - if (PEAR::isError($indexes)) { - return $indexes; - } - - $result = array(); - foreach ($indexes as $sql) { - if (preg_match("/^create unique index ([^ ]+) on /i", $sql, $tmp)) { - $index = $this->_fixIndexName($tmp[1]); - if (!empty($index)) { - $result[$index] = true; - } - } - } - - // also search in table definition for PRIMARY KEYs... - $query = "SELECT sql FROM sqlite_master WHERE type='table' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)='.strtolower($table); - } else { - $query.= "name=$table"; - } - $query.= " AND sql NOT NULL ORDER BY name"; - $table_def = $db->queryOne($query, 'text'); - if (PEAR::isError($table_def)) { - return $table_def; - } - if (preg_match("/\bPRIMARY\s+KEY\b/i", $table_def, $tmp)) { - $result['primary'] = true; - } - - // ...and for FOREIGN KEYs - if (preg_match_all("/\bCONSTRAINT\b\s+([^\s]+)\s+\bFOREIGN\s+KEY/imsx", $table_def, $tmp)) { - foreach ($tmp[1] as $fk) { - $result[$fk] = true; - } - } - - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_change_key_case($result, $db->options['field_case']); - } - return array_keys($result); - } - - // }}} - // {{{ createSequence() - - /** - * create sequence - * - * @param string $seq_name name of the sequence to be created - * @param string $start start value of the sequence; default is 1 - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function createSequence($seq_name, $start = 1) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true); - $seqcol_name = $db->quoteIdentifier($db->options['seqcol_name'], true); - $query = "CREATE TABLE $sequence_name ($seqcol_name INTEGER PRIMARY KEY DEFAULT 0 NOT NULL)"; - $res = $db->exec($query); - if (PEAR::isError($res)) { - return $res; - } - if ($start == 1) { - return MDB2_OK; - } - $res = $db->exec("INSERT INTO $sequence_name ($seqcol_name) VALUES (".($start-1).')'); - if (!PEAR::isError($res)) { - return MDB2_OK; - } - // Handle error - $result = $db->exec("DROP TABLE $sequence_name"); - if (PEAR::isError($result)) { - return $db->raiseError($result, null, null, - 'could not drop inconsistent sequence table', __FUNCTION__); - } - return $db->raiseError($res, null, null, - 'could not create sequence table', __FUNCTION__); - } - - // }}} - // {{{ dropSequence() - - /** - * drop existing sequence - * - * @param string $seq_name name of the sequence to be dropped - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function dropSequence($seq_name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true); - return $db->exec("DROP TABLE $sequence_name"); - } - - // }}} - // {{{ listSequences() - - /** - * list all sequences in the current database - * - * @return mixed array of sequence names on success, a MDB2 error on failure - * @access public - */ - function listSequences($dummy=null) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL ORDER BY name"; - $table_names = $db->queryCol($query); - if (PEAR::isError($table_names)) { - return $table_names; - } - $result = array(); - foreach ($table_names as $table_name) { - if ($sqn = $this->_fixSequenceName($table_name, true)) { - $result[] = $sqn; - } - } - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); - } - return $result; - } - - // }}} -} diff --git a/lib/MDB2/Driver/Native/sqlite3.php b/lib/MDB2/Driver/Native/sqlite3.php deleted file mode 100644 index 344d523bdf3..00000000000 --- a/lib/MDB2/Driver/Native/sqlite3.php +++ /dev/null @@ -1,33 +0,0 @@ -. - * - */ -require_once 'MDB2/Driver/Native/Common.php'; - -/** - * MDB2 SQLite driver for the native module - * - * @package MDB2 - * @category Database - * @author Lukas Smith - */ -class MDB2_Driver_Native_sqlite extends MDB2_Driver_Native_Common -{ -} diff --git a/lib/MDB2/Driver/Reverse/sqlite3.php b/lib/MDB2/Driver/Reverse/sqlite3.php deleted file mode 100644 index 97037809549..00000000000 --- a/lib/MDB2/Driver/Reverse/sqlite3.php +++ /dev/null @@ -1,586 +0,0 @@ -. - * - */ - -require_once 'MDB2/Driver/Reverse/Common.php'; - -/** - * MDB2 SQlite driver for the schema reverse engineering module - * - * @package MDB2 - * @category Database - * @author Lukas Smith - */ -class MDB2_Driver_Reverse_sqlite3 extends MDB2_Driver_Reverse_Common -{ - /** - * Remove SQL comments from the field definition - * - * @access private - */ - function _removeComments($sql) { - $lines = explode("\n", $sql); - foreach ($lines as $k => $line) { - $pieces = explode('--', $line); - if (count($pieces) > 1 && (substr_count($pieces[0], '\'') % 2) == 0) { - $lines[$k] = substr($line, 0, strpos($line, '--')); - } - } - return implode("\n", $lines); - } - - /** - * - */ - function _getTableColumns($sql) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - $start_pos = strpos($sql, '('); - $end_pos = strrpos($sql, ')'); - $column_def = substr($sql, $start_pos+1, $end_pos-$start_pos-1); - // replace the decimal length-places-separator with a colon - $column_def = preg_replace('/(\d),(\d)/', '\1:\2', $column_def); - $column_def = $this->_removeComments($column_def); - $column_sql = explode(',', $column_def); - $columns = array(); - $count = count($column_sql); - if ($count == 0) { - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'unexpected empty table column definition list', __FUNCTION__); - } - $regexp = '/^\s*([^\s]+) +(CHAR|VARCHAR|VARCHAR2|TEXT|BOOLEAN|SMALLINT|INT|INTEGER|DECIMAL|BIGINT|DOUBLE|FLOAT|DATETIME|DATE|TIME|LONGTEXT|LONGBLOB)( ?\(([1-9][0-9]*)(:([1-9][0-9]*))?\))?( NULL| NOT NULL)?( UNSIGNED)?( NULL| NOT NULL)?( PRIMARY KEY)?( AUTOINCREMENT)?( DEFAULT (\'[^\']*\'|[^ ]+))?( NULL| NOT NULL)?( PRIMARY KEY)?(\s*\-\-.*)?$/i'; - $regexp2 = '/^\s*([^ ]+) +(PRIMARY|UNIQUE|CHECK)$/i'; - for ($i=0, $j=0; $i<$count; ++$i) { - if (!preg_match($regexp, trim($column_sql[$i]), $matches)) { - if (!preg_match($regexp2, trim($column_sql[$i]))) { - continue; - } - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'unexpected table column SQL definition: "'.$column_sql[$i].'"', __FUNCTION__); - } - $columns[$j]['name'] = trim($matches[1], implode('', $db->identifier_quoting)); - $columns[$j]['type'] = strtolower($matches[2]); - if (isset($matches[4]) && strlen($matches[4])) { - $columns[$j]['length'] = $matches[4]; - } - if (isset($matches[6]) && strlen($matches[6])) { - $columns[$j]['decimal'] = $matches[6]; - } - if (isset($matches[8]) && strlen($matches[8])) { - $columns[$j]['unsigned'] = true; - } - if (isset($matches[10]) && strlen($matches[10])) { - $columns[$j]['autoincrement'] = true; - $columns[$j]['notnull']=true; - } - if (isset($matches[10]) && strlen($matches[10])) { - $columns[$j]['autoincrement'] = true; - $columns[$j]['notnull']=true; - } - if (isset($matches[13]) && strlen($matches[13])) { - $default = $matches[13]; - if (strlen($default) && $default[0]=="'") { - $default = str_replace("''", "'", substr($default, 1, strlen($default)-2)); - } - if ($default === 'NULL') { - $default = null; - } - $columns[$j]['default'] = $default; - } - if (isset($matches[7]) && strlen($matches[7])) { - $columns[$j]['notnull'] = ($matches[7] === ' NOT NULL'); - } else if (isset($matches[9]) && strlen($matches[9])) { - $columns[$j]['notnull'] = ($matches[9] === ' NOT NULL'); - } else if (isset($matches[14]) && strlen($matches[14])) { - $columns[$j]['notnull'] = ($matches[14] === ' NOT NULL'); - } - ++$j; - } - return $columns; - } - - // {{{ getTableFieldDefinition() - - /** - * Get the stucture of a field into an array - * - * @param string $table_name name of table that should be used in method - * @param string $field_name name of field that should be used in method - * @return mixed data array on success, a MDB2 error on failure. - * The returned array contains an array for each field definition, - * with (some of) these indices: - * [notnull] [nativetype] [length] [fixed] [default] [type] [mdb2type] - * @access public - */ - function getTableFieldDefinition($table_name, $field_name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - list($schema, $table) = $this->splitTableSchema($table_name); - - $result = $db->loadModule('Datatype', null, true); - if (PEAR::isError($result)) { - return $result; - } - $query = "SELECT sql FROM sqlite_master WHERE type='table' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text'); - } else { - $query.= 'name='.$db->quote($table, 'text'); - } - $sql = $db->queryOne($query); - if (PEAR::isError($sql)) { - return $sql; - } - $columns = $this->_getTableColumns($sql); - foreach ($columns as $column) { - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - if ($db->options['field_case'] == CASE_LOWER) { - $column['name'] = strtolower($column['name']); - } else { - $column['name'] = strtoupper($column['name']); - } - } else { - $column = array_change_key_case($column, $db->options['field_case']); - } - if ($field_name == $column['name']) { - $mapped_datatype = $db->datatype->mapNativeDatatype($column); - if (PEAR::isError($mapped_datatype)) { - return $mapped_datatype; - } - list($types, $length, $unsigned, $fixed) = $mapped_datatype; - $notnull = false; - if (!empty($column['notnull'])) { - $notnull = $column['notnull']; - } - $default = false; - if (array_key_exists('default', $column)) { - $default = $column['default']; - if (is_null($default) && $notnull) { - $default = ''; - } - } - $autoincrement = false; - if (!empty($column['autoincrement'])) { - $autoincrement = true; - } - - $definition[0] = array( - 'notnull' => $notnull, - 'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type']) - ); - if (!is_null($length)) { - $definition[0]['length'] = $length; - } - if (!is_null($unsigned)) { - $definition[0]['unsigned'] = $unsigned; - } - if (!is_null($fixed)) { - $definition[0]['fixed'] = $fixed; - } - if ($default !== false) { - $definition[0]['default'] = $default; - } - if ($autoincrement !== false) { - $definition[0]['autoincrement'] = $autoincrement; - } - foreach ($types as $key => $type) { - $definition[$key] = $definition[0]; - if ($type == 'clob' || $type == 'blob') { - unset($definition[$key]['default']); - } - $definition[$key]['type'] = $type; - $definition[$key]['mdb2type'] = $type; - } - return $definition; - } - } - - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing table column', __FUNCTION__); - } - - // }}} - // {{{ getTableIndexDefinition() - - /** - * Get the stucture of an index into an array - * - * @param string $table_name name of table that should be used in method - * @param string $index_name name of index that should be used in method - * @return mixed data array on success, a MDB2 error on failure - * @access public - */ - function getTableIndexDefinition($table_name, $index_name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - list($schema, $table) = $this->splitTableSchema($table_name); - - $query = "SELECT sql FROM sqlite_master WHERE type='index' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text'); - } else { - $query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text'); - } - $query.= ' AND sql NOT NULL ORDER BY name'; - $index_name_mdb2 = $db->getIndexName($index_name); - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $qry = sprintf($query, $db->quote(strtolower($index_name_mdb2), 'text')); - } else { - $qry = sprintf($query, $db->quote($index_name_mdb2, 'text')); - } - $sql = $db->queryOne($qry, 'text'); - if (PEAR::isError($sql) || empty($sql)) { - // fallback to the given $index_name, without transformation - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $qry = sprintf($query, $db->quote(strtolower($index_name), 'text')); - } else { - $qry = sprintf($query, $db->quote($index_name, 'text')); - } - $sql = $db->queryOne($qry, 'text'); - } - if (PEAR::isError($sql)) { - return $sql; - } - if (!$sql) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing table index', __FUNCTION__); - } - - $sql = strtolower($sql); - $start_pos = strpos($sql, '('); - $end_pos = strrpos($sql, ')'); - $column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1); - $column_names = explode(',', $column_names); - - if (preg_match("/^create unique/", $sql)) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing table index', __FUNCTION__); - } - - $definition = array(); - $count = count($column_names); - for ($i=0; $i<$count; ++$i) { - $column_name = strtok($column_names[$i], ' '); - $collation = strtok(' '); - $definition['fields'][$column_name] = array( - 'position' => $i+1 - ); - if (!empty($collation)) { - $definition['fields'][$column_name]['sorting'] = - ($collation=='ASC' ? 'ascending' : 'descending'); - } - } - - if (empty($definition['fields'])) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing table index', __FUNCTION__); - } - return $definition; - } - - // }}} - // {{{ getTableConstraintDefinition() - - /** - * Get the stucture of a constraint into an array - * - * @param string $table_name name of table that should be used in method - * @param string $constraint_name name of constraint that should be used in method - * @return mixed data array on success, a MDB2 error on failure - * @access public - */ - function getTableConstraintDefinition($table_name, $constraint_name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - list($schema, $table) = $this->splitTableSchema($table_name); - - $query = "SELECT sql FROM sqlite_master WHERE type='index' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text'); - } else { - $query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text'); - } - $query.= ' AND sql NOT NULL ORDER BY name'; - $constraint_name_mdb2 = $db->getIndexName($constraint_name); - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $qry = sprintf($query, $db->quote(strtolower($constraint_name_mdb2), 'text')); - } else { - $qry = sprintf($query, $db->quote($constraint_name_mdb2, 'text')); - } - $sql = $db->queryOne($qry, 'text'); - if (PEAR::isError($sql) || empty($sql)) { - // fallback to the given $index_name, without transformation - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $qry = sprintf($query, $db->quote(strtolower($constraint_name), 'text')); - } else { - $qry = sprintf($query, $db->quote($constraint_name, 'text')); - } - $sql = $db->queryOne($qry, 'text'); - } - if (PEAR::isError($sql)) { - return $sql; - } - //default values, eventually overridden - $definition = array( - 'primary' => false, - 'unique' => false, - 'foreign' => false, - 'check' => false, - 'fields' => array(), - 'references' => array( - 'table' => '', - 'fields' => array(), - ), - 'onupdate' => '', - 'ondelete' => '', - 'match' => '', - 'deferrable' => false, - 'initiallydeferred' => false, - ); - if (!$sql) { - $query = "SELECT sql FROM sqlite_master WHERE type='table' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text'); - } else { - $query.= 'name='.$db->quote($table, 'text'); - } - $query.= " AND sql NOT NULL ORDER BY name"; - $sql = $db->queryOne($query, 'text'); - if (PEAR::isError($sql)) { - return $sql; - } - if ($constraint_name == 'primary') { - // search in table definition for PRIMARY KEYs - if (preg_match("/\bPRIMARY\s+KEY\b\s*\(([^)]+)/i", $sql, $tmp)) { - $definition['primary'] = true; - $definition['fields'] = array(); - $column_names = explode(',', $tmp[1]); - $colpos = 1; - foreach ($column_names as $column_name) { - $definition['fields'][trim($column_name)] = array( - 'position' => $colpos++ - ); - } - return $definition; - } - if (preg_match("/\"([^\"]+)\"[^\,\"]+\bPRIMARY\s+KEY\b[^\,\)]*/i", $sql, $tmp)) { - $definition['primary'] = true; - $definition['fields'] = array(); - $column_names = explode(',', $tmp[1]); - $colpos = 1; - foreach ($column_names as $column_name) { - $definition['fields'][trim($column_name)] = array( - 'position' => $colpos++ - ); - } - return $definition; - } - } else { - // search in table definition for FOREIGN KEYs - $pattern = "/\bCONSTRAINT\b\s+%s\s+ - \bFOREIGN\s+KEY\b\s*\(([^\)]+)\)\s* - \bREFERENCES\b\s+([^\s]+)\s*\(([^\)]+)\)\s* - (?:\bMATCH\s*([^\s]+))?\s* - (?:\bON\s+UPDATE\s+([^\s,\)]+))?\s* - (?:\bON\s+DELETE\s+([^\s,\)]+))?\s* - /imsx"; - $found_fk = false; - if (preg_match(sprintf($pattern, $constraint_name_mdb2), $sql, $tmp)) { - $found_fk = true; - } elseif (preg_match(sprintf($pattern, $constraint_name), $sql, $tmp)) { - $found_fk = true; - } - if ($found_fk) { - $definition['foreign'] = true; - $definition['match'] = 'SIMPLE'; - $definition['onupdate'] = 'NO ACTION'; - $definition['ondelete'] = 'NO ACTION'; - $definition['references']['table'] = $tmp[2]; - $column_names = explode(',', $tmp[1]); - $colpos = 1; - foreach ($column_names as $column_name) { - $definition['fields'][trim($column_name)] = array( - 'position' => $colpos++ - ); - } - $referenced_cols = explode(',', $tmp[3]); - $colpos = 1; - foreach ($referenced_cols as $column_name) { - $definition['references']['fields'][trim($column_name)] = array( - 'position' => $colpos++ - ); - } - if (isset($tmp[4])) { - $definition['match'] = $tmp[4]; - } - if (isset($tmp[5])) { - $definition['onupdate'] = $tmp[5]; - } - if (isset($tmp[6])) { - $definition['ondelete'] = $tmp[6]; - } - return $definition; - } - } - $sql = false; - } - if (!$sql) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - $constraint_name . ' is not an existing table constraint', __FUNCTION__); - } - - $sql = strtolower($sql); - $start_pos = strpos($sql, '('); - $end_pos = strrpos($sql, ')'); - $column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1); - $column_names = explode(',', $column_names); - - if (!preg_match("/^create unique/", $sql)) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - $constraint_name . ' is not an existing table constraint', __FUNCTION__); - } - - $definition['unique'] = true; - $count = count($column_names); - for ($i=0; $i<$count; ++$i) { - $column_name = strtok($column_names[$i], " "); - $collation = strtok(" "); - $definition['fields'][$column_name] = array( - 'position' => $i+1 - ); - if (!empty($collation)) { - $definition['fields'][$column_name]['sorting'] = - ($collation=='ASC' ? 'ascending' : 'descending'); - } - } - - if (empty($definition['fields'])) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - $constraint_name . ' is not an existing table constraint', __FUNCTION__); - } - return $definition; - } - - // }}} - // {{{ getTriggerDefinition() - - /** - * Get the structure of a trigger into an array - * - * EXPERIMENTAL - * - * WARNING: this function is experimental and may change the returned value - * at any time until labelled as non-experimental - * - * @param string $trigger name of trigger that should be used in method - * @return mixed data array on success, a MDB2 error on failure - * @access public - */ - function getTriggerDefinition($trigger) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name as trigger_name, - tbl_name AS table_name, - sql AS trigger_body, - NULL AS trigger_type, - NULL AS trigger_event, - NULL AS trigger_comment, - 1 AS trigger_enabled - FROM sqlite_master - WHERE type='trigger'"; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= ' AND LOWER(name)='.$db->quote(strtolower($trigger), 'text'); - } else { - $query.= ' AND name='.$db->quote($trigger, 'text'); - } - $types = array( - 'trigger_name' => 'text', - 'table_name' => 'text', - 'trigger_body' => 'text', - 'trigger_type' => 'text', - 'trigger_event' => 'text', - 'trigger_comment' => 'text', - 'trigger_enabled' => 'boolean', - ); - $def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC); - if (PEAR::isError($def)) { - return $def; - } - if (empty($def)) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing trigger', __FUNCTION__); - } - if (preg_match("/^create\s+(?:temp|temporary)?trigger\s+(?:if\s+not\s+exists\s+)?.*(before|after)?\s+(insert|update|delete)/Uims", $def['trigger_body'], $tmp)) { - $def['trigger_type'] = strtoupper($tmp[1]); - $def['trigger_event'] = strtoupper($tmp[2]); - } - return $def; - } - - // }}} - // {{{ tableInfo() - - /** - * Returns information about a table - * - * @param string $result a string containing the name of a table - * @param int $mode a valid tableInfo mode - * - * @return array an associative array with the information requested. - * A MDB2_Error object on failure. - * - * @see MDB2_Driver_Common::tableInfo() - * @since Method available since Release 1.7.0 - */ - function tableInfo($result, $mode = null) - { - if (is_string($result)) { - return parent::tableInfo($result, $mode); - } - - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - return $db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null, - 'This DBMS can not obtain tableInfo from result sets', __FUNCTION__); - } -} diff --git a/lib/MDB2/Driver/sqlite3.php b/lib/MDB2/Driver/sqlite3.php deleted file mode 100644 index 8f057cfb6e8..00000000000 --- a/lib/MDB2/Driver/sqlite3.php +++ /dev/null @@ -1,1332 +0,0 @@ -. - * - */ - -/** - * MDB2 SQLite3 driver - * - * @package MDB2 - * @category Database - * @author Lukas Smith - */ -class MDB2_Driver_sqlite3 extends MDB2_Driver_Common -{ - // {{{ properties - public $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => false); - - public $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"'); - - public $_lasterror = ''; - - public $fix_assoc_fields_names = false; - - // }}} - // {{{ constructor - - /** - * Constructor - */ - function __construct() - { - parent::__construct(); - - $this->phptype = 'sqlite3'; - $this->dbsyntax = 'sqlite'; - - $this->supported['sequences'] = 'emulated'; - $this->supported['indexes'] = true; - $this->supported['affected_rows'] = true; - $this->supported['summary_functions'] = true; - $this->supported['order_by_text'] = true; - $this->supported['current_id'] = 'emulated'; - $this->supported['limit_queries'] = true; - $this->supported['LOBs'] = true; - $this->supported['replace'] = true; - $this->supported['transactions'] = false; - $this->supported['savepoints'] = false; - $this->supported['sub_selects'] = true; - $this->supported['triggers'] = true; - $this->supported['auto_increment'] = true; - $this->supported['primary_key'] = false; // requires alter table implementation - $this->supported['result_introspection'] = false; // not implemented - $this->supported['prepared_statements'] = true; - $this->supported['identifier_quoting'] = true; - $this->supported['pattern_escaping'] = false; - $this->supported['new_link'] = false; - - $this->options['DBA_username'] = false; - $this->options['DBA_password'] = false; - $this->options['base_transaction_name'] = '___php_MDB2_sqlite_auto_commit_off'; - $this->options['fixed_float'] = 0; - $this->options['database_path'] = ''; - $this->options['database_extension'] = ''; - $this->options['server_version'] = ''; - $this->options['max_identifiers_length'] = 128; //no real limit - } - - // }}} - // {{{ errorInfo() - - /** - * This method is used to collect information about an error - * - * @param integer $error - * @return array - * @access public - */ - function errorInfo($error = null) - { - $native_code = null; - if ($this->connection) { - $native_code = $this->connection->lastErrorCode(); - } - $native_msg = html_entity_decode($this->_lasterror); - - // PHP 5.2+ prepends the function name to $php_errormsg, so we need - // this hack to work around it, per bug #9599. - $native_msg = preg_replace('/^sqlite[a-z_]+\(\)[^:]*: /', '', $native_msg); - - if (is_null($error)) { - static $error_regexps; - if (empty($error_regexps)) { - $error_regexps = array( - '/^no such table:/' => MDB2_ERROR_NOSUCHTABLE, - '/^no such index:/' => MDB2_ERROR_NOT_FOUND, - '/^(table|index) .* already exists$/' => MDB2_ERROR_ALREADY_EXISTS, - '/PRIMARY KEY must be unique/i' => MDB2_ERROR_CONSTRAINT, - '/is not unique/' => MDB2_ERROR_CONSTRAINT, - '/columns .* are not unique/i' => MDB2_ERROR_CONSTRAINT, - '/uniqueness constraint failed/' => MDB2_ERROR_CONSTRAINT, - '/may not be NULL/' => MDB2_ERROR_CONSTRAINT_NOT_NULL, - '/^no such column:/' => MDB2_ERROR_NOSUCHFIELD, - '/no column named/' => MDB2_ERROR_NOSUCHFIELD, - '/column not present in both tables/i' => MDB2_ERROR_NOSUCHFIELD, - '/^near ".*": syntax error$/' => MDB2_ERROR_SYNTAX, - '/[0-9]+ values for [0-9]+ columns/i' => MDB2_ERROR_VALUE_COUNT_ON_ROW, - ); - } - foreach ($error_regexps as $regexp => $code) { - if (preg_match($regexp, $native_msg)) { - $error = $code; - break; - } - } - } - return array($error, $native_code, $native_msg); - } - - // }}} - // {{{ escape() - - /** - * Quotes a string so it can be safely used in a query. It will quote - * the text so it can safely be used within a query. - * - * @param string the input string to quote - * @param bool escape wildcards - * - * @return string quoted string - * - * @access public - */ - public function escape($text, $escape_wildcards = false) - { - if($this->connection) { - return $this->connection->escapeString($text); - }else{ - return str_replace("'", "''", $text);//TODO; more - } - } - - // }}} - // {{{ beginTransaction() - - /** - * Start a transaction or set a savepoint. - * - * @param string name of a savepoint to set - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - */ - function beginTransaction($savepoint = null) - { - $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); - if (!is_null($savepoint)) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'savepoints are not supported', __FUNCTION__); - } elseif ($this->in_transaction) { - return MDB2_OK; //nothing to do - } - if (!$this->destructor_registered && $this->opened_persistent) { - $this->destructor_registered = true; - register_shutdown_function('MDB2_closeOpenTransactions'); - } - $query = 'BEGIN TRANSACTION '.$this->options['base_transaction_name']; - $result =$this->_doQuery($query, true); - if (PEAR::isError($result)) { - return $result; - } - $this->in_transaction = true; - return MDB2_OK; - } - - // }}} - // {{{ commit() - - /** - * Commit the database changes done during a transaction that is in - * progress or release a savepoint. This function may only be called when - * auto-committing is disabled, otherwise it will fail. Therefore, a new - * transaction is implicitly started after committing the pending changes. - * - * @param string name of a savepoint to release - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - */ - function commit($savepoint = null) - { - $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); - if (!$this->in_transaction) { - return $this->raiseError(MDB2_ERROR_INVALID, null, null, - 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__); - } - if (!is_null($savepoint)) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'savepoints are not supported', __FUNCTION__); - } - - $query = 'COMMIT TRANSACTION '.$this->options['base_transaction_name']; - $result =$this->_doQuery($query, true); - if (PEAR::isError($result)) { - return $result; - } - $this->in_transaction = false; - return MDB2_OK; - } - - // }}} - // {{{ - - /** - * Cancel any database changes done during a transaction or since a specific - * savepoint that is in progress. This function may only be called when - * auto-committing is disabled, otherwise it will fail. Therefore, a new - * transaction is implicitly started after canceling the pending changes. - * - * @param string name of a savepoint to rollback to - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - */ - function rollback($savepoint = null) - { - $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); - if (!$this->in_transaction) { - return $this->raiseError(MDB2_ERROR_INVALID, null, null, - 'rollback cannot be done changes are auto committed', __FUNCTION__); - } - if (!is_null($savepoint)) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'savepoints are not supported', __FUNCTION__); - } - - $query = 'ROLLBACK TRANSACTION '.$this->options['base_transaction_name']; - $result =$this->_doQuery($query, true); - if (PEAR::isError($result)) { - return $result; - } - $this->in_transaction = false; - return MDB2_OK; - } - - // }}} - // {{{ function setTransactionIsolation() - - /** - * Set the transacton isolation level. - * - * @param string standard isolation level - * READ UNCOMMITTED (allows dirty reads) - * READ COMMITTED (prevents dirty reads) - * REPEATABLE READ (prevents nonrepeatable reads) - * SERIALIZABLE (prevents phantom reads) - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - * @since 2.1.1 - */ - function setTransactionIsolation($isolation, $options=array()) - { - $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true)); - switch ($isolation) { - case 'READ UNCOMMITTED': - $isolation = 0; - break; - case 'READ COMMITTED': - case 'REPEATABLE READ': - case 'SERIALIZABLE': - $isolation = 1; - break; - default: - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'isolation level is not supported: '.$isolation, __FUNCTION__); - } - - $query = "PRAGMA read_uncommitted=$isolation"; - return $this->_doQuery($query, true); - } - - // }}} - // {{{ getDatabaseFile() - - /** - * Builds the string with path+dbname+extension - * - * @return string full database path+file - * @access protected - */ - function _getDatabaseFile($database_name) - { - if ($database_name === '' || $database_name === ':memory:') { - return $database_name; - } - return $this->options['database_path'].$database_name.$this->options['database_extension']; - } - - // }}} - // {{{ connect() - - /** - * Connect to the database - * - * @return true on success, MDB2 Error Object on failure - **/ - function connect() - { - if($this->connection instanceof SQLite3) { - return MDB2_OK; - } - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); - $database_file = $this->_getDatabaseFile($this->database_name); - if (is_resource($this->connection)) { - //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0 - if (MDB2::areEquals($this->connected_dsn, $this->dsn) - && $this->connected_database_name == $database_file - && $this->opened_persistent == $this->options['persistent'] - ) { - return MDB2_OK; - } - $this->disconnect(false); - } - - if (!PEAR::loadExtension($this->phptype)) { - return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__); - } - - if (empty($this->database_name)) { - return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, - 'unable to establish a connection', __FUNCTION__); - } - - if ($database_file !== ':memory:') { - if(!strpos($database_file, '.db')) { - $database_file="$datadir/$database_file.db"; - } - if (!file_exists($database_file)) { - if (!touch($database_file)) { - return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'Could not create database file', __FUNCTION__); - } - if (!isset($this->dsn['mode']) - || !is_numeric($this->dsn['mode']) - ) { - $mode = 0644; - } else { - $mode = octdec($this->dsn['mode']); - } - if (!chmod($database_file, $mode)) { - return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'Could not be chmodded database file', __FUNCTION__); - } - if (!file_exists($database_file)) { - return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'Could not be found database file', __FUNCTION__); - } - } - if (!is_file($database_file)) { - return $this->raiseError(MDB2_ERROR_INVALID, null, null, - 'Database is a directory name', __FUNCTION__); - } - if (!is_readable($database_file)) { - return $this->raiseError(MDB2_ERROR_ACCESS_VIOLATION, null, null, - 'Could not read database file', __FUNCTION__); - } - } - - $php_errormsg = ''; - $this->connection = new SQLite3($database_file); - if(is_callable(array($this->connection, 'busyTimeout'))) {//busy timout is only available in php>=5.3 - $this->connection->busyTimeout(100); - } - $this->_lasterror = $this->connection->lastErrorMsg(); - if (!$this->connection) { - return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, - 'unable to establish a connection', __FUNCTION__); - } - - if ($this->fix_assoc_fields_names || - $this->options['portability'] & MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES) { - $this->connection->exec("PRAGMA short_column_names = 1"); - $this->fix_assoc_fields_names = true; - } - - $this->connected_dsn = $this->dsn; - $this->connected_database_name = $database_file; - $this->opened_persistent = $this->getoption('persistent'); - $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype; - - return MDB2_OK; - } - - // }}} - // {{{ databaseExists() - - /** - * check if given database name is exists? - * - * @param string $name name of the database that should be checked - * - * @return mixed true/false on success, a MDB2 error on failure - * @access public - */ - function databaseExists($name) - { - $database_file = $this->_getDatabaseFile($name); - $result = file_exists($database_file); - return $result; - } - - // }}} - // {{{ disconnect() - - /** - * Log out and disconnect from the database. - * - * @param boolean $force if the disconnect should be forced even if the - * connection is opened persistently - * @return mixed true on success, false if not connected and error - * object on error - * @access public - */ - function disconnect($force = true) - { - if ($this->connection instanceof SQLite3) { - if ($this->in_transaction) { - $dsn = $this->dsn; - $database_name = $this->database_name; - $persistent = $this->options['persistent']; - $this->dsn = $this->connected_dsn; - $this->database_name = $this->connected_database_name; - $this->options['persistent'] = $this->opened_persistent; - $this->rollback(); - $this->dsn = $dsn; - $this->database_name = $database_name; - $this->options['persistent'] = $persistent; - } - - if (!$this->opened_persistent || $force) { - $this->connection->close(); - } - } else { - return false; - } - return parent::disconnect($force); - } - - // }}} - // {{{ _doQuery() - - /** - * Execute a query - * @param string $query query - * @param boolean $is_manip if the query is a manipulation query - * @param resource $connection - * @param string $database_name - * @return result or error object - * @access protected - */ - function _doQuery($query, $is_manip = false, $connection = null, $database_name = null) - { - $this->last_query = $query; - $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre')); - if ($result) { - if (PEAR::isError($result)) { - return $result; - } - $query = $result; - } - if ($this->options['disable_query']) { - $result = $is_manip ? 0 : null; - return $result; - } - $result=$this->connection->query($query.';'); - $this->_lasterror = $this->connection->lastErrorMsg(); - - if (!$result) { - $err =$this->raiseError(null, null, null, - 'Could not execute statement', __FUNCTION__); - return $err; - } - - $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result)); - return $result; - } - - // }}} - // {{{ _affectedRows() - - /** - * Returns the number of rows affected - * - * @param resource $result - * @param resource $connection - * @return mixed MDB2 Error Object or the number of rows affected - * @access private - */ - function _affectedRows($connection, $result = null) - { - return $this->connection->changes(); - } - - // }}} - // {{{ _modifyQuery() - - /** - * Changes a query string for various DBMS specific reasons - * - * @param string $query query to modify - * @param boolean $is_manip if it is a DML query - * @param integer $limit limit the number of rows - * @param integer $offset start reading from given offset - * @return string modified query - * @access protected - */ - function _modifyQuery($query, $is_manip, $limit, $offset) - { - if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT) { - if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { - $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', - 'DELETE FROM \1 WHERE 1=1', $query); - } - } - if ($limit > 0 - && !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query) - ) { - $query = rtrim($query); - if (substr($query, -1) == ';') { - $query = substr($query, 0, -1); - } - if ($is_manip) { - $query.= " LIMIT $limit"; - } else { - $query.= " LIMIT $offset,$limit"; - } - } - return $query; - } - - // }}} - // {{{ getServerVersion() - - /** - * return version information about the server - * - * @param bool $native determines if the raw version string should be returned - * @return mixed array/string with version information or MDB2 error object - * @access public - */ - function getServerVersion($native = false) - { - $server_info = false; - if ($this->connected_server_info) { - $server_info = $this->connected_server_info; - } elseif ($this->options['server_version']) { - $server_info = $this->options['server_version']; - } elseif (function_exists('sqlite_libversion')) { - $server_info = @sqlite_libversion(); - } - if (!$server_info) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'Requires either the "server_version" option or the sqlite_libversion() function', __FUNCTION__); - } - // cache server_info - $this->connected_server_info = $server_info; - if (!$native) { - $tmp = explode('.', $server_info, 3); - $server_info = array( - 'major' => isset($tmp[0]) ? $tmp[0] : null, - 'minor' => isset($tmp[1]) ? $tmp[1] : null, - 'patch' => isset($tmp[2]) ? $tmp[2] : null, - 'extra' => null, - 'native' => $server_info, - ); - } - return $server_info; - } - - // }}} - // {{{ replace() - - /** - * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT - * query, except that if there is already a row in the table with the same - * key field values, the old row is deleted before the new row is inserted. - * - * The REPLACE type of query does not make part of the SQL standards. Since - * practically only SQLite implements it natively, this type of query is - * emulated through this method for other DBMS using standard types of - * queries inside a transaction to assure the atomicity of the operation. - * - * @access public - * - * @param string $table name of the table on which the REPLACE query will - * be executed. - * @param array $fields associative array that describes the fields and the - * values that will be inserted or updated in the specified table. The - * indexes of the array are the names of all the fields of the table. The - * values of the array are also associative arrays that describe the - * values and other properties of the table fields. - * - * Here follows a list of field properties that need to be specified: - * - * value: - * Value to be assigned to the specified field. This value may be - * of specified in database independent type format as this - * function can perform the necessary datatype conversions. - * - * Default: - * this property is required unless the Null property - * is set to 1. - * - * type - * Name of the type of the field. Currently, all types Metabase - * are supported except for clob and blob. - * - * Default: no type conversion - * - * null - * Boolean property that indicates that the value for this field - * should be set to null. - * - * The default value for fields missing in INSERT queries may be - * specified the definition of a table. Often, the default value - * is already null, but since the REPLACE may be emulated using - * an UPDATE query, make sure that all fields of the table are - * listed in this function argument array. - * - * Default: 0 - * - * key - * Boolean property that indicates that this field should be - * handled as a primary key or at least as part of the compound - * unique index of the table that will determine the row that will - * updated if it exists or inserted a new row otherwise. - * - * This function will fail if no key field is specified or if the - * value of a key field is set to null because fields that are - * part of unique index they may not be null. - * - * Default: 0 - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - */ - function replace($table, $fields) - { - $count = count($fields); - $query = $values = ''; - $keys = $colnum = 0; - for (reset($fields); $colnum < $count; next($fields), $colnum++) { - $name = key($fields); - if ($colnum > 0) { - $query .= ','; - $values.= ','; - } - $query.= $this->quoteIdentifier($name, true); - if (isset($fields[$name]['null']) && $fields[$name]['null']) { - $value = 'NULL'; - } else { - $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null; - $value = $this->quote($fields[$name]['value'], $type); - if (PEAR::isError($value)) { - return $value; - } - } - $values.= $value; - if (isset($fields[$name]['key']) && $fields[$name]['key']) { - if ($value === 'NULL') { - return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, - 'key value '.$name.' may not be NULL', __FUNCTION__); - } - $keys++; - } - } - if ($keys == 0) { - return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, - 'not specified which fields are keys', __FUNCTION__); - } - - $connection = $this->getConnection(); - if (PEAR::isError($connection)) { - return $connection; - } - - $table = $this->quoteIdentifier($table, true); - $query = "REPLACE INTO $table ($query) VALUES ($values)"; - $result =$this->_doQuery($query, true, $connection); - if (PEAR::isError($result)) { - return $result; - } - return $this->_affectedRows($connection, $result); - } - - // }}} - // {{{ nextID() - - /** - * Returns the next free id of a sequence - * - * @param string $seq_name name of the sequence - * @param boolean $ondemand when true the sequence is - * automatic created, if it - * not exists - * - * @return mixed MDB2 Error Object or id - * @access public - */ - function nextID($seq_name, $ondemand = true) - { - $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true); - $seqcol_name = $this->options['seqcol_name']; - $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL)"; - $this->pushErrorHandling(PEAR_ERROR_RETURN); - $this->expectError(MDB2_ERROR_NOSUCHTABLE); - $result =$this->_doQuery($query, true); - $this->popExpect(); - $this->popErrorHandling(); - if (PEAR::isError($result)) { - if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) { - $this->loadModule('Manager', null, true); - $result = $this->manager->createSequence($seq_name); - if (PEAR::isError($result)) { - return $this->raiseError($result, null, null, - 'on demand sequence '.$seq_name.' could not be created', __FUNCTION__); - } else { - return $this->nextID($seq_name, false); - } - } - return $result; - } - $value = $this->lastInsertID(); - if (is_numeric($value)) { - $query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value"; - $result =$this->_doQuery($query, true); - if (PEAR::isError($result)) { - $this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name; - } - } - return $value; - } - - // }}} - // {{{ lastInsertID() - - /** - * Returns the autoincrement ID if supported or $id or fetches the current - * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field) - * - * @param string $table name of the table into which a new row was inserted - * @param string $field name of the field into which a new row was inserted - * @return mixed MDB2 Error Object or id - * @access public - */ - function lastInsertID($table = null, $field = null) - { - return $this->connection->lastInsertRowID(); - } - - // }}} - // {{{ currID() - - /** - * Returns the current id of a sequence - * - * @param string $seq_name name of the sequence - * @return mixed MDB2 Error Object or id - * @access public - */ - function currID($seq_name) - { - $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true); - $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true); - $query = "SELECT MAX($seqcol_name) FROM $sequence_name"; - return $this->queryOne($query, 'integer'); - } - - /** - * Prepares a query for multiple execution with execute(). - * With some database backends, this is emulated. - * prepare() requires a generic query as string like - * 'INSERT INTO numbers VALUES(?,?)' or - * 'INSERT INTO numbers VALUES(:foo,:bar)'. - * The ? and :name and are placeholders which can be set using - * bindParam() and the query can be sent off using the execute() method. - * The allowed format for :name can be set with the 'bindname_format' option. - * - * @param string $query the query to prepare - * @param mixed $types array that contains the types of the placeholders - * @param mixed $result_types array that contains the types of the columns in - * the result set or MDB2_PREPARE_RESULT, if set to - * MDB2_PREPARE_MANIP the query is handled as a manipulation query - * @param mixed $lobs key (field) value (parameter) pair for all lob placeholders - * @return mixed resource handle for the prepared query on success, a MDB2 - * error on failure - * @access public - * @see bindParam, execute - */ - function prepare($query, $types = null, $result_types = null, $lobs = array()) - { - if ($this->options['emulate_prepared'] - || $this->supported['prepared_statements'] !== true - ) { - $obj =& parent::prepare($query, $types, $result_types, $lobs); - return $obj; - } - $this->last_query = $query; - $is_manip = ($result_types === MDB2_PREPARE_MANIP); - $offset = $this->offset; - $limit = $this->limit; - $this->offset = $this->limit = 0; - $query = $this->_modifyQuery($query, $is_manip, $limit, $offset); - $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre')); - if ($result) { - if (PEAR::isError($result)) { - return $result; - } - $query = $result; - } - $placeholder_type_guess = $placeholder_type = null; - $question = '?'; - $colon = ':'; - $positions = array(); - $position = 0; - while ($position < strlen($query)) { - $q_position = strpos($query, $question, $position); - $c_position = strpos($query, $colon, $position); - if ($q_position && $c_position) { - $p_position = min($q_position, $c_position); - } elseif ($q_position) { - $p_position = $q_position; - } elseif ($c_position) { - $p_position = $c_position; - } else { - break; - } - if (is_null($placeholder_type)) { - $placeholder_type_guess = $query[$p_position]; - } - - $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position); - if (PEAR::isError($new_pos)) { - return $new_pos; - } - if ($new_pos != $position) { - $position = $new_pos; - continue; //evaluate again starting from the new position - } - - - if ($query[$position] == $placeholder_type_guess) { - if (is_null($placeholder_type)) { - $placeholder_type = $query[$p_position]; - $question = $colon = $placeholder_type; - } - if ($placeholder_type == ':') { - $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s'; - $parameter = preg_replace($regexp, '\\1', $query); - if ($parameter === '') { - $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null, - 'named parameter name must match "bindname_format" option', __FUNCTION__); - return $err; - } - $positions[$p_position] = $parameter; - $query = substr_replace($query, '?', $position, strlen($parameter)+1); - } else { - $positions[$p_position] = count($positions); - } - $position = $p_position + 1; - } else { - $position = $p_position; - } - } - $connection = $this->getConnection(); - if (PEAR::isError($connection)) { - return $connection; - } - $statement =$this->connection->prepare($query); - if (!$statement) { - return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'unable to prepare statement: '.$query); - } - - $class_name = 'MDB2_Statement_'.$this->phptype; - $obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset); - $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj)); - return $obj; - } -} - -/** - * MDB2 SQLite result driver - * - * @package MDB2 - * @category Database - * @author Lukas Smith - */ -class MDB2_Result_sqlite3 extends MDB2_Result_Common -{ - // }}} - // {{{ fetchRow() - - /** - * Fetch a row and insert the data into an existing array. - * - * @param int $fetchmode how the array data should be indexed - * @param int $rownum number of the row where the data can be found - * @return int data array on success, a MDB2 error on failure - * @access public - */ - function fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) - { - if (!is_null($rownum)) { - $seek = $this->seek($rownum); - if (PEAR::isError($seek)) { - return $seek; - } - } - if ($fetchmode == MDB2_FETCHMODE_DEFAULT) { - $fetchmode = $this->db->fetchmode; - } - if ($fetchmode & MDB2_FETCHMODE_ASSOC) { - //$row = @sqlite_fetch_array($this->result, SQLITE_ASSOC); - $row=$this->result->fetchArray(SQLITE3_ASSOC); - if (is_array($row) - && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE - ) { - $row = array_change_key_case($row, $this->db->options['field_case']); - } - } else { - $row=$this->result->fetchArray(SQLITE3_NUM); - } - if (!$row) { - if ($this->result === false) { - $err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, - 'resultset has already been freed', __FUNCTION__); - return $err; - } - $null = null; - return $null; - } - $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL; - $rtrim = false; - if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) { - if (empty($this->types)) { - $mode += MDB2_PORTABILITY_RTRIM; - } else { - $rtrim = true; - } - } - if ($mode) { - $this->db->_fixResultArrayValues($row, $mode); - } - if (!empty($this->types)) { - $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim); - } - if (!empty($this->values)) { - $this->_assignBindColumns($row); - } - if ($fetchmode === MDB2_FETCHMODE_OBJECT) { - $object_class = $this->db->options['fetch_class']; - if ($object_class == 'stdClass') { - $row = (object) $row; - } else { - $row = new $object_class($row); - } - } - ++$this->rownum; - return $row; - } - - // }}} - // {{{ _getColumnNames() - - /** - * Retrieve the names of columns returned by the DBMS in a query result. - * - * @return mixed Array variable that holds the names of columns as keys - * or an MDB2 error on failure. - * Some DBMS may not return any columns when the result set - * does not contain any rows. - * @access private - */ - function _getColumnNames() - { - $columns = array(); - $numcols = $this->numCols(); - if (PEAR::isError($numcols)) { - return $numcols; - } - for ($column = 0; $column < $numcols; $column++) { - $column_name = $this->result->getColumnName($column); - $columns[$column_name] = $column; - } - if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $columns = array_change_key_case($columns, $this->db->options['field_case']); - } - return $columns; - } - - // }}} - // {{{ numCols() - - /** - * Count the number of columns returned by the DBMS in a query result. - * - * @access public - * @return mixed integer value with the number of columns, a MDB2 error - * on failure - */ - function numCols() - { - $this->result->numColumns(); - } -} - -/** - * MDB2 SQLite buffered result driver - * - * @package MDB2 - * @category Database - * @author Lukas Smith - */ -class MDB2_BufferedResult_sqlite3 extends MDB2_Result_sqlite3 -{ - // {{{ seek() - - /** - * Seek to a specific row in a result set - * - * @param int $rownum number of the row where the data can be found - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function seek($rownum = 0) - { - $this->result->reset(); - for($i=0;$i<$rownum;$i++) { - $this->result->fetchArray(); - } - $this->rownum = $rownum - 1; - return MDB2_OK; - } - - // }}} - // {{{ valid() - - /** - * Check if the end of the result set has been reached - * - * @return mixed true or false on sucess, a MDB2 error on failure - * @access public - */ - function valid() - { - $numrows = $this->numRows(); - if (PEAR::isError($numrows)) { - return $numrows; - } - return $this->rownum < ($numrows - 1); - } - - // }}} - // {{{ numRows() - - /** - * Returns the number of rows in a result object - * - * @return mixed MDB2 Error Object or the number of rows - * @access public - */ - function numRows() - { - $rows = 0; - $this->result->reset(); - while($this->result->fetchArray()) { - $rows++; - } - $this->result->reset(); - return $rows; - } -} - -/** - * MDB2 SQLite statement driver - * - * @package MDB2 - * @category Database - * @author Lukas Smith - */ -class MDB2_Statement_sqlite3 extends MDB2_Statement_Common -{ - // }}} - // {{{ function bindValue($parameter, &$value, $type = null) - - private function getParamType($type) { - switch(strtolower($type)) { - case 'text': - return SQLITE3_TEXT; - case 'boolean': - case 'integer': - return SQLITE3_INTEGER; - case 'float': - return SQLITE3_FLOAT; - case 'blob': - return SQLITE3_BLOB; - } - } - /** - * Set the value of a parameter of a prepared query. - * - * @param int the order number of the parameter in the query - * statement. The order number of the first parameter is 1. - * @param mixed value that is meant to be assigned to specified - * parameter. The type of the value depends on the $type argument. - * @param string specifies the type of the field - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - */ - function bindValue($parameter, $value, $type = null) { - if($type) { - $type=$this->getParamType($type); - $this->statement->bindValue($parameter, $value, $type); - }else{ - $this->statement->bindValue($parameter, $value); - } - return MDB2_OK; - } - - /** - * Bind a variable to a parameter of a prepared query. - * - * @param int the order number of the parameter in the query - * statement. The order number of the first parameter is 1. - * @param mixed variable that is meant to be bound to specified - * parameter. The type of the value depends on the $type argument. - * @param string specifies the type of the field - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - */ - function bindParam($parameter, &$value, $type = null) { - if($type) { - $type=$this->getParamType($type); - $this->statement->bindParam($parameter, $value, $type); - }else{ - $this->statement->bindParam($parameter, $value); - } - return MDB2_OK; - } - - /** - * Release resources allocated for the specified prepared query. - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function free() - { - $this->statement->close(); - } - - /** - * Execute a prepared query statement helper method. - * - * @param mixed $result_class string which specifies which result class to use - * @param mixed $result_wrap_class string which specifies which class to wrap results in - * - * @return mixed MDB2_Result or integer (affected rows) on success, - * a MDB2 error on failure - * @access private - */ - function _execute($result_class = true, $result_wrap_class = false) { - if (is_null($this->statement)) { - $result =& parent::_execute($result_class, $result_wrap_class); - return $result; - } - $this->db->last_query = $this->query; - $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'pre', 'parameters' => $this->values)); - if ($this->db->getOption('disable_query')) { - $result = $this->is_manip ? 0 : null; - return $result; - } - - $connection = $this->db->getConnection(); - if (PEAR::isError($connection)) { - return $connection; - } - - $result = $this->statement->execute(); - if ($result==false) { - $err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, - 'cant execute statement', __FUNCTION__); - } - - if ($this->is_manip) { - $affected_rows = $this->db->_affectedRows($connection, $result); - return $affected_rows; - } - - $result = $this->db->_wrapResult($result, $this->result_types, - $result_class, $result_wrap_class, $this->limit, $this->offset); - $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result)); - return $result; - } - - /** - * Set the values of multiple a parameter of a prepared query in bulk. - * - * @param array specifies all necessary information - * for bindValue() the array elements must use keys corresponding to - * the number of the position of the parameter. - * @param array specifies the types of the fields - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - * @see bindParam() - */ - function bindValueArray($values, $types = null) - { - $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null); - $parameters = array_keys($values); - foreach ($parameters as $key => $parameter) { - $this->db->pushErrorHandling(PEAR_ERROR_RETURN); - $this->db->expectError(MDB2_ERROR_NOT_FOUND); - $err = $this->bindValue($parameter+1, $values[$parameter], $types[$key]); - $this->db->popExpect(); - $this->db->popErrorHandling(); - if (PEAR::isError($err)) { - if ($err->getCode() == MDB2_ERROR_NOT_FOUND) { - //ignore (extra value for missing placeholder) - continue; - } - return $err; - } - } - return MDB2_OK; - } - // }}} - // {{{ function bindParamArray(&$values, $types = null) - - /** - * Bind the variables of multiple a parameter of a prepared query in bulk. - * - * @param array specifies all necessary information - * for bindParam() the array elements must use keys corresponding to - * the number of the position of the parameter. - * @param array specifies the types of the fields - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - * @see bindParam() - */ - function bindParamArray(&$values, $types = null) - { - $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null); - $parameters = array_keys($values); - foreach ($parameters as $key => $parameter) { - $err = $this->bindParam($parameter+1, $values[$parameter], $types[$key]); - if (PEAR::isError($err)) { - return $err; - } - } - return MDB2_OK; - } - - // }}} - // {{{ function &execute($values = null, $result_class = true, $result_wrap_class = false) - - /** - * Execute a prepared query statement. - * - * @param array specifies all necessary information - * for bindParam() the array elements must use keys corresponding - * to the number of the position of the parameter. - * @param mixed specifies which result class to use - * @param mixed specifies which class to wrap results in - * - * @return mixed MDB2_Result or integer (affected rows) on success, - * a MDB2 error on failure - * @access public - */ - function execute($values = null, $result_class = true, $result_wrap_class = false) - { - if (is_null($this->positions)) { - return $this->db->raiseError(MDB2_ERROR, null, null, - 'Prepared statement has already been freed', __FUNCTION__); - } - $values = (array)$values; - if (!empty($values)) { - if(count($this->types)) { - $types=$this->types; - }else{ - $types=null; - } - $err = $this->bindValueArray($values, $types); - if (PEAR::isError($err)) { - return $this->db->raiseError(MDB2_ERROR, null, null, - 'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__); - } - } - $result =$this->_execute($result_class, $result_wrap_class); - return $result; - } - - function __destruct() { - $this->free(); - } -} From 34e4e490b863f0628868fd50eed98573f1673f6f Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sun, 10 Mar 2013 11:40:08 +0100 Subject: [PATCH 11/47] Fix mssql connection parameters and correct driver for pgsql --- lib/db.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/db.php b/lib/db.php index 4a511908e5a..4a956674d12 100644 --- a/lib/db.php +++ b/lib/db.php @@ -147,7 +147,7 @@ class OC_DB { 'host' => $host, 'port' => $port, 'dbname' => $name, - 'driver' => 'pdo_mysql', + 'driver' => 'pdo_pgsql', ); break; case 'oci': @@ -162,13 +162,14 @@ class OC_DB { ); break; case 'mssql': - $dsn = array( - 'phptype' => 'sqlsrv', - 'username' => $user, - 'password' => $pass, - 'hostspec' => $host, - 'database' => $name - ); + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'host' => $host, + 'port' => $port, + 'dbname' => $name, + 'driver' => 'pdo_sqlsrv', + ); break; default: return false; From 4a02dacf16b880e404994084584c59bc99bf6e0b Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sun, 17 Mar 2013 13:49:54 +0100 Subject: [PATCH 12/47] Point 3rdparty submodule to the doctrine branch --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 63cb2847921..93f76b4392b 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 63cb2847921d668c2b48b572872cfddbcf41bea4 +Subproject commit 93f76b4392b9e6b60fe0d052793741f348cb2536 From e9213a67110d87c8127d0609859fe31726afbdbd Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sun, 17 Mar 2013 14:15:36 +0100 Subject: [PATCH 13/47] Change var_dumps to exceptions --- lib/db/mdb2schemareader.php | 19 +++++++------------ lib/db/schema.php | 2 -- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php index 05b9bd21289..6408c27e916 100644 --- a/lib/db/mdb2schemareader.php +++ b/lib/db/mdb2schemareader.php @@ -29,7 +29,7 @@ class OC_DB_MDB2SchemaReader { self::loadTable($schema, $child); break; default: - var_dump($child->getName()); + throw new DomainException('Unknown element: '.$child->getName()); } } @@ -52,7 +52,7 @@ class OC_DB_MDB2SchemaReader { self::loadDeclaration($table, $child); break; default: - var_dump($child->getName()); + throw new DomainException('Unknown element: '.$child->getName()); } } @@ -68,7 +68,7 @@ class OC_DB_MDB2SchemaReader { self::loadIndex($table, $child); break; default: - var_dump($child->getName()); + throw new DomainException('Unknown element: '.$child->getName()); } } @@ -118,16 +118,11 @@ class OC_DB_MDB2SchemaReader { $options['default'] = $default; break; default: - var_dump($child->getName()); + throw new DomainException('Unknown element: '.$child->getName()); } } if (isset($name) && isset($type)) { - if ($name == 'x') { - var_dump($name, $type, $options); - echo '
';
-			debug_print_backtrace();
-		}
 			if (empty($options['default'])) {
 				if ($type == 'integer') {
 					if (empty($options['default'])) {
@@ -187,13 +182,13 @@ class OC_DB_MDB2SchemaReader {
 							case 'sorting':
 								break;
 							default:
-								var_dump($field->getName());
+								throw new DomainException('Unknown element: '.$field->getName());
 
 						}
 					}
 					break;
 				default:
-					var_dump($child->getName());
+					throw new DomainException('Unknown element: '.$child->getName());
 
 			}
 		}
@@ -207,7 +202,7 @@ class OC_DB_MDB2SchemaReader {
 				$table->addIndex($fields, $name);
 			}
 		} else {
-			var_dump($name, $fields);
+			throw new DomainException('Empty index definition: '.$name.' options:'. print_r($fields, true));
 		}
 	}
 
diff --git a/lib/db/schema.php b/lib/db/schema.php
index 231b8068af0..cd356e7ff83 100644
--- a/lib/db/schema.php
+++ b/lib/db/schema.php
@@ -76,8 +76,6 @@ class OC_DB_Schema {
 		$toSchema = clone $fromSchema;
 		$toSchema->dropTable('user');
 		$sql = $fromSchema->getMigrateToSql($toSchema, $conn->getDatabasePlatform());
-		var_dump($sql);
-		die;
 		$conn->execute($sql);
 	}
 

From 427242cf32da2dbdabada5b6311ae96685fb1112 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Sun, 17 Mar 2013 14:32:01 +0100
Subject: [PATCH 14/47] Use the correct property for connection object

---
 lib/db.php    | 12 ++++++------
 lib/setup.php |  2 +-
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/lib/db.php b/lib/db.php
index 518fcc56a09..49eeeea430c 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -308,7 +308,7 @@ class OC_DB {
 	 */
 	public static function getDbStructure( $file, $mode=MDB2_SCHEMA_DUMP_STRUCTURE) {
 		self::connectDoctrine();
-		return OC_DB_Schema::getDbStructure(self::$connection, $file);
+		return OC_DB_Schema::getDbStructure(self::$DOCTRINE, $file);
 	}
 
 	/**
@@ -320,7 +320,7 @@ class OC_DB {
 	 */
 	public static function createDbFromStructure( $file ) {
 		self::connectDoctrine();
-		return OC_DB_Schema::createDbFromStructure(self::$connection, $file);
+		return OC_DB_Schema::createDbFromStructure(self::$DOCTRINE, $file);
 		/* FIXME: use CURRENT_TIMESTAMP for all databases. mysql supports it as a default for DATETIME since 5.6.5 [1]
 		 * as a fallback we could use 0000-01-01 00:00:00 everywhere
 		 * [1] http://bugs.mysql.com/bug.php?id=27645
@@ -343,7 +343,7 @@ class OC_DB {
 	public static function updateDbFromStructure($file) {
 		self::connectDoctrine();
 		try {
-			$result = OC_DB_Schema::updateDbFromStructure(self::$connection, $file);
+			$result = OC_DB_Schema::updateDbFromStructure(self::$DOCTRINE, $file);
 		} catch (Exception $e) {
 			OC_Log::write('core', 'Failed to update database structure ('.$e.')', OC_Log::FATAL);
 			throw $e;
@@ -543,7 +543,7 @@ class OC_DB {
 	 */
 	public static function dropTable($tableName) {
 		self::connectDoctrine();
-		OC_DB_Schema::dropTable(self::$connection, $tableName);
+		OC_DB_Schema::dropTable(self::$DOCTRINE, $tableName);
 	}
 
 	/**
@@ -552,7 +552,7 @@ class OC_DB {
 	 */
 	public static function removeDBStructure($file) {
 		self::connectDoctrine();
-		OC_DB_Schema::removeDBStructure(self::$connection, $file);
+		OC_DB_Schema::removeDBStructure(self::$DOCTRINE, $file);
 	}
 
 	/**
@@ -561,7 +561,7 @@ class OC_DB {
 	 */
 	public static function replaceDB( $file ) {
 		self::connectDoctrine();
-		OC_DB_Schema::replaceDB(self::$connection, $file);
+		OC_DB_Schema::replaceDB(self::$DOCTRINE, $file);
 	}
 
 	/**
diff --git a/lib/setup.php b/lib/setup.php
index 8814447f52f..bee70e7ebcc 100644
--- a/lib/setup.php
+++ b/lib/setup.php
@@ -178,7 +178,7 @@ class OC_Setup {
 				}
 			}
 			else {
-				//delete the old sqlite database first, might cause infinte loops otherwise
+				//delete the old sqlite database first, might cause infinite loops otherwise
 				if(file_exists("$datadir/owncloud.db")) {
 					unlink("$datadir/owncloud.db");
 				}

From ee57ddd0b78d83afeeded76057177e990fff9fa4 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Mon, 25 Feb 2013 08:48:28 +0100
Subject: [PATCH 15/47] Rewrite query for numRows function for SELECT queries

---
 lib/db.php | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/lib/db.php b/lib/db.php
index 49eeeea430c..379cb342db5 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -661,7 +661,14 @@ class DoctrineStatementWrapper {
 	 * provide numRows
 	 */
 	public function numRows() {
-		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]}", PDO::FETCH_NUM);
+			return $query->execute($this->lastArguments)->fetchColumn();
+		}else{
+			return $this->statement->rowCount();
+		}
 	}
 
 	/**

From 2866376f344b83d3aa6f1692b571a359c880909d Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Sun, 17 Mar 2013 15:25:41 +0100
Subject: [PATCH 16/47] Fix handling of empty defaults in schema

---
 lib/db/mdb2schemareader.php | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php
index 6408c27e916..1d71af1700c 100644
--- a/lib/db/mdb2schemareader.php
+++ b/lib/db/mdb2schemareader.php
@@ -124,17 +124,13 @@ class OC_DB_MDB2SchemaReader {
 		}
 		if (isset($name) && isset($type)) {
 			if (empty($options['default'])) {
+				if (empty($options['notnull']) || !$options['notnull']) {
+					unset($options['default']);
+				}
 				if ($type == 'integer') {
-					if (empty($options['default'])) {
-						if (empty($options['notnull'])) {
-							unset($options['default']);
-						}
-						else {
-							$options['default'] = 0;
-						}
-					}
+					$options['default'] = 0;
 				}
-				if (!empty($options['autoincrement'])) {
+				if (!empty($options['autoincrement']) && $options['autoincrement']) {
 					unset($options['default']);
 				}
 			}

From 947e03aab51493a860778a541b31f991006bf6ff Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Sun, 17 Mar 2013 17:00:07 +0100
Subject: [PATCH 17/47] Quote index columns that need it

---
 lib/db/mdb2schemareader.php | 8 +++++++-
 lib/db/schema.php           | 6 +++---
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php
index 1d71af1700c..827323a5512 100644
--- a/lib/db/mdb2schemareader.php
+++ b/lib/db/mdb2schemareader.php
@@ -9,10 +9,12 @@
 class OC_DB_MDB2SchemaReader {
 	static protected $DBNAME;
 	static protected $DBTABLEPREFIX;
+	static protected $platform;
 
-	public static function loadSchemaFromFile($file) {
+	public static function loadSchemaFromFile($file, $platform) {
 		self::$DBNAME  = OC_Config::getValue( "dbname", "owncloud" );
 		self::$DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" );
+		self::$platform = $platform;
 		$schema = new \Doctrine\DBAL\Schema\Schema();
 		$xml = simplexml_load_file($file);
 		foreach($xml->children() as $child) {
@@ -173,6 +175,10 @@ class OC_DB_MDB2SchemaReader {
 						switch($field->getName()) {
 							case 'name':
 								$field_name = (string)$field;
+								$keywords = self::$platform->getReservedKeywordsList();
+								if ($keywords->isKeyword($field_name)) {
+									$field_name = self::$platform->quoteIdentifier($field_name);
+								}
 								$fields[] = $field_name;
 								break;
 							case 'sorting':
diff --git a/lib/db/schema.php b/lib/db/schema.php
index cd356e7ff83..7a1ec204044 100644
--- a/lib/db/schema.php
+++ b/lib/db/schema.php
@@ -32,7 +32,7 @@ class OC_DB_Schema {
 	 * TODO: write more documentation
 	 */
 	public static function createDbFromStructure( $conn, $file ) {
-		$toSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file);
+		$toSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file, $conn->getDatabasePlatform());
 		return self::executeSchemaChange($conn, $toSchema);
 	}
 
@@ -45,7 +45,7 @@ class OC_DB_Schema {
 		$sm = $conn->getSchemaManager();
 		$fromSchema = $sm->createSchema();
 
-		$toSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file);
+		$toSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file, $conn->getDatabasePlatform());
 
 		// remove tables we don't know about
 		foreach($fromSchema->getTables() as $table) {
@@ -84,7 +84,7 @@ class OC_DB_Schema {
 	 * @param string $file the xml file describing the tables
 	 */
 	public static function removeDBStructure($conn, $file) {
-		$fromSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file);
+		$fromSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file, $conn->getDatabasePlatform());
 		$toSchema = clone $fromSchema;
 		foreach($toSchema->getTables() as $table) {
 			$toSchema->dropTable($table->getName());

From 82f0bcce30329df723001b0d08c62d4f0af3afc1 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Sun, 17 Mar 2013 17:28:36 +0100
Subject: [PATCH 18/47] Also fix sequences in schemachange

---
 lib/db/schema.php | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/lib/db/schema.php b/lib/db/schema.php
index 7a1ec204044..8941881d4ea 100644
--- a/lib/db/schema.php
+++ b/lib/db/schema.php
@@ -53,6 +53,12 @@ class OC_DB_Schema {
 				$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);

From d44f457d2d08ed72354a39e0cb8c34c829e83ecb Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Sun, 17 Mar 2013 17:34:35 +0100
Subject: [PATCH 19/47] integer length 1 is not only used for boolean values

---
 lib/db/mdb2schemareader.php | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php
index 827323a5512..53eb849d86a 100644
--- a/lib/db/mdb2schemareader.php
+++ b/lib/db/mdb2schemareader.php
@@ -138,10 +138,7 @@ class OC_DB_MDB2SchemaReader {
 			}
 			if ($type == 'integer') {
 				$length = $options['length'];
-				if ($length == 1) {
-					$type = 'boolean';
-				}
-				else if ($length < 4) {
+				if ($length < 4) {
 					$type = 'smallint';
 				}
 				else if ($length > 4) {

From 6f13a35513b212de937c44522df17501360dd870 Mon Sep 17 00:00:00 2001
From: Thomas Mueller 
Date: Tue, 19 Mar 2013 18:52:54 +0100
Subject: [PATCH 20/47] documentation added and trying to fix minor code issues

---
 lib/db.php                  | 11 ++++++++---
 lib/db/mdb2schemareader.php | 16 ++++++++++++++++
 lib/db/schema.php           |  6 +++---
 3 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/lib/db.php b/lib/db.php
index 379cb342db5..951c21f4146 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -196,6 +196,7 @@ class OC_DB {
 	 * @param string $query Query string
 	 * @param int $limit
 	 * @param int $offset
+	 * @throws DatabaseException
 	 * @return \Doctrine\DBAL\Statement prepared SQL query
 	 *
 	 * SQL query via Doctrine prepare(), needs to be execute()'d!
@@ -235,7 +236,7 @@ class OC_DB {
 			try {
 				$result=self::$connection->prepare($query);
 			} catch(\Doctrine\DBAL\DBALException $e) {
-				throw new DatabaseException($e->getMessage(), $query);
+				throw new \DatabaseException($e->getMessage(), $query);
 			}
 			$result=new DoctrineStatementWrapper($result);
 		}
@@ -338,6 +339,7 @@ class OC_DB {
 	/**
 	 * @brief update the database scheme
 	 * @param string $file file to read structure from
+	 * @throws Exception
 	 * @return bool
 	 */
 	public static function updateDbFromStructure($file) {
@@ -367,7 +369,7 @@ class OC_DB {
 	 * @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 The return value from DoctrineStatementWrapper->execute()
+	 * @return bool return value from DoctrineStatementWrapper->execute()
 	 */
 	public static function insertIfNotExist($table, $input) {
 		self::connect();
@@ -398,6 +400,7 @@ class OC_DB {
 				OC_Log::write('core', $entry, OC_Log::FATAL);
 				error_log('DB error: '.$entry);
 				OC_Template::printErrorPage( $entry );
+				return false;
 			}
 
 			if($result->numRows() == 0) {
@@ -430,6 +433,7 @@ class OC_DB {
 			OC_Log::write('core', $entry, OC_Log::FATAL);
 			error_log('DB error: ' . $entry);
 			OC_Template::printErrorPage( $entry );
+			return false;
 		}
 
 		return $result->execute();
@@ -556,7 +560,7 @@ class OC_DB {
 	}
 
 	/**
-	 * @brief replaces the owncloud tables with a new set
+	 * @brief replaces the ownCloud tables with a new set
 	 * @param $file string path to the MDB2 xml db export file
 	 */
 	public static function replaceDB( $file ) {
@@ -799,6 +803,7 @@ class DoctrineStatementWrapper {
 	 * 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);
diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php
index 53eb849d86a..7a7efe551c1 100644
--- a/lib/db/mdb2schemareader.php
+++ b/lib/db/mdb2schemareader.php
@@ -11,6 +11,12 @@ class OC_DB_MDB2SchemaReader {
 	static protected $DBTABLEPREFIX;
 	static protected $platform;
 
+	/**
+	 * @param $file
+	 * @param $platform
+	 * @return \Doctrine\DBAL\Schema\Schema
+	 * @throws DomainException
+	 */
 	public static function loadSchemaFromFile($file, $platform) {
 		self::$DBNAME  = OC_Config::getValue( "dbname", "owncloud" );
 		self::$DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" );
@@ -38,6 +44,11 @@ class OC_DB_MDB2SchemaReader {
 		return $schema;
 	}
 
+	/**
+	 * @param\Doctrine\DBAL\Schema\Schema $schema
+	 * @param $xml
+	 * @throws DomainException
+	 */
 	private static function loadTable($schema, $xml) {
 		foreach($xml->children() as $child) {
 			switch($child->getName()) {
@@ -60,6 +71,11 @@ class OC_DB_MDB2SchemaReader {
 		}
 	}
 
+	/**
+	 * @param \Doctrine\DBAL\Schema\Table $table
+	 * @param $xml
+	 * @throws DomainException
+	 */
 	private static function loadDeclaration($table, $xml) {
 		foreach($xml->children() as $child) {
 			switch($child->getName()) {
diff --git a/lib/db/schema.php b/lib/db/schema.php
index 8941881d4ea..37379f60663 100644
--- a/lib/db/schema.php
+++ b/lib/db/schema.php
@@ -101,19 +101,19 @@ class OC_DB_Schema {
 	}
 
 	/**
-	 * @brief replaces the owncloud tables with a new set
+	 * @brief replaces the ownCloud tables with a new set
 	 * @param $file string path to the MDB2 xml db export file
 	 */
 	public static function replaceDB( $conn, $file ) {
 		$apps = OC_App::getAllApps();
 		self::beginTransaction();
 		// Delete the old tables
-		self::removeDBStructure( OC::$SERVERROOT . '/db_structure.xml' );
+		self::removeDBStructure( $conn, OC::$SERVERROOT . '/db_structure.xml' );
 
 		foreach($apps as $app) {
 			$path = OC_App::getAppPath($app).'/appinfo/database.xml';
 			if(file_exists($path)) {
-				self::removeDBStructure( $path );
+				self::removeDBStructure( $conn, $path );
 			}
 		}
 

From 28d2379c43b31ec362f1e0b4d09fed3ac9812cd2 Mon Sep 17 00:00:00 2001
From: Thomas Mueller 
Date: Tue, 19 Mar 2013 19:17:40 +0100
Subject: [PATCH 21/47] initial fix for MSSQL

---
 lib/db.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/db.php b/lib/db.php
index 951c21f4146..3aff9cc68ae 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -703,11 +703,11 @@ class DoctrineStatementWrapper {
 	}
 
 	private function tryFixSubstringLastArgumentDataForMSSQL($input) {
-		$query = $this->statement->queryString;
+		$query = $this->statement->getWrappedStatement()->queryString;
 		$pos = stripos ($query, 'SUBSTRING');
 
 		if ( $pos === false) {
-			return;
+			return $input;
 		}
 
 		try {

From d89e748926f4ea901c0c17f0f7c3651548611b87 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Fri, 3 May 2013 16:02:53 +0200
Subject: [PATCH 22/47] Use supplied tablename

---
 lib/db/schema.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/db/schema.php b/lib/db/schema.php
index 37379f60663..89ab2381615 100644
--- a/lib/db/schema.php
+++ b/lib/db/schema.php
@@ -80,7 +80,7 @@ class OC_DB_Schema {
 		$sm = $conn->getSchemaManager();
 		$fromSchema = $sm->createSchema();
 		$toSchema = clone $fromSchema;
-		$toSchema->dropTable('user');
+		$toSchema->dropTable($tableName);
 		$sql = $fromSchema->getMigrateToSql($toSchema, $conn->getDatabasePlatform());
 		$conn->execute($sql);
 	}

From 26c91bfbc23788928f7b6c2d8666b7a4e63318ff Mon Sep 17 00:00:00 2001
From: Thomas Mueller 
Date: Fri, 24 May 2013 15:19:13 +0200
Subject: [PATCH 23/47] adding Oracle support to autotest.sh

---
 autotest.sh | 59 +++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 55 insertions(+), 4 deletions(-)

diff --git a/autotest.sh b/autotest.sh
index fdf6d2fe098..eb07cf8748b 100755
--- a/autotest.sh
+++ b/autotest.sh
@@ -54,6 +54,22 @@ cat > ./tests/autoconfig-pgsql.php < ./tests/autoconfig-oci.php < false,
+  'dbtype' => 'oci',
+  'dbtableprefix' => 'oc_',
+  'adminlogin' => 'admin',
+  'adminpass' => 'admin',
+  'directory' => '$BASEDIR/$DATADIR',
+  'dbuser' => 'oc_autotest',
+  'dbname' => 'XE',
+  'dbhost' => 'localhost',
+  'dbpass' => 'owncloud',
+);
+DELIM
+
 function execute_tests {
 	echo "Setup environment for $1 testing ..."
 	# back to root folder
@@ -77,6 +93,30 @@ function execute_tests {
 	if [ "$1" == "pgsql" ] ; then
 		dropdb -U oc_autotest oc_autotest
 	fi
+	if [ "$1" == "oci" ] ; then
+		echo "drop the database"
+		sqlplus -s -l / as sysdba <
Date: Mon, 24 Jun 2013 22:37:07 +0200
Subject: [PATCH 24/47] Fix not null with empty default

---
 lib/db/mdb2schemareader.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php
index 7a7efe551c1..00b880a68b4 100644
--- a/lib/db/mdb2schemareader.php
+++ b/lib/db/mdb2schemareader.php
@@ -144,6 +144,7 @@ class OC_DB_MDB2SchemaReader {
 			if (empty($options['default'])) {
 				if (empty($options['notnull']) || !$options['notnull']) {
 					unset($options['default']);
+					$options['notnull'] = false;
 				}
 				if ($type == 'integer') {
 					$options['default'] = 0;

From 6887d7daf5f0dfcd2f1da0d9c8f796a4fcc29963 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Mon, 24 Jun 2013 22:38:05 +0200
Subject: [PATCH 25/47] Skip Test_Archive_TAR in php 5.5 for now

---
 tests/lib/archive/tar.php | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/tests/lib/archive/tar.php b/tests/lib/archive/tar.php
index e66a8740879..d831487b16f 100644
--- a/tests/lib/archive/tar.php
+++ b/tests/lib/archive/tar.php
@@ -10,6 +10,12 @@ require_once 'archive.php';
 
 if (!OC_Util::runningOnWindows()) {
 class Test_Archive_TAR extends Test_Archive {
+	public function setUp() {
+		if (floatval(phpversion())>=5.5) {
+			$this->markTestSkipped('php 5.5 changed unpack function.');
+			return;
+		}
+	}
 	protected function getExisting() {
 		$dir = OC::$SERVERROOT . '/tests/data';
 		return new OC_Archive_TAR($dir . '/data.tar.gz');

From dca8c1cbc138203d4069660bb8c1caf23ff68e04 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Fri, 24 May 2013 21:57:34 +0200
Subject: [PATCH 26/47] Fixes connecting to Oracle without port set

---
 lib/db.php | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/db.php b/lib/db.php
index 8bd3964492a..41ec5ec67eb 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -156,11 +156,13 @@ class OC_DB {
 							'user' => $user,
 							'password' => $pass,
 							'host' => $host,
-							'port' => $port,
 							'dbname' => $name,
 							'charset' => 'AL32UTF8',
 							'driver' => 'oci8',
 					);
+					if (!empty($port)) {
+						$connectionParams['port'] = $port;
+					}
 					break;
 				case 'mssql':
 					$connectionParams = array(

From 6300b95896540dcac145598edf50ec778be1a3eb Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Wed, 12 Jun 2013 18:48:18 +0200
Subject: [PATCH 27/47] UNIX_TIMESTAMP replace for Oracle

---
 lib/db.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lib/db.php b/lib/db.php
index 41ec5ec67eb..ab4364299c4 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -517,6 +517,7 @@ class OC_DB {
 		} elseif( $type == 'oci'  ) {
 			$query = str_replace( '`', '"', $query );
 			$query = str_ireplace( 'NOW()', 'CURRENT_TIMESTAMP', $query );
+			$query = str_ireplace( 'UNIX_TIMESTAMP()', "(cast(sys_extract_utc(systimestamp) as date) - date'1970-01-01') * 86400", $query );
 		}elseif( $type == 'mssql' ) {
 			$query = preg_replace( "/\`(.*?)`/", "[$1]", $query );
 			$query = str_replace( 'NOW()', 'CURRENT_TIMESTAMP', $query );

From 159efa8bd4a5437bd2b028d40910132fd22702f1 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Wed, 12 Jun 2013 18:48:53 +0200
Subject: [PATCH 28/47] OCI doesn't have a queryString

---
 lib/db.php | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lib/db.php b/lib/db.php
index ab4364299c4..cf352fe9fb0 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -733,6 +733,11 @@ class DoctrineStatementWrapper {
 	 * 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) {

From 9fa4b78ba46a7763f3274b8fa4932cbc16e2ca7f Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Fri, 21 Jun 2013 15:41:10 +0200
Subject: [PATCH 29/47] Use Doctrines Oracle sequence suffix

---
 lib/db.php | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/lib/db.php b/lib/db.php
index cf352fe9fb0..8b2f36aac62 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -322,12 +322,20 @@ class OC_DB {
 			$row = $result->fetchRow();
 			self::raiseExceptionOnError($row, 'fetching row for insertid failed');
 			return $row['id'];
-		} else if( $type === 'mssql' || $type === 'oci') {
+		} else if( $type === 'mssql') {
 			if($table !== null) {
 				$prefix = OC_Config::getValue( "dbtableprefix", "oc_" );
 				$table = str_replace( '*PREFIX*', $prefix, $table );
 			}
-			 self::$connection->lastInsertId($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_" );

From 23da0c7d188b4c0a119e16c5be48ce322df29068 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Mon, 24 Jun 2013 17:46:51 +0200
Subject: [PATCH 30/47] Fix tableExists test function for Oracle

---
 tests/lib/dbschema.php | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/tests/lib/dbschema.php b/tests/lib/dbschema.php
index 59f203993ef..813112f1fe5 100644
--- a/tests/lib/dbschema.php
+++ b/tests/lib/dbschema.php
@@ -102,9 +102,10 @@ class Test_DBSchema extends PHPUnit_Framework_TestCase {
 				$exists = $result && $result->fetchOne();
 				break;
 			case 'oci':
-				$sql = 'SELECT table_name FROM user_tables WHERE table_name = ?';
-				$result = \OC_DB::executeAudited($sql, array($table));
-				$exists = (bool)$result->fetchOne(); //oracle uses MDB2 and returns null
+				$sql = "SELECT table_name FROM user_tables WHERE table_name = '{$table}'";
+				$query = OC_DB::prepare($sql);
+				$result = $query->execute(array());
+				$exists = $result && $result->fetchOne();
 				break;
 			case 'mssql':
 				$sql = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '{$table}'";

From 144a8eb01f29f4f6ebb8117a3d6936722732ea49 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Fri, 24 May 2013 22:00:42 +0200
Subject: [PATCH 31/47] Quote tablenames

---
 lib/db/schema.php | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lib/db/schema.php b/lib/db/schema.php
index 89ab2381615..d862f3d25c9 100644
--- a/lib/db/schema.php
+++ b/lib/db/schema.php
@@ -63,6 +63,13 @@ class OC_DB_Schema {
 		$comparator = new \Doctrine\DBAL\Schema\Comparator();
 		$schemaDiff = $comparator->compare($fromSchema, $toSchema);
 
+		$platform = $conn->getDatabasePlatform();
+		$tables = $schemaDiff->newTables + $schemaDiff->changedTables + $schemaDiff->removedTables;
+		foreach($tables as $tableDiff) {
+			$tableDiff->name = $platform->quoteIdentifier($tableDiff->name);
+		}
+
+
 		//$from = $fromSchema->toSql($conn->getDatabasePlatform());
 		//$to = $toSchema->toSql($conn->getDatabasePlatform());
 		//echo($from[9]);

From eb9078407437f275687e31c9bd7486f469ffa978 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Wed, 12 Jun 2013 18:51:59 +0200
Subject: [PATCH 32/47] Fix table change tests for OCI

---
 tests/data/db_structure2.xml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/data/db_structure2.xml b/tests/data/db_structure2.xml
index fc6fe0bba7d..6f12f81f477 100644
--- a/tests/data/db_structure2.xml
+++ b/tests/data/db_structure2.xml
@@ -49,8 +49,9 @@
 
    
     description
-    clob
+    text
     false
+    1024
    
 
    

From b980987e32270fc416eefc87f7e163763cab2776 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Fri, 21 Jun 2013 12:04:52 +0200
Subject: [PATCH 33/47] Doctrine only returns false

---
 tests/lib/db.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/lib/db.php b/tests/lib/db.php
index afbdb413c3d..60331a23295 100644
--- a/tests/lib/db.php
+++ b/tests/lib/db.php
@@ -37,7 +37,7 @@ class Test_DB extends PHPUnit_Framework_TestCase {
 		$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);

From 769212a9a025a58a5b2189eb2461a01e0ece6d36 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Mon, 24 Jun 2013 18:15:02 +0200
Subject: [PATCH 34/47] numRows doesn't work with Oracle

---
 tests/lib/db.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/lib/db.php b/tests/lib/db.php
index 60331a23295..18b7340ec9b 100644
--- a/tests/lib/db.php
+++ b/tests/lib/db.php
@@ -94,7 +94,7 @@ class Test_DB extends PHPUnit_Framework_TestCase {
 		$query = OC_DB::prepare('SELECT * FROM `*PREFIX*'.$this->table3.'`');
 		$result = $query->execute();
 		$this->assertTrue((bool)$result);
-		$this->assertEquals('4', $result->numRows());
+		$this->assertEquals('4', count($result->fetchAll()));
 	}
 
 	public function testinsertIfNotExistDontOverwrite() {

From fae3cf1a87b474cd84d9fdf20113f7fd3653e5e3 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Fri, 21 Jun 2013 12:06:26 +0200
Subject: [PATCH 35/47] Always quote db identifiers in OC_DB_MDB2SchemaReader

---
 lib/db/mdb2schemareader.php | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php
index 00b880a68b4..19d0ba4d4ea 100644
--- a/lib/db/mdb2schemareader.php
+++ b/lib/db/mdb2schemareader.php
@@ -55,6 +55,7 @@ class OC_DB_MDB2SchemaReader {
 				case 'name':
 					$name = (string)$child;
 					$name = str_replace( '*dbprefix*', self::$DBTABLEPREFIX, $name );
+					$name = self::$platform->quoteIdentifier($name);
 					$table = $schema->createTable($name);
 					break;
 				case 'create':
@@ -98,6 +99,7 @@ class OC_DB_MDB2SchemaReader {
 			switch($child->getName()) {
 				case 'name':
 					$name = (string)$child;
+					$name = self::$platform->quoteIdentifier($name);
 					break;
 				case 'type':
 					$type = (string)$child;
@@ -189,10 +191,7 @@ class OC_DB_MDB2SchemaReader {
 						switch($field->getName()) {
 							case 'name':
 								$field_name = (string)$field;
-								$keywords = self::$platform->getReservedKeywordsList();
-								if ($keywords->isKeyword($field_name)) {
-									$field_name = self::$platform->quoteIdentifier($field_name);
-								}
+								$field_name = self::$platform->quoteIdentifier($field_name);
 								$fields[] = $field_name;
 								break;
 							case 'sorting':

From 21f87d63cf9ebfeea8113efe08418b7d2d445aef Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Wed, 26 Jun 2013 18:07:53 +0200
Subject: [PATCH 36/47] Change nullable of filecache.etag and permissions.user

---
 db_structure.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/db_structure.xml b/db_structure.xml
index cefb7fc52c9..4c192ba028e 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -308,7 +308,7 @@
 				etag
 				text
 				
-				true
+				false
 				40
 			
 
@@ -383,7 +383,7 @@
 				user
 				text
 				
-				true
+				false
 				64
 			
 

From a9ee15cf405bd61c50856d3cc47ce6ecdfac0841 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Wed, 26 Jun 2013 20:48:01 +0200
Subject: [PATCH 37/47] Use Doctrine platform to add limit and offset to query

---
 lib/db.php | 19 ++++---------------
 1 file changed, 4 insertions(+), 15 deletions(-)

diff --git a/lib/db.php b/lib/db.php
index 8b2f36aac62..ac9ed77898f 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -207,22 +207,11 @@ class OC_DB {
 	static public function prepare( $query , $limit=null, $offset=null ) {
 
 		if (!is_null($limit) && $limit != -1) {
-			//Doctrine 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];

From 0c680b46cdfe5106d87ad807657c9d2e558b4a73 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Wed, 26 Jun 2013 20:48:54 +0200
Subject: [PATCH 38/47] View test needs a dummy user

---
 tests/lib/files/view.php | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php
index 830913a91ad..3bac9e770aa 100644
--- a/tests/lib/files/view.php
+++ b/tests/lib/files/view.php
@@ -20,10 +20,19 @@ class View extends \PHPUnit_Framework_TestCase {
 	private $storages = array();
 
 	public function setUp() {
+		\OC_User::clearBackends();
+		\OC_User::useBackend(new \OC_User_Dummy());
+
+		//login
+		\OC_User::createUser('test', 'test');
+		$this->user=\OC_User::getUser();
+		\OC_User::setUserId('test');
+
 		\OC\Files\Filesystem::clearMounts();
 	}
 
 	public function tearDown() {
+		\OC_User::setUserId($this->user);
 		foreach ($this->storages as $storage) {
 			$cache = $storage->getCache();
 			$ids = $cache->getAll();

From 3b31afb2a712ad06e96e0e2e5f872ec3e435810d Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Wed, 26 Jun 2013 21:40:31 +0200
Subject: [PATCH 39/47] Oracle doesn't know & as bitwise AND

---
 lib/public/share.php | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/lib/public/share.php b/lib/public/share.php
index 122ab3fa030..304cb7239eb 100644
--- a/lib/public/share.php
+++ b/lib/public/share.php
@@ -662,7 +662,13 @@ class Share {
 					// Remove the permissions for all reshares of this item
 					if (!empty($ids)) {
 						$ids = "'".implode("','", $ids)."'";
-						$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = `permissions` & ?'
+						// TODO this should be done with Doctrine platform objects
+						if (\OC_Config::getValue( "dbtype") === 'oci') {
+							$andOp = 'BITAND(`permissions`, ?)';
+						} else {
+							$andOp = '`permissions` & ?';
+						}
+						$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp
 							.' WHERE `id` IN ('.$ids.')');
 						$query->execute(array($permissions));
 					}

From 77dc3964f8c89826e7706ea8cc88da5efdaf8212 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= 
Date: Wed, 26 Jun 2013 19:57:28 +0200
Subject: [PATCH 40/47] check item id is set

---
 lib/public/share.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/public/share.php b/lib/public/share.php
index 304cb7239eb..5abf5dee264 100644
--- a/lib/public/share.php
+++ b/lib/public/share.php
@@ -984,7 +984,7 @@ class Share {
 					// Check if the same owner shared with the user twice
 					// through a group and user share - this is allowed
 					$id = $targets[$row[$column]];
-					if ($items[$id]['uid_owner'] == $row['uid_owner']) {
+					if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
 						// Switch to group share type to ensure resharing conditions aren't bypassed
 						if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
 							$items[$id]['share_type'] = self::SHARE_TYPE_GROUP;

From 10951f9bd542a3e0d162194c8737cfb0ef9048b0 Mon Sep 17 00:00:00 2001
From: Thomas Mueller 
Date: Thu, 27 Jun 2013 23:34:36 +0200
Subject: [PATCH 41/47] adding PHPDoc

---
 lib/db.php                  | 6 +++---
 lib/db/mdb2schemawriter.php | 6 ++++++
 lib/db/schema.php           | 3 ++-
 3 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/lib/db.php b/lib/db.php
index ac9ed77898f..9e5bd2d99b3 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -49,10 +49,10 @@ class OC_DB {
 	/**
 	 * @var \Doctrine\DBAL\Connection
 	 */
-	static private $connection; //the prefered connection to use, only Doctrine
+	static private $connection; //the preferred connection to use, only Doctrine
 	static private $backend=null;
 	/**
-	 * @var Doctrine
+	 * @var \Doctrine\DBAL\Connection
 	 */
 	static private $DOCTRINE=null;
 
@@ -652,7 +652,7 @@ class OC_DB {
 	/**
 	 * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException
 	 * @param mixed $result
-	 * @param string message
+	 * @param string $message
 	 * @return void
 	 * @throws DatabaseException
 	 */
diff --git a/lib/db/mdb2schemawriter.php b/lib/db/mdb2schemawriter.php
index a6367a0e354..21b43cbfe80 100644
--- a/lib/db/mdb2schemawriter.php
+++ b/lib/db/mdb2schemawriter.php
@@ -7,6 +7,12 @@
  */
 
 class OC_DB_MDB2SchemaWriter {
+
+	/**
+	 * @param $file
+	 * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $sm
+	 * @return bool
+	 */
 	static public function saveSchemaToFile($file, $sm) {
 		$xml = new SimpleXMLElement('');
 		$xml->addChild('name', OC_Config::getValue( "dbname", "owncloud" ));
diff --git a/lib/db/schema.php b/lib/db/schema.php
index d862f3d25c9..3d5b404825a 100644
--- a/lib/db/schema.php
+++ b/lib/db/schema.php
@@ -12,8 +12,9 @@ class OC_DB_Schema {
 
 	/**
 	 * @brief saves database scheme to xml file
+	 * @param \Doctrine\DBAL\Connection $conn
 	 * @param string $file name of file
-	 * @param int $mode
+	 * @param int|string $mode
 	 * @return bool
 	 *
 	 * TODO: write more documentation

From 74016666c114236b354c6aa7fe3ec2c854a72e9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= 
Date: Fri, 28 Jun 2013 11:12:05 +0200
Subject: [PATCH 42/47] enable oracle testing

enabling, because the test shows a green ball: https://ci.tmit.eu/job/ownCloud-Server(doctrine)-oci/33/console
---
 autotest.sh | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/autotest.sh b/autotest.sh
index 4562b3ed08a..71c12fd45fd 100755
--- a/autotest.sh
+++ b/autotest.sh
@@ -146,8 +146,7 @@ if [ -z "$1" ]
 	execute_tests "sqlite"
 	execute_tests 'mysql'
 	execute_tests 'pgsql'
-	# we will add oci as soon as it's stable
-	#execute_tests 'oci'
+	execute_tests 'oci'
 else
 	execute_tests $1
 fi

From 6145e617185e24919eaf6e94dddeb3b3be735b8c Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Fri, 28 Jun 2013 11:42:23 +0200
Subject: [PATCH 43/47] Remove deadcode

---
 lib/db.php                  | 24 ------------------------
 lib/db/mdb2schemareader.php |  3 ---
 lib/db/schema.php           |  3 ---
 3 files changed, 30 deletions(-)

diff --git a/lib/db.php b/lib/db.php
index 9e5bd2d99b3..0fa487dedb3 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -376,18 +376,6 @@ class OC_DB {
 	public static function createDbFromStructure( $file ) {
 		self::connectDoctrine();
 		return OC_DB_Schema::createDbFromStructure(self::$DOCTRINE, $file);
-		/* FIXME: use CURRENT_TIMESTAMP for all databases. mysql supports it as a default for DATETIME since 5.6.5 [1]
-		 * as a fallback we could use 0000-01-01 00:00:00 everywhere
-		 * [1] http://bugs.mysql.com/bug.php?id=27645
-		 * http://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html
-		 * http://www.postgresql.org/docs/8.1/static/functions-datetime.html
-		 * http://www.sqlite.org/lang_createtable.html
-		 * http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm
-		 */
-		if( $CONFIG_DBTYPE == 'pgsql' ) { //mysql support it too but sqlite doesn't
-			$content = str_replace( '0000-00-00 00:00:00',
-				'CURRENT_TIMESTAMP', $content );
-		}
 	}
 
 	/**
@@ -405,18 +393,6 @@ class OC_DB {
 			throw $e;
 		}
 		return $result;
-		/* FIXME: use CURRENT_TIMESTAMP for all databases. mysql supports it as a default for DATETIME since 5.6.5 [1]
-		 * as a fallback we could use 0000-01-01 00:00:00 everywhere
-		 * [1] http://bugs.mysql.com/bug.php?id=27645
-		 * http://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html
-		 * http://www.postgresql.org/docs/8.1/static/functions-datetime.html
-		 * http://www.sqlite.org/lang_createtable.html
-		 * http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm
-		 */
-		if( $CONFIG_DBTYPE == 'pgsql' ) { //mysql support it too but sqlite doesn't
-			$content = str_replace( '0000-00-00 00:00:00',
-				'CURRENT_TIMESTAMP', $content );
-		}
 	}
 
 	/**
diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php
index 19d0ba4d4ea..702482b569f 100644
--- a/lib/db/mdb2schemareader.php
+++ b/lib/db/mdb2schemareader.php
@@ -26,9 +26,6 @@ class OC_DB_MDB2SchemaReader {
 		foreach($xml->children() as $child) {
 			switch($child->getName()) {
 				case 'name':
-					$name = (string)$child;
-					$name = str_replace( '*dbname*', self::$DBNAME, $name );
-					break;
 				case 'create':
 				case 'overwrite':
 				case 'charset':
diff --git a/lib/db/schema.php b/lib/db/schema.php
index 3d5b404825a..fa053c64ef0 100644
--- a/lib/db/schema.php
+++ b/lib/db/schema.php
@@ -7,9 +7,6 @@
  */
 
 class OC_DB_Schema {
-	private static $DBNAME;
-	private static $DBTABLEPREFIX;
-
 	/**
 	 * @brief saves database scheme to xml file
 	 * @param \Doctrine\DBAL\Connection $conn

From b04e09a90118bfba8faa1fc6a5381c26148a4796 Mon Sep 17 00:00:00 2001
From: Bart Visscher 
Date: Fri, 28 Jun 2013 11:48:38 +0200
Subject: [PATCH 44/47] Move DoctrineStatementWrapper to its own file

---
 lib/db.php                  | 187 +-----------------------------------
 lib/db/statementwrapper.php | 186 +++++++++++++++++++++++++++++++++++
 2 files changed, 190 insertions(+), 183 deletions(-)
 create mode 100644 lib/db/statementwrapper.php

diff --git a/lib/db.php b/lib/db.php
index 0fa487dedb3..e38d464e755 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -232,7 +232,7 @@ class OC_DB {
 			} catch(\Doctrine\DBAL\DBALException $e) {
 				throw new \DatabaseException($e->getMessage(), $query);
 			}
-			$result=new DoctrineStatementWrapper($result);
+			$result=new OC_DB_StatementWrapper($result);
 		}
 		if ((is_null($limit) || $limit == -1) and self::$cachingEnabled ) {
 			$type = OC_Config::getValue( "dbtype", "sqlite" );
@@ -245,7 +245,7 @@ class OC_DB {
 
 	/**
 	 * @brief execute a prepared statement, on error write log and throw exception
-	 * @param mixed $stmt DoctrineStatementWrapperm,
+	 * @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
@@ -278,7 +278,7 @@ class OC_DB {
 			$stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']);
 		}
 		self::raiseExceptionOnError($stmt, 'Could not prepare statement');
-		if ($stmt instanceof DoctrineStatementWrapper) {
+		if ($stmt instanceof OC_DB_StatementWrapper) {
 			$result = $stmt->execute($parameters);
 			self::raiseExceptionOnError($result, 'Could not execute statement');
 		} else {
@@ -399,7 +399,7 @@ class OC_DB {
 	 * @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 return value from DoctrineStatementWrapper->execute()
+	 * @return bool return value from OC_DB_StatementWrapper->execute()
 	 */
 	public static function insertIfNotExist($table, $input) {
 		self::connect();
@@ -680,182 +680,3 @@ class OC_DB {
 		self::$cachingEnabled = $enabled;
 	}
 }
-
-/**
- * small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement
- */
-class DoctrineStatementWrapper {
-	/**
-	 * @var \Doctrine\DBAL\Driver\Statement
-	 */
-	private $statement=null;
-	private $lastArguments=array();
-
-	public function __construct($statement) {
-		$this->statement=$statement;
-	}
-
-	/**
-	 * 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($input);
-		} else {
-			$result=$this->statement->execute();
-		}
-		
-		if ($result) {
-			return $this;
-		} else {
-			return false;
-		}
-	}
-
-	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().'"
'; - $entry .= 'Offending command was: '.$this->statement->queryString .'
'; - $entry .= 'Input parameters: ' .print_r($input, true).'
'; - $entry .= 'Stack trace: ' .$e->getTraceAsString().'
'; - 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); - } -} diff --git a/lib/db/statementwrapper.php b/lib/db/statementwrapper.php new file mode 100644 index 00000000000..0d650186412 --- /dev/null +++ b/lib/db/statementwrapper.php @@ -0,0 +1,186 @@ + + * 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 + */ + private $statement=null; + private $lastArguments=array(); + + public function __construct($statement) { + $this->statement=$statement; + } + + /** + * 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($input); + } else { + $result=$this->statement->execute(); + } + + if ($result) { + return $this; + } else { + return false; + } + } + + 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().'"
'; + $entry .= 'Offending command was: '.$this->statement->queryString .'
'; + $entry .= 'Input parameters: ' .print_r($input, true).'
'; + $entry .= 'Stack trace: ' .$e->getTraceAsString().'
'; + 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); + } +} From b8a7e9730159f66d86e2b09f80c7c420b479646b Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 5 Jul 2013 21:42:37 +0200 Subject: [PATCH 45/47] Add comment to column definition --- lib/db/mdb2schemareader.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php index 702482b569f..c506aa26521 100644 --- a/lib/db/mdb2schemareader.php +++ b/lib/db/mdb2schemareader.php @@ -134,6 +134,10 @@ class OC_DB_MDB2SchemaReader { $default = (string)$child; $options['default'] = $default; break; + case 'comments': + $comment = (string)$child; + $options['comment'] = $comment; + break; default: throw new DomainException('Unknown element: '.$child->getName()); From ab2037ab5d215220c7edbd0685f4f3b25d523258 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 5 Jul 2013 22:05:16 +0200 Subject: [PATCH 46/47] Only change integer type when the length is set --- lib/db/mdb2schemareader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php index c506aa26521..4dc1fd4616d 100644 --- a/lib/db/mdb2schemareader.php +++ b/lib/db/mdb2schemareader.php @@ -156,7 +156,7 @@ class OC_DB_MDB2SchemaReader { unset($options['default']); } } - if ($type == 'integer') { + if ($type == 'integer' && isset($options['length'])) { $length = $options['length']; if ($length < 4) { $type = 'smallint'; From e2b6781cf9a97e4ad10e4824e38ccdaf3c2dc676 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Thu, 18 Jul 2013 20:28:57 +0200 Subject: [PATCH 47/47] Tweaks to the MDB2SchemaReader --- 3rdparty | 2 +- db_structure.xml | 2 +- lib/db/mdb2schemareader.php | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/3rdparty b/3rdparty index c8623cc80d4..25e8568d41a 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit c8623cc80d47022cb25874b69849cd2f57fd4874 +Subproject commit 25e8568d41a9b9a6d1662ccf33058822a890e7f5 diff --git a/db_structure.xml b/db_structure.xml index 4c192ba028e..ef5de653033 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -383,7 +383,7 @@ user text - false + true 64 diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php index 4dc1fd4616d..0ead9528c93 100644 --- a/lib/db/mdb2schemareader.php +++ b/lib/db/mdb2schemareader.php @@ -148,6 +148,8 @@ class OC_DB_MDB2SchemaReader { if (empty($options['notnull']) || !$options['notnull']) { unset($options['default']); $options['notnull'] = false; + } else { + $options['default'] = ''; } if ($type == 'integer') { $options['default'] = 0; @@ -165,9 +167,12 @@ class OC_DB_MDB2SchemaReader { $type = 'bigint'; } } - $table->addColumn($name, $type, $options); 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)); } }