File indexing completed on 2025-01-26 03:44:37
0001 <?php 0002 /* 0003 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org> 0004 0005 SPDX-License-Identifier: MIT 0006 */ 0007 0008 require_once('config.php'); 0009 require_once('restexception.php'); 0010 require_once('utils.php'); 0011 0012 /** Handles all database operations. */ 0013 class DataStore { 0014 0015 private $db; 0016 0017 function __construct() 0018 { 0019 try { 0020 $conf = new Config; 0021 $this->db = new PDO($conf->dsn(), $conf->username(), $conf->password()); 0022 } catch (PDOException $e) { 0023 throw new RESTException($e->getMessage(), 500); 0024 } 0025 } 0026 0027 /** Returns the type of the database driver. */ 0028 public function driver() 0029 { 0030 return $this->db->getAttribute(PDO::ATTR_DRIVER_NAME); 0031 } 0032 0033 /** Access to the PDO handle. */ 0034 public function pdoHandle() 0035 { 0036 return $this->db; 0037 } 0038 0039 /** Prepare a query. */ 0040 public function prepare($queryString) 0041 { 0042 try { 0043 $res = $this->db->prepare($queryString); 0044 $this->checkError($res); 0045 } catch (PDOException $e) { 0046 throw new RESTException($e->getMessage(), 500); 0047 } 0048 return $res; 0049 } 0050 0051 /** Execute a prepared query and check the result for errors. */ 0052 public function execute(PDOStatement $stmt) 0053 { 0054 try { 0055 if (!$stmt->execute()) { 0056 $err = $stmt->errorInfo(); 0057 $msg = "SQL execution error: SQLSTATE: " . $err[0] . "\nDriver error code: " . $err[1] . "\nDriver error message: " . $err[2]; 0058 throw new RESTException($msg, 500); 0059 } 0060 } catch (PDOException $e) { 0061 throw new RESTException($e->getMessage(), 500); 0062 } 0063 } 0064 0065 /** Check database result for errors, and if so, bail out. */ 0066 private function checkError($res) 0067 { 0068 if ($res === FALSE) { 0069 $err = $this->db->errorInfo(); 0070 $msg = "SQLSTATE: " . $err[0] . "\nDriver error code: " . $err[1] . "\nDriver error message: " . $err[2]; 0071 throw new RESTException($msg, 500); 0072 } 0073 } 0074 0075 /** Begin transaction. */ 0076 public function beginTransaction() 0077 { 0078 $this->db->beginTransaction(); 0079 } 0080 0081 /** Commit transaction. */ 0082 public function commit() 0083 { 0084 $this->db->commit(); 0085 } 0086 0087 /** Abort an ongoing transaction. */ 0088 public function rollback() 0089 { 0090 $this->db->rollback(); 0091 } 0092 0093 /** Verify the database schema, and fix if needed. */ 0094 public function checkSchema() 0095 { 0096 $this->beginTransaction(); 0097 $currentVersion = $this->schemaVersion(); 0098 $schemaFile = __DIR__ . '/schema.json'; 0099 $schemaDefs = json_decode(file_get_contents($schemaFile), true); 0100 $targetVersion = count($schemaDefs['schema']); 0101 0102 $res = array(); 0103 $res['previousSchemaVersion'] = $currentVersion; 0104 $res['currentSchemaVersion'] = $targetVersion; 0105 0106 if ($currentVersion == $targetVersion) 0107 return $res; 0108 if ($currentVersion > $targetVersion || $currentVersion < 0) 0109 throw new RESTException('Current schema version is invalid: ' . $currentVersion . '.', 500); 0110 0111 # apply database updates 0112 error_log('Current schema version: ' . $currentVersion . ' should be: ' . $targetVersion); 0113 for ($i = $currentVersion; $i < $targetVersion; $i++) { 0114 error_log("Applying update $i..."); 0115 $this->applySchemaChange($schemaDefs['schema'][$i]); 0116 } 0117 $this->commit(); 0118 return $res; 0119 } 0120 0121 /** Returns the current schema version. */ 0122 private function schemaVersion() 0123 { 0124 try { 0125 $res = $this->db->query('SELECT col_version FROM tbl_version'); 0126 if ($res === FALSE) { 0127 // restart transaction, so this fail doesn't block the following commands 0128 $this->rollback(); 0129 $this->beginTransaction(); 0130 return 0; 0131 } 0132 foreach ($res as $row) 0133 return intval($row['col_version']); 0134 } catch (PDOException $e) { 0135 // table does not exist yet 0136 } 0137 return 0; 0138 } 0139 0140 /** Applies a list of schema setup commands. */ 0141 private function applySchemaChange($schemaDef) 0142 { 0143 if (array_key_exists($this->driver(), $schemaDef)) 0144 $cmds = $schemaDef[$this->driver()]; 0145 else 0146 $cmds = $schemaDef['sql']; 0147 0148 foreach($cmds as $cmd) { 0149 $res = $this->db->exec($cmd); 0150 $this->checkError($res); 0151 } 0152 $res = $this->db->exec('UPDATE tbl_version SET col_version = ' . $schemaDef['version']); 0153 $this->checkError($res); 0154 } 0155 0156 } 0157 0158 ?>