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 ?>