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 13*801b921eSSzymon Olewniczak 14*801b921eSSzymon Olewniczak/** 15*801b921eSSzymon Olewniczak * For compatibility with previous adapter implementation. 16*801b921eSSzymon Olewniczak */ 17*801b921eSSzymon Olewniczakif(!defined('DOKU_EXT_PDO')) define('DOKU_EXT_PDO', 'pdo'); 18*801b921eSSzymon Olewniczakclass helper_plugin_sqlite_adapter_dummy 19*801b921eSSzymon Olewniczak{ 20*801b921eSSzymon Olewniczak public function getName() { 21*801b921eSSzymon Olewniczak return DOKU_EXT_PDO; 22*801b921eSSzymon Olewniczak } 23*801b921eSSzymon Olewniczak 24*801b921eSSzymon Olewniczak public function setUseNativeAlter($set) {} 25*801b921eSSzymon Olewniczak} 26*801b921eSSzymon Olewniczak 27a1e6784eSAndreas Gohr/** 28a1e6784eSAndreas Gohr * DokuWiki Plugin sqlite (Helper Component) 29a1e6784eSAndreas Gohr * 30a1e6784eSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 31a1e6784eSAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de> 328da7d805SAndreas Gohr * @deprecated 2023-03-15 33a1e6784eSAndreas Gohr */ 348da7d805SAndreas Gohrclass helper_plugin_sqlite extends DokuWiki_Plugin 358da7d805SAndreas Gohr{ 368da7d805SAndreas Gohr /** @var SQLiteDB|null */ 376c7ee3f2SAndreas Gohr protected $adapter = null; 38aa81d781SKlap-in 398da7d805SAndreas Gohr /** @var array result cache */ 408da7d805SAndreas Gohr protected $data; 418da7d805SAndreas Gohr 423e9ac593SGerrit Uitslag /** 438da7d805SAndreas Gohr * constructor 443e9ac593SGerrit Uitslag */ 458da7d805SAndreas Gohr public function __construct() 468da7d805SAndreas Gohr { 478da7d805SAndreas Gohr if (!$this->existsPDOSqlite()) { 488da7d805SAndreas Gohr msg('PDO SQLite support missing in this PHP install - The sqlite plugin will not work', -1); 498da7d805SAndreas Gohr } 50*801b921eSSzymon Olewniczak $this->adapter = new helper_plugin_sqlite_adapter_dummy(); 518da7d805SAndreas Gohr } 528da7d805SAndreas Gohr 538da7d805SAndreas Gohr /** 548da7d805SAndreas Gohr * Get the current Adapter 558da7d805SAndreas Gohr * @return SQLiteDB|null 568da7d805SAndreas Gohr */ 578da7d805SAndreas Gohr public function getAdapter() 588da7d805SAndreas Gohr { 59ca2f2adaSKlap-in return $this->adapter; 60ca2f2adaSKlap-in } 61ca2f2adaSKlap-in 62a1e6784eSAndreas Gohr /** 63aa81d781SKlap-in * Keep separate instances for every call to keep database connections 64aa81d781SKlap-in */ 658da7d805SAndreas Gohr public function isSingleton() 668da7d805SAndreas Gohr { 67aa81d781SKlap-in return false; 68aa81d781SKlap-in } 69aa81d781SKlap-in 70aa81d781SKlap-in /** 71aa81d781SKlap-in * check availabilty of PHP PDO sqlite3 72aa81d781SKlap-in */ 738da7d805SAndreas Gohr public function existsPDOSqlite() 748da7d805SAndreas Gohr { 7587fa2c18Sstretchyboy if (class_exists('pdo')) { 768da7d805SAndreas Gohr return in_array('sqlite', \PDO::getAvailableDrivers()); 777ed6069fSAdrian Lang } 78aa81d781SKlap-in return false; 79a1e6784eSAndreas Gohr } 80a1e6784eSAndreas Gohr 81a1e6784eSAndreas Gohr /** 82a1e6784eSAndreas Gohr * Initializes and opens the database 83a1e6784eSAndreas Gohr * 84a1e6784eSAndreas Gohr * Needs to be called right after loading this helper plugin 85aa81d781SKlap-in * 86aa81d781SKlap-in * @param string $dbname 87aa81d781SKlap-in * @param string $updatedir - Database update infos 88aa81d781SKlap-in * @return bool 89a1e6784eSAndreas Gohr */ 908da7d805SAndreas Gohr public function init($dbname, $updatedir) 918da7d805SAndreas Gohr { 92*801b921eSSzymon Olewniczak if(!defined('DOKU_UNITTEST')) { // for now we don't want to trigger the deprecation warning in the tests 933a56750bSAndreas Gohr dbg_deprecated(SQLiteDB::class); 94d0a5ba7aSAndreas Gohr } 953a56750bSAndreas Gohr 968da7d805SAndreas Gohr try { 978da7d805SAndreas Gohr $this->adapter = new SQLiteDB($dbname, $updatedir, $this); 988da7d805SAndreas Gohr } catch (Exception $e) { 998da7d805SAndreas Gohr msg('SQLite: ' . $e->getMessage(), -1); 100c137e95fSAndreas Gohr return false; 101c137e95fSAndreas Gohr } 102a1e6784eSAndreas Gohr return true; 103a1e6784eSAndreas Gohr } 104a1e6784eSAndreas Gohr 105a1e6784eSAndreas Gohr /** 1068da7d805SAndreas Gohr * This is called from the adapter itself for backwards compatibility 1078da7d805SAndreas Gohr * 1088da7d805SAndreas Gohr * @param SQLiteDB $adapter 1098da7d805SAndreas Gohr * @return void 110a1e6784eSAndreas Gohr */ 1118da7d805SAndreas Gohr function setAdapter($adapter) 1128da7d805SAndreas Gohr { 1138da7d805SAndreas Gohr $this->adapter = $adapter; 114a34ef333SKlap-in } 115f10ea6c1SKlap-in 116a1e6784eSAndreas Gohr /** 1173ae3f79eSKlap-in * Registers a User Defined Function for use in SQL statements 1183ae3f79eSKlap-in */ 1198da7d805SAndreas Gohr public function create_function($function_name, $callback, $num_args) 1208da7d805SAndreas Gohr { 1218da7d805SAndreas Gohr $this->adapter->pdo()->sqliteCreateFunction($function_name, $callback, $num_args); 1223ae3f79eSKlap-in } 1233ae3f79eSKlap-in 1248da7d805SAndreas Gohr // region query and result handling functions 1258da7d805SAndreas Gohr 1263ae3f79eSKlap-in /** 127e7b0736cSAndreas Gohr * Convenience function to run an INSERT OR REPLACE operation 128e7b0736cSAndreas Gohr * 129e7b0736cSAndreas Gohr * The function takes a key-value array with the column names in the key and the actual value in the value, 130e7b0736cSAndreas Gohr * build the appropriate query and executes it. 131e7b0736cSAndreas Gohr * 132e7b0736cSAndreas Gohr * @param string $table the table the entry should be saved to (will not be escaped) 133e7b0736cSAndreas Gohr * @param array $entry A simple key-value pair array (only values will be escaped) 1348da7d805SAndreas Gohr * @return bool 135e7b0736cSAndreas Gohr */ 1368da7d805SAndreas Gohr public function storeEntry($table, $entry) 1378da7d805SAndreas Gohr { 1388da7d805SAndreas Gohr try { 1398da7d805SAndreas Gohr $this->adapter->saveRecord($table, $entry); 1408da7d805SAndreas Gohr } catch (\Exception $e) { 1418da7d805SAndreas Gohr msg('SQLite: ' . $e->getMessage(), -1); 1428da7d805SAndreas Gohr return false; 143e7b0736cSAndreas Gohr } 144e7b0736cSAndreas Gohr 1458da7d805SAndreas Gohr return true; 1468da7d805SAndreas Gohr } 147e7b0736cSAndreas Gohr 148e7b0736cSAndreas Gohr /** 149a1e6784eSAndreas Gohr * Execute a query with the given parameters. 150a1e6784eSAndreas Gohr * 151a1e6784eSAndreas Gohr * Takes care of escaping 152a1e6784eSAndreas Gohr * 153a2a82480SAndreas Gohr * 154a2a82480SAndreas Gohr * @param string ...$args - the arguments of query(), the first is the sql and others are values 155a1e6784eSAndreas Gohr */ 1568da7d805SAndreas Gohr public function query() 1578da7d805SAndreas Gohr { 158a1e6784eSAndreas Gohr // get function arguments 159a1e6784eSAndreas Gohr $args = func_get_args(); 1608da7d805SAndreas Gohr $sql = array_shift($args); 161a1e6784eSAndreas Gohr 1628da7d805SAndreas Gohr try { 1638da7d805SAndreas Gohr return $this->adapter->query($sql, $args); 1648da7d805SAndreas Gohr } catch (\Exception $e) { 1658da7d805SAndreas Gohr msg('SQLite: ' . $e->getMessage(), -1); 1668da7d805SAndreas Gohr return false; 1678da7d805SAndreas Gohr } 16887fa2c18Sstretchyboy } 169a1e6784eSAndreas Gohr 170ff97cc8fSstretchyboy 171ff97cc8fSstretchyboy /** 172b122d121SAndreas Gohr * Closes the result set (and it's cursors) 173b122d121SAndreas Gohr * 174b122d121SAndreas Gohr * If you're doing SELECT queries inside a TRANSACTION, be sure to call this 175b122d121SAndreas Gohr * function on all your results sets, before COMMITing the transaction. 176b122d121SAndreas Gohr * 1774b4b2db0SGerrit Uitslag * Also required when not all rows of a result are fetched 1784b4b2db0SGerrit Uitslag * 1798da7d805SAndreas Gohr * @param \PDOStatement $res 180b122d121SAndreas Gohr * @return bool 181b122d121SAndreas Gohr */ 1828da7d805SAndreas Gohr public function res_close($res) 1838da7d805SAndreas Gohr { 1848da7d805SAndreas Gohr if (!$res) return false; 1858da7d805SAndreas Gohr 1868da7d805SAndreas Gohr return $res->closeCursor(); 187b122d121SAndreas Gohr } 188b122d121SAndreas Gohr 189b122d121SAndreas Gohr /** 190aa81d781SKlap-in * Returns a complete result set as array 1918da7d805SAndreas Gohr * 1928da7d805SAndreas Gohr * @param \PDOStatement $res 1938da7d805SAndreas Gohr * @return array 194ff97cc8fSstretchyboy */ 1958da7d805SAndreas Gohr public function res2arr($res, $assoc = true) 1968da7d805SAndreas Gohr { 1978da7d805SAndreas Gohr if (!$res) return []; 1988da7d805SAndreas Gohr 1998da7d805SAndreas Gohr // this is a bullshit workaround for having res2arr and res2count work on one result 2008da7d805SAndreas Gohr if (!$this->data) { 2018da7d805SAndreas Gohr $mode = $assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM; 2028da7d805SAndreas Gohr $this->data = $res->fetchAll($mode); 2038da7d805SAndreas Gohr } 2048da7d805SAndreas Gohr return $this->data; 205b5b947d7SAndreas Gohr } 206b5b947d7SAndreas Gohr 207b5b947d7SAndreas Gohr /** 2088da7d805SAndreas Gohr * Return the next row from the result set as associative array 2098da7d805SAndreas Gohr * 2108da7d805SAndreas Gohr * @param \PDOStatement $res 2118da7d805SAndreas Gohr * @param int $rownum will be ignored 212b5b947d7SAndreas Gohr */ 2138da7d805SAndreas Gohr public function res2row($res, $rownum = 0) 2148da7d805SAndreas Gohr { 2158da7d805SAndreas Gohr if (!$res) return false; 2168da7d805SAndreas Gohr 2178da7d805SAndreas Gohr return $res->fetch(\PDO::FETCH_ASSOC); 218b5b947d7SAndreas Gohr } 219b5b947d7SAndreas Gohr 220e7112ccbSAdrian Lang /** 22144685fc6SKlap-in * Return the first value from the next row. 2228da7d805SAndreas Gohr * 2238da7d805SAndreas Gohr * @param \PDOStatement $res 2248da7d805SAndreas Gohr * @return mixed 225e7112ccbSAdrian Lang */ 2268da7d805SAndreas Gohr public function res2single($res) 2278da7d805SAndreas Gohr { 2288da7d805SAndreas Gohr if (!$res) return false; 2298da7d805SAndreas Gohr 2308da7d805SAndreas Gohr $data = $res->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_ABS, 0); 2318da7d805SAndreas Gohr if (empty($data)) { 2328da7d805SAndreas Gohr return false; 2338da7d805SAndreas Gohr } 2348da7d805SAndreas Gohr return $data[0]; 235e7112ccbSAdrian Lang } 236fee3b689Sstretchyboy 237fee3b689Sstretchyboy /** 238fee3b689Sstretchyboy * fetch the next row as zero indexed array 2398da7d805SAndreas Gohr * 2408da7d805SAndreas Gohr * @param \PDOStatement $res 2418da7d805SAndreas Gohr * @return array|bool 242fee3b689Sstretchyboy */ 2438da7d805SAndreas Gohr public function res_fetch_array($res) 2448da7d805SAndreas Gohr { 2458da7d805SAndreas Gohr if (!$res) return false; 2468da7d805SAndreas Gohr 2478da7d805SAndreas Gohr return $res->fetch(PDO::FETCH_NUM); 24887fa2c18Sstretchyboy } 249fee3b689Sstretchyboy 250fee3b689Sstretchyboy /** 251fee3b689Sstretchyboy * fetch the next row as assocative array 2528da7d805SAndreas Gohr * 2538da7d805SAndreas Gohr * @param \PDOStatement $res 2548da7d805SAndreas Gohr * @return array|bool 255fee3b689Sstretchyboy */ 2568da7d805SAndreas Gohr public function res_fetch_assoc($res) 2578da7d805SAndreas Gohr { 2588da7d805SAndreas Gohr if (!$res) return false; 2598da7d805SAndreas Gohr 2608da7d805SAndreas Gohr return $res->fetch(PDO::FETCH_ASSOC); 261fee3b689Sstretchyboy } 262fee3b689Sstretchyboy 263fee3b689Sstretchyboy /** 26478977d74SKlap-in * Count the number of records in result 2653157674bSAndreas Gohr * 266db58e525SKlap-in * This function is really inperformant in PDO and should be avoided! 2678da7d805SAndreas Gohr * 2688da7d805SAndreas Gohr * @param \PDOStatement $res 2698da7d805SAndreas Gohr * @return int 270fee3b689Sstretchyboy */ 2718da7d805SAndreas Gohr public function res2count($res) 2728da7d805SAndreas Gohr { 2738da7d805SAndreas Gohr if (!$res) return 0; 2748da7d805SAndreas Gohr 2758da7d805SAndreas Gohr // this is a bullshit workaround for having res2arr and res2count work on one result 2768da7d805SAndreas Gohr if (!$this->data) { 2778da7d805SAndreas Gohr $this->data = $this->res2arr($res); 2788da7d805SAndreas Gohr } 2798da7d805SAndreas Gohr 2808da7d805SAndreas Gohr return count($this->data); 281fee3b689Sstretchyboy } 28224a03f6cSstretchyboy 28324a03f6cSstretchyboy /** 28424a03f6cSstretchyboy * Count the number of records changed last time 2858da7d805SAndreas Gohr * 2868da7d805SAndreas Gohr * @param \PDOStatement $res 2878da7d805SAndreas Gohr * @return int 28824a03f6cSstretchyboy */ 2898da7d805SAndreas Gohr public function countChanges($res) 2908da7d805SAndreas Gohr { 2918da7d805SAndreas Gohr if (!$res) return 0; 2928da7d805SAndreas Gohr 2938da7d805SAndreas Gohr return $res->rowCount(); 294a1e6784eSAndreas Gohr } 295a1e6784eSAndreas Gohr 2968da7d805SAndreas Gohr // endregion 2978da7d805SAndreas Gohr 2988da7d805SAndreas Gohr // region quoting/escaping functions 2998da7d805SAndreas Gohr 3008da7d805SAndreas Gohr /** 3018da7d805SAndreas Gohr * Join the given values and quote them for SQL insertion 3028da7d805SAndreas Gohr */ 3038da7d805SAndreas Gohr public function quote_and_join($vals, $sep = ',') 3048da7d805SAndreas Gohr { 3058da7d805SAndreas Gohr $vals = array_map([$this->adapter->pdo(), 'quote'], $vals); 3068da7d805SAndreas Gohr return join($sep, $vals); 3078da7d805SAndreas Gohr } 3088da7d805SAndreas Gohr 3098da7d805SAndreas Gohr /** 3108da7d805SAndreas Gohr * Quotes a string, by escaping it and adding quotes 3118da7d805SAndreas Gohr */ 3128da7d805SAndreas Gohr public function quote_string($string) 3138da7d805SAndreas Gohr { 3148da7d805SAndreas Gohr return $this->adapter->pdo()->quote($string); 3158da7d805SAndreas Gohr } 3168da7d805SAndreas Gohr 3178da7d805SAndreas Gohr /** 3188da7d805SAndreas Gohr * Similar to quote_string, but without the quotes, useful to construct LIKE patterns 3198da7d805SAndreas Gohr */ 3208da7d805SAndreas Gohr public function escape_string($str) 3218da7d805SAndreas Gohr { 3228da7d805SAndreas Gohr return trim($this->adapter->pdo()->quote($str), "'"); 3238da7d805SAndreas Gohr } 3248da7d805SAndreas Gohr 3258da7d805SAndreas Gohr // endregion 3268da7d805SAndreas Gohr 3278da7d805SAndreas Gohr // region speciality functions 3288da7d805SAndreas Gohr 3298da7d805SAndreas Gohr /** 3308da7d805SAndreas Gohr * Split sql queries on semicolons, unless when semicolons are quoted 3318da7d805SAndreas Gohr * 3328da7d805SAndreas Gohr * Usually you don't need this. It's only really needed if you need individual results for 3338da7d805SAndreas Gohr * multiple queries. For example in the admin interface. 3348da7d805SAndreas Gohr * 3358da7d805SAndreas Gohr * @param string $sql 3368da7d805SAndreas Gohr * @return array sql queries 3378da7d805SAndreas Gohr * @deprecated 3388da7d805SAndreas Gohr */ 3398da7d805SAndreas Gohr public function SQLstring2array($sql) 3408da7d805SAndreas Gohr { 341d0a5ba7aSAndreas Gohr if(!DOKU_UNITTEST) { // for now we don't want to trigger the deprecation warning in the tests 3428da7d805SAndreas Gohr dbg_deprecated(Tools::class . '::SQLstring2array'); 343d0a5ba7aSAndreas Gohr } 3448da7d805SAndreas Gohr return Tools::SQLstring2array($sql); 3458da7d805SAndreas Gohr } 3468da7d805SAndreas Gohr 3478da7d805SAndreas Gohr /** 3488da7d805SAndreas Gohr * @deprecated needs to be fixed in stuct and structpublish 3498da7d805SAndreas Gohr */ 3508da7d805SAndreas Gohr public function doTransaction($sql, $sqlpreparing = true) { 3518da7d805SAndreas Gohr throw new \Exception( 3528da7d805SAndreas Gohr 'This method seems to never have done what it suggests. Please use the query() function instead.' 3538da7d805SAndreas Gohr ); 3548da7d805SAndreas Gohr } 3558da7d805SAndreas Gohr 3568da7d805SAndreas Gohr // endregion 357aa81d781SKlap-in} 358