2011-04-16 08:17:40 +00:00
< ? php
/**
* ownCloud
*
* @ author Frank Karlitschek
2012-05-26 17:14:24 +00:00
* @ copyright 2012 Frank Karlitschek frank @ owncloud . org
2011-04-16 08:17:40 +00:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation ; either
* version 3 of the License , or any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details .
*
* You should have received a copy of the GNU Affero General Public
* License along with this library . If not , see < http :// www . gnu . org / licenses />.
*
*/
/**
* This class manages the access to the database . It basically is a wrapper for
* MDB2 with some adaptions .
*/
class OC_DB {
2011-09-17 00:30:58 +00:00
const BACKEND_PDO = 0 ;
const BACKEND_MDB2 = 1 ;
2012-08-29 06:38:33 +00:00
2011-10-16 21:03:03 +00:00
static private $connection ; //the prefered connection to use, either PDO or MDB2
2011-09-17 00:30:58 +00:00
static private $backend = null ;
static private $MDB2 = false ;
static private $PDO = false ;
2011-04-16 08:17:40 +00:00
static private $schema = false ;
2012-02-09 18:51:24 +00:00
static private $inTransaction = false ;
2012-04-14 14:28:36 +00:00
static private $prefix = null ;
static private $type = null ;
2011-04-16 08:17:40 +00:00
2012-04-14 14:28:36 +00:00
/**
* check which backend we should use
* @ return BACKEND_MDB2 or BACKEND_PDO
*/
private static function getDBBackend (){
2012-07-26 16:50:59 +00:00
if ( class_exists ( 'PDO' ) && OC_Config :: getValue ( 'installed' , false )){ //check if we can use PDO, else use MDB2 (installation always needs to be done my mdb2)
2012-04-14 14:28:36 +00:00
$type = OC_Config :: getValue ( " dbtype " , " sqlite " );
2012-08-24 22:05:07 +00:00
if ( $type == 'oci' ) { //oracle also always needs mdb2
return self :: BACKEND_MDB2 ;
}
2012-04-14 14:28:36 +00:00
if ( $type == 'sqlite3' ) $type = 'sqlite' ;
$drivers = PDO :: getAvailableDrivers ();
if ( array_search ( $type , $drivers ) !== false ){
2012-07-30 18:53:21 +00:00
return self :: BACKEND_PDO ;
2012-04-14 14:28:36 +00:00
}
}
2012-08-24 22:05:07 +00:00
return self :: BACKEND_MDB2 ;
2012-04-14 14:28:36 +00:00
}
2012-08-29 06:38:33 +00:00
2011-04-16 08:17:40 +00:00
/**
* @ brief connects to the database
* @ returns true if connection can be established or nothing ( die ())
*
* Connects to the database as specified in config . php
*/
2011-10-23 10:29:12 +00:00
public static function connect ( $backend = null ){
2011-10-16 18:47:25 +00:00
if ( self :: $connection ){
return ;
}
2011-10-23 10:29:12 +00:00
if ( is_null ( $backend )){
2012-04-14 14:28:36 +00:00
$backend = self :: getDBBackend ();
2011-10-23 10:29:12 +00:00
}
if ( $backend == self :: BACKEND_PDO ){
2011-09-17 00:30:58 +00:00
self :: connectPDO ();
self :: $connection = self :: $PDO ;
self :: $backend = self :: BACKEND_PDO ;
} else {
self :: connectMDB2 ();
self :: $connection = self :: $MDB2 ;
self :: $backend = self :: BACKEND_MDB2 ;
}
}
/**
* connect to the database using pdo
*/
2012-01-08 01:57:52 +00:00
public static function connectPDO (){
if ( self :: $connection ){
if ( self :: $backend == self :: BACKEND_MDB2 ){
self :: disconnect ();
} else {
return ;
}
}
2011-04-16 08:17:40 +00:00
// The global data we need
2011-09-17 00:30:58 +00:00
$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 " );
2012-05-16 23:06:22 +00:00
if ( strpos ( $host , ':' )){
list ( $host , $port ) = explode ( ':' , $host , 2 );
} else {
$port = false ;
}
2012-04-08 01:30:06 +00:00
$opts = array ();
2011-10-16 18:47:25 +00:00
$datadir = OC_Config :: getValue ( " datadirectory " , OC :: $SERVERROOT . '/data' );
2012-08-29 06:38:33 +00:00
2011-09-17 00:30:58 +00:00
// 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' :
2012-05-16 23:06:22 +00:00
if ( $port ){
$dsn = 'mysql:dbname=' . $name . ';host=' . $host . ';port=' . $port ;
} else {
$dsn = 'mysql:dbname=' . $name . ';host=' . $host ;
}
2012-04-08 01:30:06 +00:00
$opts [ PDO :: MYSQL_ATTR_INIT_COMMAND ] = " SET NAMES 'UTF8' " ;
2011-09-17 00:30:58 +00:00
break ;
case 'pgsql' :
2012-05-16 23:06:22 +00:00
if ( $port ){
$dsn = 'pgsql:dbname=' . $name . ';host=' . $host . ';port=' . $port ;
} else {
$dsn = 'pgsql:dbname=' . $name . ';host=' . $host ;
}
2012-07-04 10:13:00 +00:00
/**
* 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***/
2011-09-17 00:30:58 +00:00
break ;
2012-08-24 22:05:07 +00:00
case 'oci' : // Oracle with PDO is unsupported
if ( $port ) {
$dsn = 'oci:dbname=//' . $host . ':' . $port . '/' . $name ;
} else {
$dsn = 'oci:dbname=//' . $host . '/' . $name ;
}
break ;
2011-09-17 00:30:58 +00:00
}
try {
2012-04-08 01:30:06 +00:00
self :: $PDO = new PDO ( $dsn , $user , $pass , $opts );
2011-09-17 00:30:58 +00:00
} catch ( PDOException $e ){
echo ( '<b>can not connect to database, using ' . $type . '. (' . $e -> getMessage () . ')</center>' );
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 ;
}
2012-08-29 06:38:33 +00:00
2011-09-17 00:30:58 +00:00
/**
* connect to the database using mdb2
*/
2012-01-08 01:57:52 +00:00
public static function connectMDB2 (){
if ( self :: $connection ){
if ( self :: $backend == self :: BACKEND_PDO ){
self :: disconnect ();
} else {
return ;
}
}
2011-09-17 00:30:58 +00:00
// 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 ;
2011-07-29 19:36:03 +00:00
$datadir = OC_Config :: getValue ( " datadirectory " , " $SERVERROOT /data " );
2011-04-16 08:17:40 +00:00
// do nothing if the connection already has been established
2011-09-17 00:30:58 +00:00
if ( ! self :: $MDB2 ){
2011-04-16 08:17:40 +00:00
// 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 (
2012-01-16 01:06:19 +00:00
'portability' => MDB2_PORTABILITY_ALL & ( ! MDB2_PORTABILITY_FIX_CASE ),
2011-04-16 08:17:40 +00:00
'log_line_break' => '<br>' ,
'idxname_format' => '%s' ,
'debug' => true ,
'quote_identifier' => true );
// Add the dsn according to the database type
2011-09-17 00:30:58 +00:00
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 ;
2012-08-24 22:05:07 +00:00
case 'oci' :
$dsn = array (
'phptype' => 'oci8' ,
'username' => $user ,
'password' => $pass ,
);
if ( $host != '' ) {
$dsn [ 'hostspec' ] = $host ;
$dsn [ 'database' ] = $name ;
} else { // use dbname for hostspec
$dsn [ 'hostspec' ] = $name ;
}
2012-07-30 20:42:43 +00:00
break ;
2011-04-16 08:17:40 +00:00
}
2012-08-29 06:38:33 +00:00
2011-04-16 08:17:40 +00:00
// Try to establish connection
2011-09-17 00:30:58 +00:00
self :: $MDB2 = MDB2 :: factory ( $dsn , $options );
2012-08-29 06:38:33 +00:00
2011-04-16 08:17:40 +00:00
// Die if we could not connect
2011-09-17 00:30:58 +00:00
if ( PEAR :: isError ( self :: $MDB2 )){
echo ( '<b>can not connect to database, using ' . $type . '. (' . self :: $MDB2 -> getUserInfo () . ')</center>' );
2011-10-16 19:42:24 +00:00
OC_Log :: write ( 'core' , self :: $MDB2 -> getUserInfo (), OC_Log :: FATAL );
OC_Log :: write ( 'core' , self :: $MDB2 -> getMessage (), OC_Log :: FATAL );
2011-04-16 08:17:40 +00:00
die ( $error );
}
2012-08-29 06:38:33 +00:00
2011-04-16 08:17:40 +00:00
// We always, really always want associative arrays
2011-09-17 00:30:58 +00:00
self :: $MDB2 -> setFetchMode ( MDB2_FETCHMODE_ASSOC );
2011-04-16 08:17:40 +00:00
}
2012-08-29 06:38:33 +00:00
2011-04-16 08:17:40 +00:00
// we are done. great!
return true ;
}
/**
* @ brief Prepare a SQL query
* @ param $query Query string
* @ returns prepared SQL query
*
* SQL query via MDB2 prepare (), needs to be execute () ' d !
*/
2012-07-30 20:42:43 +00:00
static public function prepare ( $query , $limit = null , $offset = null ){
2012-08-29 06:38:33 +00:00
2012-08-25 00:17:44 +00:00
if ( ! is_null ( $limit ) && $limit != - 1 ) {
2012-08-24 22:05:07 +00:00
if ( self :: $backend == self :: BACKEND_MDB2 ) {
//MDB2 uses or emulates limits & offset internally
self :: $MDB2 -> setLimit ( $limit , $offset );
} else {
//PDO does not handle limit and offset.
//FIXME: check limit notation for other dbs
//the following sql thus might needs to take into account db ways of representing it
//(oracle has no LIMIT / OFFSET)
$limitsql = ' LIMIT ' . $limit ;
if ( ! is_null ( $offset )) {
$limitsql .= ' OFFSET ' . $offset ;
}
//insert limitsql
if ( substr ( $query , - 1 ) == ';' ) { //if query ends with ;
$query = substr ( $query , 0 , - 1 ) . $limitsql . ';' ;
} else {
$query .= $limitsql ;
}
}
}
2011-04-16 08:17:40 +00:00
// Optimize the query
$query = self :: processQuery ( $query );
self :: connect ();
// return the result
2011-09-17 00:30:58 +00:00
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 () . '"<br />' ;
$entry .= 'Offending command was: ' . $query . '<br />' ;
2011-10-16 19:42:24 +00:00
OC_Log :: write ( 'core' , $entry , OC_Log :: FATAL );
2012-07-30 20:42:43 +00:00
error_log ( 'DB error: ' . $entry );
2011-09-17 00:30:58 +00:00
die ( $entry );
}
} else {
try {
$result = self :: $connection -> prepare ( $query );
} catch ( PDOException $e ){
$entry = 'DB Error: "' . $e -> getMessage () . '"<br />' ;
$entry .= 'Offending command was: ' . $query . '<br />' ;
2011-10-16 19:42:24 +00:00
OC_Log :: write ( 'core' , $entry , OC_Log :: FATAL );
2012-07-30 20:42:43 +00:00
error_log ( 'DB error: ' . $entry );
2011-09-17 00:30:58 +00:00
die ( $entry );
}
$result = new PDOStatementWrapper ( $result );
2011-04-16 08:17:40 +00:00
}
return $result ;
}
/**
* @ brief gets last value of autoincrement
2011-10-29 09:40:48 +00:00
* @ param $table string The optional table name ( will replace * PREFIX * ) and add sequence suffix
2011-04-16 08:17:40 +00:00
* @ returns id
*
* MDB2 lastInsertID ()
*
* Call this method right after the insert command or other functions may
* cause trouble !
*/
2011-10-29 09:40:48 +00:00
public static function insertid ( $table = null ){
2011-04-16 08:17:40 +00:00
self :: connect ();
2011-10-29 09:40:48 +00:00
if ( $table !== null ){
$prefix = OC_Config :: getValue ( " dbtableprefix " , " oc_ " );
$suffix = OC_Config :: getValue ( " dbsequencesuffix " , " _id_seq " );
$table = str_replace ( '*PREFIX*' , $prefix , $table );
}
return self :: $connection -> lastInsertId ( $table . $suffix );
2011-04-16 08:17:40 +00:00
}
/**
* @ brief Disconnect
* @ returns true / false
*
* This is good bye , good bye , yeah !
*/
public static function disconnect (){
// Cut connection if required
2011-09-17 00:30:58 +00:00
if ( self :: $connection ){
if ( self :: $backend == self :: BACKEND_MDB2 ){
self :: $connection -> disconnect ();
}
self :: $connection = false ;
2012-01-08 01:57:52 +00:00
self :: $MDB2 = false ;
self :: $PDO = false ;
2011-04-16 08:17:40 +00:00
}
return true ;
}
/**
* @ brief saves database scheme to xml file
* @ param $file name of file
* @ returns true / false
*
* TODO : write more documentation
*/
2011-10-23 13:25:38 +00:00
public static function getDbStructure ( $file , $mode = MDB2_SCHEMA_DUMP_STRUCTURE ){
2011-04-16 08:17:40 +00:00
self :: connectScheme ();
// write the scheme
$definition = self :: $schema -> getDefinitionFromDatabase ();
$dump_options = array (
'output_mode' => 'file' ,
'output' => $file ,
'end_of_line' => " \n "
);
2012-01-06 18:04:24 +00:00
self :: $schema -> dumpDatabase ( $definition , $dump_options , $mode );
2011-04-16 08:17:40 +00:00
return true ;
}
/**
* @ brief Creates tables from XML file
* @ param $file file to read structure from
* @ returns true / false
*
* TODO : write more documentation
*/
public static function createDbFromStructure ( $file ){
2011-07-29 19:36:03 +00:00
$CONFIG_DBNAME = OC_Config :: getValue ( " dbname " , " owncloud " );
$CONFIG_DBTABLEPREFIX = OC_Config :: getValue ( " dbtableprefix " , " oc_ " );
2011-08-07 19:06:53 +00:00
$CONFIG_DBTYPE = OC_Config :: getValue ( " dbtype " , " sqlite " );
2011-04-16 08:17:40 +00:00
self :: connectScheme ();
// read file
$content = file_get_contents ( $file );
2012-08-29 06:38:33 +00:00
2012-03-01 19:41:14 +00:00
// Make changes and save them to an in-memory file
$file2 = 'static://db_scheme' ;
2011-04-16 08:17:40 +00:00
$content = str_replace ( '*dbname*' , $CONFIG_DBNAME , $content );
$content = str_replace ( '*dbprefix*' , $CONFIG_DBTABLEPREFIX , $content );
2012-08-24 22:05:07 +00:00
/* FIXME : REMOVE this commented code
* actually mysql , postgresql , sqlite and oracle support CURRENT_TIMESTAMP
* 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 ( '<default>0000-00-00 00:00:00</default>' , '<default>CURRENT_TIMESTAMP</default>' , $content );
}
*/
2011-04-16 08:17:40 +00:00
file_put_contents ( $file2 , $content );
// Try to create tables
2011-04-17 09:59:15 +00:00
$definition = self :: $schema -> parseDatabaseDefinitionFile ( $file2 );
2012-08-29 06:38:33 +00:00
2012-03-01 19:41:14 +00:00
//clean up memory
2011-04-16 08:17:40 +00:00
unlink ( $file2 );
// Die in case something went wrong
if ( $definition instanceof MDB2_Schema_Error ){
die ( $definition -> getMessage () . ': ' . $definition -> getUserInfo ());
}
2012-08-24 22:05:07 +00:00
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 );
}
2012-08-29 06:38:33 +00:00
2011-04-17 09:59:15 +00:00
$ret = self :: $schema -> createDatabase ( $definition );
2011-04-16 08:17:40 +00:00
// Die in case something went wrong
if ( $ret instanceof MDB2_Error ){
2012-08-24 22:05:07 +00:00
echo ( self :: $MDB2 -> getDebugOutput ());
2011-04-16 08:17:40 +00:00
die ( $ret -> getMessage () . ': ' . $ret -> getUserInfo ());
}
return true ;
}
2012-08-29 06:38:33 +00:00
2011-10-23 13:25:38 +00:00
/**
* @ brief update the database scheme
* @ param $file file to read structure from
*/
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 );
2012-08-29 06:38:33 +00:00
2011-11-13 15:16:21 +00:00
$previousSchema = self :: $schema -> getDefinitionFromDatabase ();
2012-01-16 00:13:54 +00:00
if ( PEAR :: isError ( $previousSchema )) {
$error = $previousSchema -> getMessage ();
OC_Log :: write ( 'core' , 'Failed to get existing database structure for upgrading (' . $error . ')' , OC_Log :: FATAL );
return false ;
}
2011-11-13 15:16:21 +00:00
2012-03-01 19:41:14 +00:00
// Make changes and save them to an in-memory file
$file2 = 'static://db_scheme' ;
2011-11-13 15:16:21 +00:00
$content = str_replace ( '*dbname*' , $previousSchema [ 'name' ], $content );
2011-10-23 13:25:38 +00:00
$content = str_replace ( '*dbprefix*' , $CONFIG_DBTABLEPREFIX , $content );
2012-08-24 22:05:07 +00:00
/* FIXME : REMOVE this commented code
* actually mysql , postgresql , sqlite and oracle support CUURENT_TIMESTAMP
* 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
2011-10-23 13:25:38 +00:00
if ( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't
$content = str_replace ( '<default>0000-00-00 00:00:00</default>' , '<default>CURRENT_TIMESTAMP</default>' , $content );
}
2012-08-24 22:05:07 +00:00
*/
2011-10-23 13:25:38 +00:00
file_put_contents ( $file2 , $content );
2011-11-13 15:16:21 +00:00
$op = self :: $schema -> updateDatabase ( $file2 , $previousSchema , array (), false );
2012-08-29 06:38:33 +00:00
2012-03-01 19:41:14 +00:00
//clean up memory
2012-01-13 19:05:44 +00:00
unlink ( $file2 );
2012-08-29 06:38:33 +00:00
2011-10-23 13:25:38 +00:00
if ( PEAR :: isError ( $op )) {
2012-01-16 00:13:54 +00:00
$error = $op -> getMessage ();
2012-05-13 19:21:39 +00:00
$detail = $op -> getDebugInfo ();
OC_Log :: write ( 'core' , 'Failed to update database structure (' . $error . ', ' . $detail . ')' , OC_Log :: FATAL );
2012-01-16 00:13:54 +00:00
return false ;
2011-10-23 13:25:38 +00:00
}
return true ;
}
2011-04-16 08:17:40 +00:00
/**
* @ brief connects to a MDB2 database scheme
* @ returns true / false
*
* Connects to a MDB2 database scheme
*/
private static function connectScheme (){
2011-09-17 00:30:58 +00:00
// We need a mdb2 database connection
self :: connectMDB2 ();
2012-01-08 01:57:52 +00:00
self :: $MDB2 -> loadModule ( 'Manager' );
2012-01-08 12:16:11 +00:00
self :: $MDB2 -> loadModule ( 'Reverse' );
2011-04-16 08:17:40 +00:00
// Connect if this did not happen before
if ( ! self :: $schema ){
require_once ( 'MDB2/Schema.php' );
2011-09-17 00:30:58 +00:00
self :: $schema = MDB2_Schema :: factory ( self :: $MDB2 );
2011-04-16 08:17:40 +00:00
}
return true ;
}
/**
* @ brief does minor chages to query
* @ param $query Query string
* @ returns corrected query string
*
* This function replaces * PREFIX * with the value of $CONFIG_DBTABLEPREFIX
* and replaces the ` woth ' or " according to the database driver.
*/
private static function processQuery ( $query ){
2011-06-13 02:06:43 +00:00
self :: connect ();
2011-04-16 08:17:40 +00:00
// We need Database type and table prefix
2012-04-14 14:28:36 +00:00
if ( is_null ( self :: $type )){
2012-04-14 16:30:13 +00:00
self :: $type = OC_Config :: getValue ( " dbtype " , " sqlite " );
2012-04-14 14:28:36 +00:00
}
$type = self :: $type ;
if ( is_null ( self :: $prefix )){
self :: $prefix = OC_Config :: getValue ( " dbtableprefix " , " oc_ " );
}
$prefix = self :: $prefix ;
2012-08-29 06:38:33 +00:00
2011-09-17 00:30:58 +00:00
// differences in escaping of table names ('`' for mysql) and getting the current timestamp
2011-10-16 22:24:42 +00:00
if ( $type == 'sqlite' || $type == 'sqlite3' ){
2012-05-11 19:45:24 +00:00
$query = str_replace ( '`' , '"' , $query );
2011-09-17 00:30:58 +00:00
$query = str_replace ( 'NOW()' , 'datetime(\'now\')' , $query );
$query = str_replace ( 'now()' , 'datetime(\'now\')' , $query );
2012-08-27 16:37:16 +00:00
} elseif ( $type == 'pgsql' ){
$query = str_replace ( '`' , '"' , $query );
} elseif ( $type == 'oci' ){
2011-04-16 08:17:40 +00:00
$query = str_replace ( '`' , '"' , $query );
2011-09-17 00:30:58 +00:00
$query = str_replace ( 'NOW()' , 'CURRENT_TIMESTAMP' , $query );
$query = str_replace ( 'now()' , 'CURRENT_TIMESTAMP' , $query );
2011-04-16 08:17:40 +00:00
}
2011-04-28 15:40:51 +00:00
// replace table name prefix
2011-09-17 00:30:58 +00:00
$query = str_replace ( '*PREFIX*' , $prefix , $query );
2011-04-16 08:17:40 +00:00
return $query ;
}
2012-08-29 06:38:33 +00:00
2011-06-12 15:51:31 +00:00
/**
* @ brief drop a table
* @ param string $tableNamme the table to drop
*/
public static function dropTable ( $tableName ){
2011-09-17 00:30:58 +00:00
self :: connectMDB2 ();
self :: $MDB2 -> loadModule ( 'Manager' );
self :: $MDB2 -> dropTable ( $tableName );
2011-06-12 15:51:31 +00:00
}
2012-08-29 06:38:33 +00:00
2011-06-12 15:51:31 +00:00
/**
* remove all tables defined in a database structure xml file
* @ param string $file the xml file describing the tables
*/
public static function removeDBStructure ( $file ){
2011-07-29 19:36:03 +00:00
$CONFIG_DBNAME = OC_Config :: getValue ( " dbname " , " owncloud " );
$CONFIG_DBTABLEPREFIX = OC_Config :: getValue ( " dbtableprefix " , " oc_ " );
2011-06-12 15:51:31 +00:00
self :: connectScheme ();
// read file
$content = file_get_contents ( $file );
// Make changes and save them to a temporary file
2011-10-19 21:38:35 +00:00
$file2 = tempnam ( get_temp_dir (), 'oc_db_scheme_' );
2011-06-12 15:51:31 +00:00
$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 );
2012-08-29 06:38:33 +00:00
2011-06-12 15:51:31 +00:00
// Delete our temporary file
unlink ( $file2 );
2012-07-20 15:51:50 +00:00
$tables = array_keys ( $definition [ 'tables' ]);
foreach ( $tables as $table ){
self :: dropTable ( $table );
2011-06-12 15:51:31 +00:00
}
}
2012-08-29 06:38:33 +00:00
2012-03-01 19:41:14 +00:00
/**
2012-04-14 16:31:37 +00:00
* @ brief replaces the owncloud tables with a new set
2012-03-20 20:19:21 +00:00
* @ param $file string path to the MDB2 xml db export file
2012-03-01 19:41:14 +00:00
*/
2012-07-23 22:39:59 +00:00
public static function replaceDB ( $file ){
2012-03-02 21:47:20 +00:00
$apps = OC_App :: getAllApps ();
2012-04-01 00:25:47 +00:00
self :: beginTransaction ();
2012-03-01 19:41:14 +00:00
// Delete the old tables
2012-03-02 22:19:06 +00:00
self :: removeDBStructure ( OC :: $SERVERROOT . '/db_structure.xml' );
2012-08-29 06:38:33 +00:00
2012-03-01 19:41:14 +00:00
foreach ( $apps as $app ){
2012-06-30 10:55:38 +00:00
$path = OC_App :: getAppPath ( $app ) . '/appinfo/database.xml' ;
2012-03-01 19:41:14 +00:00
if ( file_exists ( $path )){
2012-08-29 06:38:33 +00:00
self :: removeDBStructure ( $path );
2012-03-01 19:41:14 +00:00
}
}
2012-08-29 06:38:33 +00:00
2012-03-01 19:41:14 +00:00
// Create new tables
2012-04-01 00:25:47 +00:00
self :: createDBFromStructure ( $file );
self :: commit ();
2012-08-29 06:38:33 +00:00
2012-03-01 19:41:14 +00:00
}
2012-08-29 06:38:33 +00:00
2011-07-31 18:24:53 +00:00
/**
2011-09-17 00:30:58 +00:00
* Start a transaction
2011-07-31 18:24:53 +00:00
*/
2011-09-17 00:30:58 +00:00
public static function beginTransaction (){
2011-07-31 22:07:46 +00:00
self :: connect ();
2012-02-05 00:23:41 +00:00
if ( self :: $backend == self :: BACKEND_MDB2 && ! self :: $connection -> supports ( 'transactions' )) {
2011-07-31 18:24:53 +00:00
return false ;
}
2011-09-17 00:30:58 +00:00
self :: $connection -> beginTransaction ();
2012-02-09 18:51:24 +00:00
self :: $inTransaction = true ;
2011-07-31 18:24:53 +00:00
}
/**
2011-09-17 00:30:58 +00:00
* Commit the database changes done during a transaction that is in progress
2011-07-31 18:24:53 +00:00
*/
2012-02-09 18:51:24 +00:00
public static function commit (){
2011-07-31 22:07:46 +00:00
self :: connect ();
2012-02-09 18:51:24 +00:00
if ( ! self :: $inTransaction ){
2011-07-31 18:24:53 +00:00
return false ;
}
2011-09-17 00:30:58 +00:00
self :: $connection -> commit ();
2012-02-09 18:51:24 +00:00
self :: $inTransaction = false ;
2011-09-17 00:30:58 +00:00
}
2012-03-01 19:41:14 +00:00
/**
* check if a result is an error , works with MDB2 and PDOException
* @ 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 ;
}
}
2011-09-17 00:30:58 +00:00
}
/**
* small wrapper around PDOStatement to make it behave , more like an MDB2 Statement
*/
class PDOStatementWrapper {
private $statement = null ;
private $lastArguments = array ();
public function __construct ( $statement ){
$this -> statement = $statement ;
}
2012-08-29 06:38:33 +00:00
2011-09-17 00:30:58 +00:00
/**
2011-12-08 19:04:56 +00:00
* make execute return the result instead of a bool
2011-09-17 00:30:58 +00:00
*/
public function execute ( $input = array ()){
$this -> lastArguments = $input ;
if ( count ( $input ) > 0 ){
2012-03-01 19:41:14 +00:00
$result = $this -> statement -> execute ( $input );
} else {
$result = $this -> statement -> execute ();
}
if ( $result ){
return $this ;
2011-09-17 00:30:58 +00:00
} else {
2012-03-01 19:41:14 +00:00
return false ;
2011-09-17 00:30:58 +00:00
}
}
2012-08-29 06:38:33 +00:00
2011-09-17 00:30:58 +00:00
/**
* 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 ();
2011-07-31 18:24:53 +00:00
} else {
2011-09-17 00:30:58 +00:00
return $this -> statement -> rowCount ();
2011-07-31 18:24:53 +00:00
}
}
2012-08-29 06:38:33 +00:00
2011-09-17 00:30:58 +00:00
/**
* provide an alias for fetch
*/
public function fetchRow (){
return $this -> statement -> fetch ();
}
2012-08-29 06:38:33 +00:00
2011-09-17 00:30:58 +00:00
/**
* pass all other function directly to the PDOStatement
*/
public function __call ( $name , $arguments ){
return call_user_func_array ( array ( $this -> statement , $name ), $arguments );
}
2012-08-29 06:38:33 +00:00
2011-10-16 21:03:03 +00:00
/**
* 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 );
}
2011-04-16 08:17:40 +00:00
}