1a1e6784eSAndreas Gohr<?php 28da7d805SAndreas Gohr 38da7d805SAndreas Gohr/** 48da7d805SAndreas Gohr * @noinspection SqlNoDataSourceInspection 58da7d805SAndreas Gohr * @noinspection SqlDialectInspection 68da7d805SAndreas Gohr * @noinspection PhpComposerExtensionStubsInspection 78da7d805SAndreas Gohr */ 88da7d805SAndreas Gohr 98da7d805SAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB; 108da7d805SAndreas Gohruse dokuwiki\plugin\sqlite\Tools; 118da7d805SAndreas Gohr 128da7d805SAndreas Gohr 13a1e6784eSAndreas Gohr/** 14a1e6784eSAndreas Gohr * DokuWiki Plugin sqlite (Helper Component) 15a1e6784eSAndreas Gohr * 16a1e6784eSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 17a1e6784eSAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de> 188da7d805SAndreas Gohr * @deprecated 2023-03-15 19a1e6784eSAndreas Gohr */ 208da7d805SAndreas Gohrclass helper_plugin_sqlite extends DokuWiki_Plugin 218da7d805SAndreas Gohr{ 228da7d805SAndreas Gohr /** @var SQLiteDB|null */ 236c7ee3f2SAndreas Gohr protected $adapter = null; 24aa81d781SKlap-in 258da7d805SAndreas Gohr /** @var array result cache */ 268da7d805SAndreas Gohr protected $data; 278da7d805SAndreas Gohr 283e9ac593SGerrit Uitslag /** 298da7d805SAndreas Gohr * constructor 303e9ac593SGerrit Uitslag */ 318da7d805SAndreas Gohr public function __construct() 328da7d805SAndreas Gohr { 338da7d805SAndreas Gohr if (!$this->existsPDOSqlite()) { 348da7d805SAndreas Gohr msg('PDO SQLite support missing in this PHP install - The sqlite plugin will not work', -1); 358da7d805SAndreas Gohr } 368da7d805SAndreas Gohr } 378da7d805SAndreas Gohr 388da7d805SAndreas Gohr /** 398da7d805SAndreas Gohr * Get the current Adapter 408da7d805SAndreas Gohr * @return SQLiteDB|null 418da7d805SAndreas Gohr */ 428da7d805SAndreas Gohr public function getAdapter() 438da7d805SAndreas Gohr { 44ca2f2adaSKlap-in return $this->adapter; 45ca2f2adaSKlap-in } 46ca2f2adaSKlap-in 47a1e6784eSAndreas Gohr /** 48aa81d781SKlap-in * Keep separate instances for every call to keep database connections 49aa81d781SKlap-in */ 508da7d805SAndreas Gohr public function isSingleton() 518da7d805SAndreas Gohr { 52aa81d781SKlap-in return false; 53aa81d781SKlap-in } 54aa81d781SKlap-in 55aa81d781SKlap-in /** 56aa81d781SKlap-in * check availabilty of PHP PDO sqlite3 57aa81d781SKlap-in */ 588da7d805SAndreas Gohr public function existsPDOSqlite() 598da7d805SAndreas Gohr { 6087fa2c18Sstretchyboy if (class_exists('pdo')) { 618da7d805SAndreas Gohr return in_array('sqlite', \PDO::getAvailableDrivers()); 627ed6069fSAdrian Lang } 63aa81d781SKlap-in return false; 64a1e6784eSAndreas Gohr } 65a1e6784eSAndreas Gohr 66a1e6784eSAndreas Gohr /** 67a1e6784eSAndreas Gohr * Initializes and opens the database 68a1e6784eSAndreas Gohr * 69a1e6784eSAndreas Gohr * Needs to be called right after loading this helper plugin 70aa81d781SKlap-in * 71aa81d781SKlap-in * @param string $dbname 72aa81d781SKlap-in * @param string $updatedir - Database update infos 73aa81d781SKlap-in * @return bool 74a1e6784eSAndreas Gohr */ 758da7d805SAndreas Gohr public function init($dbname, $updatedir) 768da7d805SAndreas Gohr { 77*d0a5ba7aSAndreas Gohr if(!DOKU_UNITTEST) { // for now we don't want to trigger the deprecation warning in the tests 783a56750bSAndreas Gohr dbg_deprecated(SQLiteDB::class); 79*d0a5ba7aSAndreas Gohr } 803a56750bSAndreas Gohr 818da7d805SAndreas Gohr try { 828da7d805SAndreas Gohr $this->adapter = new SQLiteDB($dbname, $updatedir, $this); 838da7d805SAndreas Gohr } catch (Exception $e) { 848da7d805SAndreas Gohr msg('SQLite: ' . $e->getMessage(), -1); 85c137e95fSAndreas Gohr return false; 86c137e95fSAndreas Gohr } 87a1e6784eSAndreas Gohr return true; 88a1e6784eSAndreas Gohr } 89a1e6784eSAndreas Gohr 90a1e6784eSAndreas Gohr /** 918da7d805SAndreas Gohr * This is called from the adapter itself for backwards compatibility 928da7d805SAndreas Gohr * 938da7d805SAndreas Gohr * @param SQLiteDB $adapter 948da7d805SAndreas Gohr * @return void 95a1e6784eSAndreas Gohr */ 968da7d805SAndreas Gohr function setAdapter($adapter) 978da7d805SAndreas Gohr { 988da7d805SAndreas Gohr $this->adapter = $adapter; 99a34ef333SKlap-in } 100f10ea6c1SKlap-in 101a1e6784eSAndreas Gohr /** 1023ae3f79eSKlap-in * Registers a User Defined Function for use in SQL statements 1033ae3f79eSKlap-in */ 1048da7d805SAndreas Gohr public function create_function($function_name, $callback, $num_args) 1058da7d805SAndreas Gohr { 1068da7d805SAndreas Gohr $this->adapter->pdo()->sqliteCreateFunction($function_name, $callback, $num_args); 1073ae3f79eSKlap-in } 1083ae3f79eSKlap-in 1098da7d805SAndreas Gohr // region query and result handling functions 1108da7d805SAndreas Gohr 1113ae3f79eSKlap-in /** 112e7b0736cSAndreas Gohr * Convenience function to run an INSERT OR REPLACE operation 113e7b0736cSAndreas Gohr * 114e7b0736cSAndreas Gohr * The function takes a key-value array with the column names in the key and the actual value in the value, 115e7b0736cSAndreas Gohr * build the appropriate query and executes it. 116e7b0736cSAndreas Gohr * 117e7b0736cSAndreas Gohr * @param string $table the table the entry should be saved to (will not be escaped) 118e7b0736cSAndreas Gohr * @param array $entry A simple key-value pair array (only values will be escaped) 1198da7d805SAndreas Gohr * @return bool 120e7b0736cSAndreas Gohr */ 1218da7d805SAndreas Gohr public function storeEntry($table, $entry) 1228da7d805SAndreas Gohr { 1238da7d805SAndreas Gohr try { 1248da7d805SAndreas Gohr $this->adapter->saveRecord($table, $entry); 1258da7d805SAndreas Gohr } catch (\Exception $e) { 1268da7d805SAndreas Gohr msg('SQLite: ' . $e->getMessage(), -1); 1278da7d805SAndreas Gohr return false; 128e7b0736cSAndreas Gohr } 129e7b0736cSAndreas Gohr 1308da7d805SAndreas Gohr return true; 1318da7d805SAndreas Gohr } 132e7b0736cSAndreas Gohr 133e7b0736cSAndreas Gohr /** 134a1e6784eSAndreas Gohr * Execute a query with the given parameters. 135a1e6784eSAndreas Gohr * 136a1e6784eSAndreas Gohr * Takes care of escaping 137a1e6784eSAndreas Gohr * 138a2a82480SAndreas Gohr * 139a2a82480SAndreas Gohr * @param string ...$args - the arguments of query(), the first is the sql and others are values 140a1e6784eSAndreas Gohr */ 1418da7d805SAndreas Gohr public function query() 1428da7d805SAndreas Gohr { 143a1e6784eSAndreas Gohr // get function arguments 144a1e6784eSAndreas Gohr $args = func_get_args(); 1458da7d805SAndreas Gohr $sql = array_shift($args); 146a1e6784eSAndreas Gohr 1478da7d805SAndreas Gohr try { 1488da7d805SAndreas Gohr return $this->adapter->query($sql, $args); 1498da7d805SAndreas Gohr } catch (\Exception $e) { 1508da7d805SAndreas Gohr msg('SQLite: ' . $e->getMessage(), -1); 1518da7d805SAndreas Gohr return false; 1528da7d805SAndreas Gohr } 15387fa2c18Sstretchyboy } 154a1e6784eSAndreas Gohr 155ff97cc8fSstretchyboy 156ff97cc8fSstretchyboy /** 157b122d121SAndreas Gohr * Closes the result set (and it's cursors) 158b122d121SAndreas Gohr * 159b122d121SAndreas Gohr * If you're doing SELECT queries inside a TRANSACTION, be sure to call this 160b122d121SAndreas Gohr * function on all your results sets, before COMMITing the transaction. 161b122d121SAndreas Gohr * 1624b4b2db0SGerrit Uitslag * Also required when not all rows of a result are fetched 1634b4b2db0SGerrit Uitslag * 1648da7d805SAndreas Gohr * @param \PDOStatement $res 165b122d121SAndreas Gohr * @return bool 166b122d121SAndreas Gohr */ 1678da7d805SAndreas Gohr public function res_close($res) 1688da7d805SAndreas Gohr { 1698da7d805SAndreas Gohr if (!$res) return false; 1708da7d805SAndreas Gohr 1718da7d805SAndreas Gohr return $res->closeCursor(); 172b122d121SAndreas Gohr } 173b122d121SAndreas Gohr 174b122d121SAndreas Gohr /** 175aa81d781SKlap-in * Returns a complete result set as array 1768da7d805SAndreas Gohr * 1778da7d805SAndreas Gohr * @param \PDOStatement $res 1788da7d805SAndreas Gohr * @return array 179ff97cc8fSstretchyboy */ 1808da7d805SAndreas Gohr public function res2arr($res, $assoc = true) 1818da7d805SAndreas Gohr { 1828da7d805SAndreas Gohr if (!$res) return []; 1838da7d805SAndreas Gohr 1848da7d805SAndreas Gohr // this is a bullshit workaround for having res2arr and res2count work on one result 1858da7d805SAndreas Gohr if (!$this->data) { 1868da7d805SAndreas Gohr $mode = $assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM; 1878da7d805SAndreas Gohr $this->data = $res->fetchAll($mode); 1888da7d805SAndreas Gohr } 1898da7d805SAndreas Gohr return $this->data; 190b5b947d7SAndreas Gohr } 191b5b947d7SAndreas Gohr 192b5b947d7SAndreas Gohr /** 1938da7d805SAndreas Gohr * Return the next row from the result set as associative array 1948da7d805SAndreas Gohr * 1958da7d805SAndreas Gohr * @param \PDOStatement $res 1968da7d805SAndreas Gohr * @param int $rownum will be ignored 197b5b947d7SAndreas Gohr */ 1988da7d805SAndreas Gohr public function res2row($res, $rownum = 0) 1998da7d805SAndreas Gohr { 2008da7d805SAndreas Gohr if (!$res) return false; 2018da7d805SAndreas Gohr 2028da7d805SAndreas Gohr return $res->fetch(\PDO::FETCH_ASSOC); 203b5b947d7SAndreas Gohr } 204b5b947d7SAndreas Gohr 205e7112ccbSAdrian Lang /** 20644685fc6SKlap-in * Return the first value from the next row. 2078da7d805SAndreas Gohr * 2088da7d805SAndreas Gohr * @param \PDOStatement $res 2098da7d805SAndreas Gohr * @return mixed 210e7112ccbSAdrian Lang */ 2118da7d805SAndreas Gohr public function res2single($res) 2128da7d805SAndreas Gohr { 2138da7d805SAndreas Gohr if (!$res) return false; 2148da7d805SAndreas Gohr 2158da7d805SAndreas Gohr $data = $res->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_ABS, 0); 2168da7d805SAndreas Gohr if (empty($data)) { 2178da7d805SAndreas Gohr return false; 2188da7d805SAndreas Gohr } 2198da7d805SAndreas Gohr return $data[0]; 220e7112ccbSAdrian Lang } 221fee3b689Sstretchyboy 222fee3b689Sstretchyboy /** 223fee3b689Sstretchyboy * fetch the next row as zero indexed array 2248da7d805SAndreas Gohr * 2258da7d805SAndreas Gohr * @param \PDOStatement $res 2268da7d805SAndreas Gohr * @return array|bool 227fee3b689Sstretchyboy */ 2288da7d805SAndreas Gohr public function res_fetch_array($res) 2298da7d805SAndreas Gohr { 2308da7d805SAndreas Gohr if (!$res) return false; 2318da7d805SAndreas Gohr 2328da7d805SAndreas Gohr return $res->fetch(PDO::FETCH_NUM); 23387fa2c18Sstretchyboy } 234fee3b689Sstretchyboy 235fee3b689Sstretchyboy /** 236fee3b689Sstretchyboy * fetch the next row as assocative array 2378da7d805SAndreas Gohr * 2388da7d805SAndreas Gohr * @param \PDOStatement $res 2398da7d805SAndreas Gohr * @return array|bool 240fee3b689Sstretchyboy */ 2418da7d805SAndreas Gohr public function res_fetch_assoc($res) 2428da7d805SAndreas Gohr { 2438da7d805SAndreas Gohr if (!$res) return false; 2448da7d805SAndreas Gohr 2458da7d805SAndreas Gohr return $res->fetch(PDO::FETCH_ASSOC); 246fee3b689Sstretchyboy } 247fee3b689Sstretchyboy 248fee3b689Sstretchyboy /** 24978977d74SKlap-in * Count the number of records in result 2503157674bSAndreas Gohr * 251db58e525SKlap-in * This function is really inperformant in PDO and should be avoided! 2528da7d805SAndreas Gohr * 2538da7d805SAndreas Gohr * @param \PDOStatement $res 2548da7d805SAndreas Gohr * @return int 255fee3b689Sstretchyboy */ 2568da7d805SAndreas Gohr public function res2count($res) 2578da7d805SAndreas Gohr { 2588da7d805SAndreas Gohr if (!$res) return 0; 2598da7d805SAndreas Gohr 2608da7d805SAndreas Gohr // this is a bullshit workaround for having res2arr and res2count work on one result 2618da7d805SAndreas Gohr if (!$this->data) { 2628da7d805SAndreas Gohr $this->data = $this->res2arr($res); 2638da7d805SAndreas Gohr } 2648da7d805SAndreas Gohr 2658da7d805SAndreas Gohr return count($this->data); 266fee3b689Sstretchyboy } 26724a03f6cSstretchyboy 26824a03f6cSstretchyboy /** 26924a03f6cSstretchyboy * Count the number of records changed last time 2708da7d805SAndreas Gohr * 2718da7d805SAndreas Gohr * @param \PDOStatement $res 2728da7d805SAndreas Gohr * @return int 27324a03f6cSstretchyboy */ 2748da7d805SAndreas Gohr public function countChanges($res) 2758da7d805SAndreas Gohr { 2768da7d805SAndreas Gohr if (!$res) return 0; 2778da7d805SAndreas Gohr 2788da7d805SAndreas Gohr return $res->rowCount(); 279a1e6784eSAndreas Gohr } 280a1e6784eSAndreas Gohr 2818da7d805SAndreas Gohr // endregion 2828da7d805SAndreas Gohr 2838da7d805SAndreas Gohr // region quoting/escaping functions 2848da7d805SAndreas Gohr 2858da7d805SAndreas Gohr /** 2868da7d805SAndreas Gohr * Join the given values and quote them for SQL insertion 2878da7d805SAndreas Gohr */ 2888da7d805SAndreas Gohr public function quote_and_join($vals, $sep = ',') 2898da7d805SAndreas Gohr { 2908da7d805SAndreas Gohr $vals = array_map([$this->adapter->pdo(), 'quote'], $vals); 2918da7d805SAndreas Gohr return join($sep, $vals); 2928da7d805SAndreas Gohr } 2938da7d805SAndreas Gohr 2948da7d805SAndreas Gohr /** 2958da7d805SAndreas Gohr * Quotes a string, by escaping it and adding quotes 2968da7d805SAndreas Gohr */ 2978da7d805SAndreas Gohr public function quote_string($string) 2988da7d805SAndreas Gohr { 2998da7d805SAndreas Gohr return $this->adapter->pdo()->quote($string); 3008da7d805SAndreas Gohr } 3018da7d805SAndreas Gohr 3028da7d805SAndreas Gohr /** 3038da7d805SAndreas Gohr * Similar to quote_string, but without the quotes, useful to construct LIKE patterns 3048da7d805SAndreas Gohr */ 3058da7d805SAndreas Gohr public function escape_string($str) 3068da7d805SAndreas Gohr { 3078da7d805SAndreas Gohr return trim($this->adapter->pdo()->quote($str), "'"); 3088da7d805SAndreas Gohr } 3098da7d805SAndreas Gohr 3108da7d805SAndreas Gohr // endregion 3118da7d805SAndreas Gohr 3128da7d805SAndreas Gohr // region speciality functions 3138da7d805SAndreas Gohr 3148da7d805SAndreas Gohr /** 3158da7d805SAndreas Gohr * Split sql queries on semicolons, unless when semicolons are quoted 3168da7d805SAndreas Gohr * 3178da7d805SAndreas Gohr * Usually you don't need this. It's only really needed if you need individual results for 3188da7d805SAndreas Gohr * multiple queries. For example in the admin interface. 3198da7d805SAndreas Gohr * 3208da7d805SAndreas Gohr * @param string $sql 3218da7d805SAndreas Gohr * @return array sql queries 3228da7d805SAndreas Gohr * @deprecated 3238da7d805SAndreas Gohr */ 3248da7d805SAndreas Gohr public function SQLstring2array($sql) 3258da7d805SAndreas Gohr { 326*d0a5ba7aSAndreas Gohr if(!DOKU_UNITTEST) { // for now we don't want to trigger the deprecation warning in the tests 3278da7d805SAndreas Gohr dbg_deprecated(Tools::class . '::SQLstring2array'); 328*d0a5ba7aSAndreas Gohr } 3298da7d805SAndreas Gohr return Tools::SQLstring2array($sql); 3308da7d805SAndreas Gohr } 3318da7d805SAndreas Gohr 3328da7d805SAndreas Gohr /** 3338da7d805SAndreas Gohr * @deprecated needs to be fixed in stuct and structpublish 3348da7d805SAndreas Gohr */ 3358da7d805SAndreas Gohr public function doTransaction($sql, $sqlpreparing = true) { 3368da7d805SAndreas Gohr throw new \Exception( 3378da7d805SAndreas Gohr 'This method seems to never have done what it suggests. Please use the query() function instead.' 3388da7d805SAndreas Gohr ); 3398da7d805SAndreas Gohr } 3408da7d805SAndreas Gohr 3418da7d805SAndreas Gohr // endregion 342aa81d781SKlap-in} 343