You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

db.php 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Frank Karlitschek
  6. * @copyright 2012 Frank Karlitschek frank@owncloud.org
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public
  19. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. class DatabaseException extends Exception{
  23. private $query;
  24. public function __construct($message, $query){
  25. parent::__construct($message);
  26. $this->query = $query;
  27. }
  28. public function getQuery(){
  29. return $this->query;
  30. }
  31. }
  32. /**
  33. * This class manages the access to the database. It basically is a wrapper for
  34. * MDB2 with some adaptions.
  35. */
  36. class OC_DB {
  37. const BACKEND_PDO=0;
  38. const BACKEND_MDB2=1;
  39. static private $preparedQueries = array();
  40. /**
  41. * @var MDB2_Driver_Common
  42. */
  43. static private $connection; //the prefered connection to use, either PDO or MDB2
  44. static private $backend=null;
  45. /**
  46. * @var MDB2_Driver_Common
  47. */
  48. static private $MDB2=null;
  49. /**
  50. * @var PDO
  51. */
  52. static private $PDO=null;
  53. /**
  54. * @var MDB2_Schema
  55. */
  56. static private $schema=null;
  57. static private $inTransaction=false;
  58. static private $prefix=null;
  59. static private $type=null;
  60. /**
  61. * check which backend we should use
  62. * @return int BACKEND_MDB2 or BACKEND_PDO
  63. */
  64. private static function getDBBackend() {
  65. //check if we can use PDO, else use MDB2 (installation always needs to be done my mdb2)
  66. if(class_exists('PDO') && OC_Config::getValue('installed', false)) {
  67. $type = OC_Config::getValue( "dbtype", "sqlite" );
  68. if($type=='oci') { //oracle also always needs mdb2
  69. return self::BACKEND_MDB2;
  70. }
  71. if($type=='sqlite3') $type='sqlite';
  72. $drivers=PDO::getAvailableDrivers();
  73. if(array_search($type, $drivers)!==false) {
  74. return self::BACKEND_PDO;
  75. }
  76. }
  77. return self::BACKEND_MDB2;
  78. }
  79. /**
  80. * @brief connects to the database
  81. * @param int $backend
  82. * @return bool true if connection can be established or false on error
  83. *
  84. * Connects to the database as specified in config.php
  85. */
  86. public static function connect($backend=null) {
  87. if(self::$connection) {
  88. return true;
  89. }
  90. if(is_null($backend)) {
  91. $backend=self::getDBBackend();
  92. }
  93. if($backend==self::BACKEND_PDO) {
  94. $success = self::connectPDO();
  95. self::$connection=self::$PDO;
  96. self::$backend=self::BACKEND_PDO;
  97. }else{
  98. $success = self::connectMDB2();
  99. self::$connection=self::$MDB2;
  100. self::$backend=self::BACKEND_MDB2;
  101. }
  102. return $success;
  103. }
  104. /**
  105. * connect to the database using pdo
  106. *
  107. * @return bool
  108. */
  109. public static function connectPDO() {
  110. if(self::$connection) {
  111. if(self::$backend==self::BACKEND_MDB2) {
  112. self::disconnect();
  113. }else{
  114. return true;
  115. }
  116. }
  117. self::$preparedQueries = array();
  118. // The global data we need
  119. $name = OC_Config::getValue( "dbname", "owncloud" );
  120. $host = OC_Config::getValue( "dbhost", "" );
  121. $user = OC_Config::getValue( "dbuser", "" );
  122. $pass = OC_Config::getValue( "dbpassword", "" );
  123. $type = OC_Config::getValue( "dbtype", "sqlite" );
  124. if(strpos($host, ':')) {
  125. list($host, $port)=explode(':', $host, 2);
  126. }else{
  127. $port=false;
  128. }
  129. $opts = array();
  130. $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' );
  131. // do nothing if the connection already has been established
  132. if(!self::$PDO) {
  133. // Add the dsn according to the database type
  134. switch($type) {
  135. case 'sqlite':
  136. $dsn='sqlite2:'.$datadir.'/'.$name.'.db';
  137. break;
  138. case 'sqlite3':
  139. $dsn='sqlite:'.$datadir.'/'.$name.'.db';
  140. break;
  141. case 'mysql':
  142. if($port) {
  143. $dsn='mysql:dbname='.$name.';host='.$host.';port='.$port;
  144. }else{
  145. $dsn='mysql:dbname='.$name.';host='.$host;
  146. }
  147. $opts[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES 'UTF8'";
  148. break;
  149. case 'pgsql':
  150. if($port) {
  151. $dsn='pgsql:dbname='.$name.';host='.$host.';port='.$port;
  152. }else{
  153. $dsn='pgsql:dbname='.$name.';host='.$host;
  154. }
  155. /**
  156. * Ugly fix for pg connections pbm when password use spaces
  157. */
  158. $e_user = addslashes($user);
  159. $e_password = addslashes($pass);
  160. $pass = $user = null;
  161. $dsn .= ";user='$e_user';password='$e_password'";
  162. /** END OF FIX***/
  163. break;
  164. case 'oci': // Oracle with PDO is unsupported
  165. if ($port) {
  166. $dsn = 'oci:dbname=//' . $host . ':' . $port . '/' . $name;
  167. } else {
  168. $dsn = 'oci:dbname=//' . $host . '/' . $name;
  169. }
  170. break;
  171. default:
  172. return false;
  173. }
  174. try{
  175. self::$PDO=new PDO($dsn, $user, $pass, $opts);
  176. }catch(PDOException $e) {
  177. OC_Log::write('core', $e->getMessage(), OC_Log::FATAL);
  178. OC_User::setUserId(null);
  179. // send http status 503
  180. header('HTTP/1.1 503 Service Temporarily Unavailable');
  181. header('Status: 503 Service Temporarily Unavailable');
  182. OC_Template::printErrorPage('Failed to connect to database');
  183. die();
  184. }
  185. // We always, really always want associative arrays
  186. self::$PDO->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
  187. self::$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  188. }
  189. return true;
  190. }
  191. /**
  192. * connect to the database using mdb2
  193. */
  194. public static function connectMDB2() {
  195. if(self::$connection) {
  196. if(self::$backend==self::BACKEND_PDO) {
  197. self::disconnect();
  198. }else{
  199. return true;
  200. }
  201. }
  202. self::$preparedQueries = array();
  203. // The global data we need
  204. $name = OC_Config::getValue( "dbname", "owncloud" );
  205. $host = OC_Config::getValue( "dbhost", "" );
  206. $user = OC_Config::getValue( "dbuser", "" );
  207. $pass = OC_Config::getValue( "dbpassword", "" );
  208. $type = OC_Config::getValue( "dbtype", "sqlite" );
  209. $SERVERROOT=OC::$SERVERROOT;
  210. $datadir=OC_Config::getValue( "datadirectory", "$SERVERROOT/data" );
  211. // do nothing if the connection already has been established
  212. if(!self::$MDB2) {
  213. // Require MDB2.php (not required in the head of the file so we only load it when needed)
  214. require_once 'MDB2.php';
  215. // Prepare options array
  216. $options = array(
  217. 'portability' => MDB2_PORTABILITY_ALL - MDB2_PORTABILITY_FIX_CASE,
  218. 'log_line_break' => '<br>',
  219. 'idxname_format' => '%s',
  220. 'debug' => true,
  221. 'quote_identifier' => true );
  222. // Add the dsn according to the database type
  223. switch($type) {
  224. case 'sqlite':
  225. case 'sqlite3':
  226. $dsn = array(
  227. 'phptype' => $type,
  228. 'database' => "$datadir/$name.db",
  229. 'mode' => '0644'
  230. );
  231. break;
  232. case 'mysql':
  233. $dsn = array(
  234. 'phptype' => 'mysql',
  235. 'username' => $user,
  236. 'password' => $pass,
  237. 'hostspec' => $host,
  238. 'database' => $name
  239. );
  240. break;
  241. case 'pgsql':
  242. $dsn = array(
  243. 'phptype' => 'pgsql',
  244. 'username' => $user,
  245. 'password' => $pass,
  246. 'hostspec' => $host,
  247. 'database' => $name
  248. );
  249. break;
  250. case 'oci':
  251. $dsn = array(
  252. 'phptype' => 'oci8',
  253. 'username' => $user,
  254. 'password' => $pass,
  255. 'charset' => 'AL32UTF8',
  256. );
  257. if ($host != '') {
  258. $dsn['hostspec'] = $host;
  259. $dsn['database'] = $name;
  260. } else { // use dbname for hostspec
  261. $dsn['hostspec'] = $name;
  262. $dsn['database'] = $user;
  263. }
  264. break;
  265. default:
  266. return false;
  267. }
  268. // Try to establish connection
  269. self::$MDB2 = MDB2::factory( $dsn, $options );
  270. // Die if we could not connect
  271. if( PEAR::isError( self::$MDB2 )) {
  272. OC_Log::write('core', self::$MDB2->getUserInfo(), OC_Log::FATAL);
  273. OC_Log::write('core', self::$MDB2->getMessage(), OC_Log::FATAL);
  274. OC_User::setUserId(null);
  275. // send http status 503
  276. header('HTTP/1.1 503 Service Temporarily Unavailable');
  277. header('Status: 503 Service Temporarily Unavailable');
  278. OC_Template::printErrorPage('Failed to connect to database');
  279. die();
  280. }
  281. // We always, really always want associative arrays
  282. self::$MDB2->setFetchMode(MDB2_FETCHMODE_ASSOC);
  283. }
  284. // we are done. great!
  285. return true;
  286. }
  287. /**
  288. * @brief Prepare a SQL query
  289. * @param string $query Query string
  290. * @param int $limit
  291. * @param int $offset
  292. * @return MDB2_Statement_Common prepared SQL query
  293. *
  294. * SQL query via MDB2 prepare(), needs to be execute()'d!
  295. */
  296. static public function prepare( $query , $limit=null, $offset=null ) {
  297. if (!is_null($limit) && $limit != -1) {
  298. if (self::$backend == self::BACKEND_MDB2) {
  299. //MDB2 uses or emulates limits & offset internally
  300. self::$MDB2->setLimit($limit, $offset);
  301. } else {
  302. //PDO does not handle limit and offset.
  303. //FIXME: check limit notation for other dbs
  304. //the following sql thus might needs to take into account db ways of representing it
  305. //(oracle has no LIMIT / OFFSET)
  306. $limit = (int)$limit;
  307. $limitsql = ' LIMIT ' . $limit;
  308. if (!is_null($offset)) {
  309. $offset = (int)$offset;
  310. $limitsql .= ' OFFSET ' . $offset;
  311. }
  312. //insert limitsql
  313. if (substr($query, -1) == ';') { //if query ends with ;
  314. $query = substr($query, 0, -1) . $limitsql . ';';
  315. } else {
  316. $query.=$limitsql;
  317. }
  318. }
  319. } else {
  320. if (isset(self::$preparedQueries[$query])) {
  321. return self::$preparedQueries[$query];
  322. }
  323. }
  324. $rawQuery = $query;
  325. // Optimize the query
  326. $query = self::processQuery( $query );
  327. self::connect();
  328. // return the result
  329. if(self::$backend==self::BACKEND_MDB2) {
  330. $result = self::$connection->prepare( $query );
  331. // Die if we have an error (error means: bad query, not 0 results!)
  332. if( PEAR::isError($result)) {
  333. throw new DatabaseException($result->getMessage(), $query);
  334. }
  335. }else{
  336. try{
  337. $result=self::$connection->prepare($query);
  338. }catch(PDOException $e) {
  339. throw new DatabaseException($e->getMessage(), $query);
  340. }
  341. $result=new PDOStatementWrapper($result);
  342. }
  343. if (is_null($limit) || $limit == -1) {
  344. self::$preparedQueries[$rawQuery] = $result;
  345. }
  346. return $result;
  347. }
  348. /**
  349. * @brief gets last value of autoincrement
  350. * @param string $table The optional table name (will replace *PREFIX*) and add sequence suffix
  351. * @return int id
  352. *
  353. * MDB2 lastInsertID()
  354. *
  355. * Call this method right after the insert command or other functions may
  356. * cause trouble!
  357. */
  358. public static function insertid($table=null) {
  359. self::connect();
  360. $type = OC_Config::getValue( "dbtype", "sqlite" );
  361. if( $type == 'pgsql' ) {
  362. $query = self::prepare('SELECT lastval() AS id');
  363. $row = $query->execute()->fetchRow();
  364. return $row['id'];
  365. }else{
  366. if($table !== null) {
  367. $prefix = OC_Config::getValue( "dbtableprefix", "oc_" );
  368. $suffix = OC_Config::getValue( "dbsequencesuffix", "_id_seq" );
  369. $table = str_replace( '*PREFIX*', $prefix, $table ).$suffix;
  370. }
  371. return self::$connection->lastInsertId($table);
  372. }
  373. }
  374. /**
  375. * @brief Disconnect
  376. * @return bool
  377. *
  378. * This is good bye, good bye, yeah!
  379. */
  380. public static function disconnect() {
  381. // Cut connection if required
  382. if(self::$connection) {
  383. if(self::$backend==self::BACKEND_MDB2) {
  384. self::$connection->disconnect();
  385. }
  386. self::$connection=false;
  387. self::$MDB2=false;
  388. self::$PDO=false;
  389. }
  390. return true;
  391. }
  392. /**
  393. * @brief saves database scheme to xml file
  394. * @param string $file name of file
  395. * @param int $mode
  396. * @return bool
  397. *
  398. * TODO: write more documentation
  399. */
  400. public static function getDbStructure( $file, $mode=MDB2_SCHEMA_DUMP_STRUCTURE) {
  401. self::connectScheme();
  402. // write the scheme
  403. $definition = self::$schema->getDefinitionFromDatabase();
  404. $dump_options = array(
  405. 'output_mode' => 'file',
  406. 'output' => $file,
  407. 'end_of_line' => "\n"
  408. );
  409. self::$schema->dumpDatabase( $definition, $dump_options, $mode );
  410. return true;
  411. }
  412. /**
  413. * @brief Creates tables from XML file
  414. * @param string $file file to read structure from
  415. * @return bool
  416. *
  417. * TODO: write more documentation
  418. */
  419. public static function createDbFromStructure( $file ) {
  420. $CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" );
  421. $CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" );
  422. $CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );
  423. // cleanup the cached queries
  424. self::$preparedQueries = array();
  425. self::connectScheme();
  426. // read file
  427. $content = file_get_contents( $file );
  428. // Make changes and save them to an in-memory file
  429. $file2 = 'static://db_scheme';
  430. $content = str_replace( '*dbname*', $CONFIG_DBNAME, $content );
  431. $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content );
  432. /* FIXME: use CURRENT_TIMESTAMP for all databases. mysql supports it as a default for DATETIME since 5.6.5 [1]
  433. * as a fallback we could use <default>0000-01-01 00:00:00</default> everywhere
  434. * [1] http://bugs.mysql.com/bug.php?id=27645
  435. * http://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html
  436. * http://www.postgresql.org/docs/8.1/static/functions-datetime.html
  437. * http://www.sqlite.org/lang_createtable.html
  438. * http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm
  439. */
  440. if( $CONFIG_DBTYPE == 'pgsql' ) { //mysql support it too but sqlite doesn't
  441. $content = str_replace( '<default>0000-00-00 00:00:00</default>', '<default>CURRENT_TIMESTAMP</default>', $content );
  442. }
  443. file_put_contents( $file2, $content );
  444. // Try to create tables
  445. $definition = self::$schema->parseDatabaseDefinitionFile( $file2 );
  446. //clean up memory
  447. unlink( $file2 );
  448. // Die in case something went wrong
  449. if( $definition instanceof MDB2_Schema_Error ) {
  450. OC_Template::printErrorPage( $definition->getMessage().': '.$definition->getUserInfo() );
  451. }
  452. if(OC_Config::getValue('dbtype', 'sqlite')==='oci') {
  453. unset($definition['charset']); //or MDB2 tries SHUTDOWN IMMEDIATE
  454. $oldname = $definition['name'];
  455. $definition['name']=OC_Config::getValue( "dbuser", $oldname );
  456. }
  457. $ret=self::$schema->createDatabase( $definition );
  458. // Die in case something went wrong
  459. if( $ret instanceof MDB2_Error ) {
  460. OC_Template::printErrorPage( self::$MDB2->getDebugOutput().' '.$ret->getMessage() . ': ' . $ret->getUserInfo() );
  461. }
  462. return true;
  463. }
  464. /**
  465. * @brief update the database scheme
  466. * @param string $file file to read structure from
  467. * @return bool
  468. */
  469. public static function updateDbFromStructure($file) {
  470. $CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" );
  471. $CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );
  472. self::connectScheme();
  473. // read file
  474. $content = file_get_contents( $file );
  475. $previousSchema = self::$schema->getDefinitionFromDatabase();
  476. if (PEAR::isError($previousSchema)) {
  477. $error = $previousSchema->getMessage();
  478. $detail = $previousSchema->getDebugInfo();
  479. $message = 'Failed to get existing database structure for updating ('.$error.', '.$detail.')';
  480. OC_Log::write('core', $message, OC_Log::FATAL);
  481. throw new Exception($message);
  482. }
  483. // Make changes and save them to an in-memory file
  484. $file2 = 'static://db_scheme';
  485. $content = str_replace( '*dbname*', $previousSchema['name'], $content );
  486. $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content );
  487. /* FIXME: use CURRENT_TIMESTAMP for all databases. mysql supports it as a default for DATETIME since 5.6.5 [1]
  488. * as a fallback we could use <default>0000-01-01 00:00:00</default> everywhere
  489. * [1] http://bugs.mysql.com/bug.php?id=27645
  490. * http://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html
  491. * http://www.postgresql.org/docs/8.1/static/functions-datetime.html
  492. * http://www.sqlite.org/lang_createtable.html
  493. * http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm
  494. */
  495. if( $CONFIG_DBTYPE == 'pgsql' ) { //mysql support it too but sqlite doesn't
  496. $content = str_replace( '<default>0000-00-00 00:00:00</default>', '<default>CURRENT_TIMESTAMP</default>', $content );
  497. }
  498. file_put_contents( $file2, $content );
  499. $op = self::$schema->updateDatabase($file2, $previousSchema, array(), false);
  500. //clean up memory
  501. unlink( $file2 );
  502. if (PEAR::isError($op)) {
  503. $error = $op->getMessage();
  504. $detail = $op->getDebugInfo();
  505. $message = 'Failed to update database structure ('.$error.', '.$detail.')';
  506. OC_Log::write('core', $message, OC_Log::FATAL);
  507. throw new Exception($message);
  508. }
  509. return true;
  510. }
  511. /**
  512. * @brief connects to a MDB2 database scheme
  513. * @returns bool
  514. *
  515. * Connects to a MDB2 database scheme
  516. */
  517. private static function connectScheme() {
  518. // We need a mdb2 database connection
  519. self::connectMDB2();
  520. self::$MDB2->loadModule('Manager');
  521. self::$MDB2->loadModule('Reverse');
  522. // Connect if this did not happen before
  523. if(!self::$schema) {
  524. require_once 'MDB2/Schema.php';
  525. self::$schema=MDB2_Schema::factory(self::$MDB2);
  526. }
  527. return true;
  528. }
  529. /**
  530. * @brief Insert a row if a matching row doesn't exists.
  531. * @param string $table. The table to insert into in the form '*PREFIX*tableName'
  532. * @param array $input. An array of fieldname/value pairs
  533. * @returns The return value from PDOStatementWrapper->execute()
  534. */
  535. public static function insertIfNotExist($table, $input) {
  536. self::connect();
  537. $prefix = OC_Config::getValue( "dbtableprefix", "oc_" );
  538. $table = str_replace( '*PREFIX*', $prefix, $table );
  539. if(is_null(self::$type)) {
  540. self::$type=OC_Config::getValue( "dbtype", "sqlite" );
  541. }
  542. $type = self::$type;
  543. $query = '';
  544. // differences in escaping of table names ('`' for mysql) and getting the current timestamp
  545. if( $type == 'sqlite' || $type == 'sqlite3' ) {
  546. // NOTE: For SQLite we have to use this clumsy approach
  547. // otherwise all fieldnames used must have a unique key.
  548. $query = 'SELECT * FROM "' . $table . '" WHERE ';
  549. foreach($input as $key => $value) {
  550. $query .= $key . " = '" . $value . '\' AND ';
  551. }
  552. $query = substr($query, 0, strlen($query) - 5);
  553. try {
  554. $stmt = self::prepare($query);
  555. $result = $stmt->execute();
  556. } catch(PDOException $e) {
  557. $entry = 'DB Error: "'.$e->getMessage() . '"<br />';
  558. $entry .= 'Offending command was: ' . $query . '<br />';
  559. OC_Log::write('core', $entry, OC_Log::FATAL);
  560. error_log('DB error: '.$entry);
  561. OC_Template::printErrorPage( $entry );
  562. }
  563. if($result->numRows() == 0) {
  564. $query = 'INSERT INTO "' . $table . '" ("'
  565. . implode('","', array_keys($input)) . '") VALUES("'
  566. . implode('","', array_values($input)) . '")';
  567. } else {
  568. return true;
  569. }
  570. } elseif( $type == 'pgsql' || $type == 'oci' || $type == 'mysql') {
  571. $query = 'INSERT INTO `' .$table . '` ('
  572. . implode(',', array_keys($input)) . ') SELECT \''
  573. . implode('\',\'', array_values($input)) . '\' FROM ' . $table . ' WHERE ';
  574. foreach($input as $key => $value) {
  575. $query .= $key . " = '" . $value . '\' AND ';
  576. }
  577. $query = substr($query, 0, strlen($query) - 5);
  578. $query .= ' HAVING COUNT(*) = 0';
  579. }
  580. // TODO: oci should be use " (quote) instead of ` (backtick).
  581. //OC_Log::write('core', __METHOD__ . ', type: ' . $type . ', query: ' . $query, OC_Log::DEBUG);
  582. try {
  583. $result = self::prepare($query);
  584. } catch(PDOException $e) {
  585. $entry = 'DB Error: "'.$e->getMessage() . '"<br />';
  586. $entry .= 'Offending command was: ' . $query.'<br />';
  587. OC_Log::write('core', $entry, OC_Log::FATAL);
  588. error_log('DB error: ' . $entry);
  589. OC_Template::printErrorPage( $entry );
  590. }
  591. return $result->execute();
  592. }
  593. /**
  594. * @brief does minor changes to query
  595. * @param string $query Query string
  596. * @return string corrected query string
  597. *
  598. * This function replaces *PREFIX* with the value of $CONFIG_DBTABLEPREFIX
  599. * and replaces the ` with ' or " according to the database driver.
  600. */
  601. private static function processQuery( $query ) {
  602. self::connect();
  603. // We need Database type and table prefix
  604. if(is_null(self::$type)) {
  605. self::$type=OC_Config::getValue( "dbtype", "sqlite" );
  606. }
  607. $type = self::$type;
  608. if(is_null(self::$prefix)) {
  609. self::$prefix=OC_Config::getValue( "dbtableprefix", "oc_" );
  610. }
  611. $prefix = self::$prefix;
  612. // differences in escaping of table names ('`' for mysql) and getting the current timestamp
  613. if( $type == 'sqlite' || $type == 'sqlite3' ) {
  614. $query = str_replace( '`', '"', $query );
  615. $query = str_ireplace( 'NOW()', 'datetime(\'now\')', $query );
  616. $query = str_ireplace( 'UNIX_TIMESTAMP()', 'strftime(\'%s\',\'now\')', $query );
  617. }elseif( $type == 'pgsql' ) {
  618. $query = str_replace( '`', '"', $query );
  619. $query = str_ireplace( 'UNIX_TIMESTAMP()', 'cast(extract(epoch from current_timestamp) as integer)', $query );
  620. }elseif( $type == 'oci' ) {
  621. $query = str_replace( '`', '"', $query );
  622. $query = str_ireplace( 'NOW()', 'CURRENT_TIMESTAMP', $query );
  623. }
  624. // replace table name prefix
  625. $query = str_replace( '*PREFIX*', $prefix, $query );
  626. return $query;
  627. }
  628. /**
  629. * @brief drop a table
  630. * @param string $tableName the table to drop
  631. */
  632. public static function dropTable($tableName) {
  633. self::connectMDB2();
  634. self::$MDB2->loadModule('Manager');
  635. self::$MDB2->dropTable($tableName);
  636. }
  637. /**
  638. * remove all tables defined in a database structure xml file
  639. * @param string $file the xml file describing the tables
  640. */
  641. public static function removeDBStructure($file) {
  642. $CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" );
  643. $CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" );
  644. self::connectScheme();
  645. // read file
  646. $content = file_get_contents( $file );
  647. // Make changes and save them to a temporary file
  648. $file2 = tempnam( get_temp_dir(), 'oc_db_scheme_' );
  649. $content = str_replace( '*dbname*', $CONFIG_DBNAME, $content );
  650. $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content );
  651. file_put_contents( $file2, $content );
  652. // get the tables
  653. $definition = self::$schema->parseDatabaseDefinitionFile( $file2 );
  654. // Delete our temporary file
  655. unlink( $file2 );
  656. $tables=array_keys($definition['tables']);
  657. foreach($tables as $table) {
  658. self::dropTable($table);
  659. }
  660. }
  661. /**
  662. * @brief replaces the owncloud tables with a new set
  663. * @param $file string path to the MDB2 xml db export file
  664. */
  665. public static function replaceDB( $file ) {
  666. $apps = OC_App::getAllApps();
  667. self::beginTransaction();
  668. // Delete the old tables
  669. self::removeDBStructure( OC::$SERVERROOT . '/db_structure.xml' );
  670. foreach($apps as $app) {
  671. $path = OC_App::getAppPath($app).'/appinfo/database.xml';
  672. if(file_exists($path)) {
  673. self::removeDBStructure( $path );
  674. }
  675. }
  676. // Create new tables
  677. self::createDBFromStructure( $file );
  678. self::commit();
  679. }
  680. /**
  681. * Start a transaction
  682. * @return bool
  683. */
  684. public static function beginTransaction() {
  685. self::connect();
  686. if (self::$backend==self::BACKEND_MDB2 && !self::$connection->supports('transactions')) {
  687. return false;
  688. }
  689. self::$connection->beginTransaction();
  690. self::$inTransaction=true;
  691. return true;
  692. }
  693. /**
  694. * Commit the database changes done during a transaction that is in progress
  695. * @return bool
  696. */
  697. public static function commit() {
  698. self::connect();
  699. if(!self::$inTransaction) {
  700. return false;
  701. }
  702. self::$connection->commit();
  703. self::$inTransaction=false;
  704. return true;
  705. }
  706. /**
  707. * check if a result is an error, works with MDB2 and PDOException
  708. * @param mixed $result
  709. * @return bool
  710. */
  711. public static function isError($result) {
  712. if(!$result) {
  713. return true;
  714. }elseif(self::$backend==self::BACKEND_MDB2 and PEAR::isError($result)) {
  715. return true;
  716. }else{
  717. return false;
  718. }
  719. }
  720. /**
  721. * returns the error code and message as a string for logging
  722. * works with MDB2 and PDOException
  723. * @param mixed $error
  724. * @return string
  725. */
  726. public static function getErrorMessage($error) {
  727. if ( self::$backend==self::BACKEND_MDB2 and PEAR::isError($error) ) {
  728. $msg = $error->getCode() . ': ' . $error->getMessage();
  729. if (defined('DEBUG') && DEBUG) {
  730. $msg .= '(' . $error->getDebugInfo() . ')';
  731. }
  732. } elseif (self::$backend==self::BACKEND_PDO and self::$PDO) {
  733. $msg = self::$PDO->errorCode() . ': ';
  734. $errorInfo = self::$PDO->errorInfo();
  735. if (is_array($errorInfo)) {
  736. $msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
  737. $msg .= 'Driver Code = '.$errorInfo[1] . ', ';
  738. $msg .= 'Driver Message = '.$errorInfo[2];
  739. }else{
  740. $msg = '';
  741. }
  742. }else{
  743. $msg = '';
  744. }
  745. return $msg;
  746. }
  747. }
  748. /**
  749. * small wrapper around PDOStatement to make it behave ,more like an MDB2 Statement
  750. */
  751. class PDOStatementWrapper{
  752. /**
  753. * @var PDOStatement
  754. */
  755. private $statement=null;
  756. private $lastArguments=array();
  757. public function __construct($statement) {
  758. $this->statement=$statement;
  759. }
  760. /**
  761. * make execute return the result instead of a bool
  762. */
  763. public function execute($input=array()) {
  764. $this->lastArguments=$input;
  765. if(count($input)>0) {
  766. $result=$this->statement->execute($input);
  767. }else{
  768. $result=$this->statement->execute();
  769. }
  770. if($result) {
  771. return $this;
  772. }else{
  773. return false;
  774. }
  775. }
  776. /**
  777. * provide numRows
  778. */
  779. public function numRows() {
  780. $regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/i';
  781. if (preg_match($regex, $this->statement->queryString, $output) > 0) {
  782. $query = OC_DB::prepare("SELECT COUNT(*) FROM {$output[1]}", PDO::FETCH_NUM);
  783. return $query->execute($this->lastArguments)->fetchColumn();
  784. }else{
  785. return $this->statement->rowCount();
  786. }
  787. }
  788. /**
  789. * provide an alias for fetch
  790. */
  791. public function fetchRow() {
  792. return $this->statement->fetch();
  793. }
  794. /**
  795. * pass all other function directly to the PDOStatement
  796. */
  797. public function __call($name, $arguments) {
  798. return call_user_func_array(array($this->statement, $name), $arguments);
  799. }
  800. /**
  801. * Provide a simple fetchOne.
  802. * fetch single column from the next row
  803. * @param int $colnum the column number to fetch
  804. */
  805. public function fetchOne($colnum = 0) {
  806. return $this->statement->fetchColumn($colnum);
  807. }
  808. }