xref: /plugin/sqlite/helper.php (revision 14fe8e279910db76dc1446ba0e4f688190343c1a)
1a1e6784eSAndreas Gohr<?php
28da7d805SAndreas Gohr
38da7d805SAndreas Gohr/**
48da7d805SAndreas Gohr * @noinspection SqlNoDataSourceInspection
58da7d805SAndreas Gohr * @noinspection SqlDialectInspection
68da7d805SAndreas Gohr * @noinspection PhpComposerExtensionStubsInspection
78da7d805SAndreas Gohr */
88da7d805SAndreas Gohr
9a7a40fb2SAnna Dabrowskause dokuwiki\Extension\Plugin;
108da7d805SAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB;
118da7d805SAndreas Gohruse dokuwiki\plugin\sqlite\Tools;
128da7d805SAndreas Gohr
13*14fe8e27SAnna Dabrowska// phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols, PSR1.Classes.ClassDeclaration.MultipleClasses
14*14fe8e27SAnna Dabrowska// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
15*14fe8e27SAnna Dabrowska
16801b921eSSzymon Olewniczak/**
17801b921eSSzymon Olewniczak * For compatibility with previous adapter implementation.
18801b921eSSzymon Olewniczak */
19801b921eSSzymon Olewniczakif (!defined('DOKU_EXT_PDO')) define('DOKU_EXT_PDO', 'pdo');
20801b921eSSzymon Olewniczakclass helper_plugin_sqlite_adapter_dummy
21801b921eSSzymon Olewniczak{
22a7a40fb2SAnna Dabrowska    public function getName()
23a7a40fb2SAnna Dabrowska    {
24801b921eSSzymon Olewniczak        return DOKU_EXT_PDO;
25801b921eSSzymon Olewniczak    }
26801b921eSSzymon Olewniczak
27a7a40fb2SAnna Dabrowska    public function setUseNativeAlter($set)
28a7a40fb2SAnna Dabrowska    {
29a7a40fb2SAnna Dabrowska    }
30801b921eSSzymon Olewniczak}
31801b921eSSzymon Olewniczak
32a1e6784eSAndreas Gohr/**
33a1e6784eSAndreas Gohr * DokuWiki Plugin sqlite (Helper Component)
34a1e6784eSAndreas Gohr *
35a1e6784eSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
36a1e6784eSAndreas Gohr * @author  Andreas Gohr <gohr@cosmocode.de>
378da7d805SAndreas Gohr * @deprecated 2023-03-15
38a1e6784eSAndreas Gohr */
39a7a40fb2SAnna Dabrowskaclass helper_plugin_sqlite extends Plugin
408da7d805SAndreas Gohr{
418da7d805SAndreas Gohr    /** @var SQLiteDB|null */
42a7a40fb2SAnna Dabrowska    protected $adapter;
43aa81d781SKlap-in
448da7d805SAndreas Gohr    /** @var array result cache */
458da7d805SAndreas Gohr    protected $data;
468da7d805SAndreas Gohr
473e9ac593SGerrit Uitslag    /**
488da7d805SAndreas Gohr     * constructor
493e9ac593SGerrit Uitslag     */
508da7d805SAndreas Gohr    public function __construct()
518da7d805SAndreas Gohr    {
528da7d805SAndreas Gohr        if (!$this->existsPDOSqlite()) {
538da7d805SAndreas Gohr            msg('PDO SQLite support missing in this PHP install - The sqlite plugin will not work', -1);
548da7d805SAndreas Gohr        }
55801b921eSSzymon Olewniczak        $this->adapter = new helper_plugin_sqlite_adapter_dummy();
568da7d805SAndreas Gohr    }
578da7d805SAndreas Gohr
588da7d805SAndreas Gohr    /**
598da7d805SAndreas Gohr     * Get the current Adapter
608da7d805SAndreas Gohr     * @return SQLiteDB|null
618da7d805SAndreas Gohr     */
628da7d805SAndreas Gohr    public function getAdapter()
638da7d805SAndreas Gohr    {
64ca2f2adaSKlap-in        return $this->adapter;
65ca2f2adaSKlap-in    }
66ca2f2adaSKlap-in
67a1e6784eSAndreas Gohr    /**
68aa81d781SKlap-in     * Keep separate instances for every call to keep database connections
69aa81d781SKlap-in     */
708da7d805SAndreas Gohr    public function isSingleton()
718da7d805SAndreas Gohr    {
72aa81d781SKlap-in        return false;
73aa81d781SKlap-in    }
74aa81d781SKlap-in
75aa81d781SKlap-in    /**
76aa81d781SKlap-in     * check availabilty of PHP PDO sqlite3
77aa81d781SKlap-in     */
788da7d805SAndreas Gohr    public function existsPDOSqlite()
798da7d805SAndreas Gohr    {
8087fa2c18Sstretchyboy        if (class_exists('pdo')) {
818da7d805SAndreas Gohr            return in_array('sqlite', \PDO::getAvailableDrivers());
827ed6069fSAdrian Lang        }
83aa81d781SKlap-in        return false;
84a1e6784eSAndreas Gohr    }
85a1e6784eSAndreas Gohr
86a1e6784eSAndreas Gohr    /**
87a1e6784eSAndreas Gohr     * Initializes and opens the database
88a1e6784eSAndreas Gohr     *
89a1e6784eSAndreas Gohr     * Needs to be called right after loading this helper plugin
90aa81d781SKlap-in     *
91aa81d781SKlap-in     * @param string $dbname
92aa81d781SKlap-in     * @param string $updatedir - Database update infos
93aa81d781SKlap-in     * @return bool
94a1e6784eSAndreas Gohr     */
958da7d805SAndreas Gohr    public function init($dbname, $updatedir)
968da7d805SAndreas Gohr    {
97801b921eSSzymon Olewniczak        if (!defined('DOKU_UNITTEST')) { // for now we don't want to trigger the deprecation warning in the tests
983a56750bSAndreas Gohr            dbg_deprecated(SQLiteDB::class);
99d0a5ba7aSAndreas Gohr        }
1003a56750bSAndreas Gohr
1018da7d805SAndreas Gohr        try {
1028da7d805SAndreas Gohr            $this->adapter = new SQLiteDB($dbname, $updatedir, $this);
1038da7d805SAndreas Gohr        } catch (Exception $e) {
1048da7d805SAndreas Gohr            msg('SQLite: ' . $e->getMessage(), -1);
105c137e95fSAndreas Gohr            return false;
106c137e95fSAndreas Gohr        }
107a1e6784eSAndreas Gohr        return true;
108a1e6784eSAndreas Gohr    }
109a1e6784eSAndreas Gohr
110a1e6784eSAndreas Gohr    /**
1118da7d805SAndreas Gohr     * This is called from the adapter itself for backwards compatibility
1128da7d805SAndreas Gohr     *
1138da7d805SAndreas Gohr     * @param SQLiteDB $adapter
1148da7d805SAndreas Gohr     * @return void
115a1e6784eSAndreas Gohr     */
116a7a40fb2SAnna Dabrowska    public function setAdapter($adapter)
1178da7d805SAndreas Gohr    {
1188da7d805SAndreas Gohr        $this->adapter = $adapter;
119a34ef333SKlap-in    }
120f10ea6c1SKlap-in
121a1e6784eSAndreas Gohr    /**
1223ae3f79eSKlap-in     * Registers a User Defined Function for use in SQL statements
1233ae3f79eSKlap-in     */
1248da7d805SAndreas Gohr    public function create_function($function_name, $callback, $num_args)
1258da7d805SAndreas Gohr    {
12610cb854aSAndreas Gohr        $this->adapter->getPdo()->sqliteCreateFunction($function_name, $callback, $num_args);
1273ae3f79eSKlap-in    }
1283ae3f79eSKlap-in
1298da7d805SAndreas Gohr    // region query and result handling functions
1308da7d805SAndreas Gohr
1313ae3f79eSKlap-in    /**
132e7b0736cSAndreas Gohr     * Convenience function to run an INSERT OR REPLACE operation
133e7b0736cSAndreas Gohr     *
134e7b0736cSAndreas Gohr     * The function takes a key-value array with the column names in the key and the actual value in the value,
135e7b0736cSAndreas Gohr     * build the appropriate query and executes it.
136e7b0736cSAndreas Gohr     *
137e7b0736cSAndreas Gohr     * @param string $table the table the entry should be saved to (will not be escaped)
138e7b0736cSAndreas Gohr     * @param array $entry A simple key-value pair array (only values will be escaped)
1398da7d805SAndreas Gohr     * @return bool
140e7b0736cSAndreas Gohr     */
1418da7d805SAndreas Gohr    public function storeEntry($table, $entry)
1428da7d805SAndreas Gohr    {
1438da7d805SAndreas Gohr        try {
1448da7d805SAndreas Gohr            $this->adapter->saveRecord($table, $entry);
1458da7d805SAndreas Gohr        } catch (\Exception $e) {
1468da7d805SAndreas Gohr            msg('SQLite: ' . $e->getMessage(), -1);
1478da7d805SAndreas Gohr            return false;
148e7b0736cSAndreas Gohr        }
149e7b0736cSAndreas Gohr
1508da7d805SAndreas Gohr        return true;
1518da7d805SAndreas Gohr    }
152e7b0736cSAndreas Gohr
153e7b0736cSAndreas Gohr    /**
154a1e6784eSAndreas Gohr     * Execute a query with the given parameters.
155a1e6784eSAndreas Gohr     *
156a1e6784eSAndreas Gohr     * Takes care of escaping
157a1e6784eSAndreas Gohr     *
158a2a82480SAndreas Gohr     *
159a2a82480SAndreas Gohr     * @param string ...$args - the arguments of query(), the first is the sql and others are values
160a1e6784eSAndreas Gohr     */
1618da7d805SAndreas Gohr    public function query()
1628da7d805SAndreas Gohr    {
163a1e6784eSAndreas Gohr        // get function arguments
164a1e6784eSAndreas Gohr        $args = func_get_args();
1656917135cSSzymon Olewniczak
1666917135cSSzymon Olewniczak        // clear the cache
1676917135cSSzymon Olewniczak        $this->data = null;
1686917135cSSzymon Olewniczak
1698da7d805SAndreas Gohr        try {
1701973f122SSzymon Olewniczak            $sql = $this->prepareSql($args);
1711973f122SSzymon Olewniczak            return $this->adapter->query($sql);
1728da7d805SAndreas Gohr        } catch (\Exception $e) {
1738da7d805SAndreas Gohr            msg('SQLite: ' . $e->getMessage(), -1);
1748da7d805SAndreas Gohr            return false;
1758da7d805SAndreas Gohr        }
17687fa2c18Sstretchyboy    }
177a1e6784eSAndreas Gohr
1781973f122SSzymon Olewniczak    /**
1791973f122SSzymon Olewniczak     * Prepare a query with the given arguments.
1801973f122SSzymon Olewniczak     *
1811973f122SSzymon Olewniczak     * Takes care of escaping
1821973f122SSzymon Olewniczak     *
1831973f122SSzymon Olewniczak     * @param array $args
1841973f122SSzymon Olewniczak     *    array of arguments:
1851973f122SSzymon Olewniczak     *      - string $sql - the statement
1861973f122SSzymon Olewniczak     *      - arguments...
1871973f122SSzymon Olewniczak     * @return bool|string
1881973f122SSzymon Olewniczak     * @throws Exception
1891973f122SSzymon Olewniczak     */
190a7a40fb2SAnna Dabrowska    public function prepareSql($args)
191a7a40fb2SAnna Dabrowska    {
1921973f122SSzymon Olewniczak
1931973f122SSzymon Olewniczak        $sql = trim(array_shift($args));
1941973f122SSzymon Olewniczak        $sql = rtrim($sql, ';');
1951973f122SSzymon Olewniczak
1961973f122SSzymon Olewniczak        if (!$sql) {
1971973f122SSzymon Olewniczak            throw new \Exception('No SQL statement given', -1);
1981973f122SSzymon Olewniczak        }
1991973f122SSzymon Olewniczak
2001973f122SSzymon Olewniczak        $argc = count($args);
2011973f122SSzymon Olewniczak        if ($argc > 0 && is_array($args[0])) {
2021973f122SSzymon Olewniczak            $args = $args[0];
2031973f122SSzymon Olewniczak            $argc = count($args);
2041973f122SSzymon Olewniczak        }
2051973f122SSzymon Olewniczak
2061973f122SSzymon Olewniczak        // check number of arguments
2071973f122SSzymon Olewniczak        $qmc = substr_count($sql, '?');
2081973f122SSzymon Olewniczak        if ($argc < $qmc) {
2091973f122SSzymon Olewniczak            throw new \Exception('Not enough arguments passed for statement. ' .
2101973f122SSzymon Olewniczak                'Expected ' . $qmc . ' got ' . $argc . ' - ' . hsc($sql));
2111973f122SSzymon Olewniczak        } elseif ($argc > $qmc) {
2121973f122SSzymon Olewniczak            throw new \Exception('Too much arguments passed for statement. ' .
2131973f122SSzymon Olewniczak                'Expected ' . $qmc . ' got ' . $argc . ' - ' . hsc($sql));
2141973f122SSzymon Olewniczak        }
2151973f122SSzymon Olewniczak
2161973f122SSzymon Olewniczak        // explode at wildcard, then join again
2171973f122SSzymon Olewniczak        $parts = explode('?', $sql, $argc + 1);
21810cb854aSAndreas Gohr        $args  = array_map([$this->adapter->getPdo(), 'quote'], $args);
2191973f122SSzymon Olewniczak        $sql   = '';
2201973f122SSzymon Olewniczak
2211973f122SSzymon Olewniczak        while (($part = array_shift($parts)) !== null) {
2221973f122SSzymon Olewniczak            $sql .= $part;
2231973f122SSzymon Olewniczak            $sql .= array_shift($args);
2241973f122SSzymon Olewniczak        }
2251973f122SSzymon Olewniczak
2261973f122SSzymon Olewniczak        return $sql;
2271973f122SSzymon Olewniczak    }
2281973f122SSzymon Olewniczak
229ff97cc8fSstretchyboy
230ff97cc8fSstretchyboy    /**
231b122d121SAndreas Gohr     * Closes the result set (and it's cursors)
232b122d121SAndreas Gohr     *
233b122d121SAndreas Gohr     * If you're doing SELECT queries inside a TRANSACTION, be sure to call this
234b122d121SAndreas Gohr     * function on all your results sets, before COMMITing the transaction.
235b122d121SAndreas Gohr     *
2364b4b2db0SGerrit Uitslag     * Also required when not all rows of a result are fetched
2374b4b2db0SGerrit Uitslag     *
2388da7d805SAndreas Gohr     * @param \PDOStatement $res
239b122d121SAndreas Gohr     * @return bool
240b122d121SAndreas Gohr     */
2418da7d805SAndreas Gohr    public function res_close($res)
2428da7d805SAndreas Gohr    {
2438da7d805SAndreas Gohr        if (!$res) return false;
2448da7d805SAndreas Gohr
2458da7d805SAndreas Gohr        return $res->closeCursor();
246b122d121SAndreas Gohr    }
247b122d121SAndreas Gohr
248b122d121SAndreas Gohr    /**
249aa81d781SKlap-in     * Returns a complete result set as array
2508da7d805SAndreas Gohr     *
2518da7d805SAndreas Gohr     * @param \PDOStatement $res
2528da7d805SAndreas Gohr     * @return array
253ff97cc8fSstretchyboy     */
2548da7d805SAndreas Gohr    public function res2arr($res, $assoc = true)
2558da7d805SAndreas Gohr    {
2568da7d805SAndreas Gohr        if (!$res) return [];
2578da7d805SAndreas Gohr
2588da7d805SAndreas Gohr        // this is a bullshit workaround for having res2arr and res2count work on one result
2598da7d805SAndreas Gohr        if (!$this->data) {
2608da7d805SAndreas Gohr            $mode = $assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM;
2618da7d805SAndreas Gohr            $this->data = $res->fetchAll($mode);
2628da7d805SAndreas Gohr        }
2638da7d805SAndreas Gohr        return $this->data;
264b5b947d7SAndreas Gohr    }
265b5b947d7SAndreas Gohr
266b5b947d7SAndreas Gohr    /**
2678da7d805SAndreas Gohr     * Return the next row from the result set as associative array
2688da7d805SAndreas Gohr     *
2698da7d805SAndreas Gohr     * @param \PDOStatement $res
2708da7d805SAndreas Gohr     * @param int $rownum will be ignored
271b5b947d7SAndreas Gohr     */
2728da7d805SAndreas Gohr    public function res2row($res, $rownum = 0)
2738da7d805SAndreas Gohr    {
2748da7d805SAndreas Gohr        if (!$res) return false;
2758da7d805SAndreas Gohr
2768da7d805SAndreas Gohr        return $res->fetch(\PDO::FETCH_ASSOC);
277b5b947d7SAndreas Gohr    }
278b5b947d7SAndreas Gohr
279e7112ccbSAdrian Lang    /**
28044685fc6SKlap-in     * Return the first value from the next row.
2818da7d805SAndreas Gohr     *
2828da7d805SAndreas Gohr     * @param \PDOStatement $res
2838da7d805SAndreas Gohr     * @return mixed
284e7112ccbSAdrian Lang     */
2858da7d805SAndreas Gohr    public function res2single($res)
2868da7d805SAndreas Gohr    {
2878da7d805SAndreas Gohr        if (!$res) return false;
2888da7d805SAndreas Gohr
2898da7d805SAndreas Gohr        $data = $res->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_ABS, 0);
2908da7d805SAndreas Gohr        if (empty($data)) {
2918da7d805SAndreas Gohr            return false;
2928da7d805SAndreas Gohr        }
2938da7d805SAndreas Gohr        return $data[0];
294e7112ccbSAdrian Lang    }
295fee3b689Sstretchyboy
296fee3b689Sstretchyboy    /**
297fee3b689Sstretchyboy     * fetch the next row as zero indexed array
2988da7d805SAndreas Gohr     *
2998da7d805SAndreas Gohr     * @param \PDOStatement $res
3008da7d805SAndreas Gohr     * @return array|bool
301fee3b689Sstretchyboy     */
3028da7d805SAndreas Gohr    public function res_fetch_array($res)
3038da7d805SAndreas Gohr    {
3048da7d805SAndreas Gohr        if (!$res) return false;
3058da7d805SAndreas Gohr
3068da7d805SAndreas Gohr        return $res->fetch(PDO::FETCH_NUM);
30787fa2c18Sstretchyboy    }
308fee3b689Sstretchyboy
309fee3b689Sstretchyboy    /**
310fee3b689Sstretchyboy     * fetch the next row as assocative array
3118da7d805SAndreas Gohr     *
3128da7d805SAndreas Gohr     * @param \PDOStatement $res
3138da7d805SAndreas Gohr     * @return array|bool
314fee3b689Sstretchyboy     */
3158da7d805SAndreas Gohr    public function res_fetch_assoc($res)
3168da7d805SAndreas Gohr    {
3178da7d805SAndreas Gohr        if (!$res) return false;
3188da7d805SAndreas Gohr
3198da7d805SAndreas Gohr        return $res->fetch(PDO::FETCH_ASSOC);
320fee3b689Sstretchyboy    }
321fee3b689Sstretchyboy
322fee3b689Sstretchyboy    /**
32378977d74SKlap-in     * Count the number of records in result
3243157674bSAndreas Gohr     *
325db58e525SKlap-in     * This function is really inperformant in PDO and should be avoided!
3268da7d805SAndreas Gohr     *
3278da7d805SAndreas Gohr     * @param \PDOStatement $res
3288da7d805SAndreas Gohr     * @return int
329fee3b689Sstretchyboy     */
3308da7d805SAndreas Gohr    public function res2count($res)
3318da7d805SAndreas Gohr    {
3328da7d805SAndreas Gohr        if (!$res) return 0;
3338da7d805SAndreas Gohr
3348da7d805SAndreas Gohr        // this is a bullshit workaround for having res2arr and res2count work on one result
3358da7d805SAndreas Gohr        if (!$this->data) {
3368da7d805SAndreas Gohr            $this->data = $this->res2arr($res);
3378da7d805SAndreas Gohr        }
3388da7d805SAndreas Gohr
3398da7d805SAndreas Gohr        return count($this->data);
340fee3b689Sstretchyboy    }
34124a03f6cSstretchyboy
34224a03f6cSstretchyboy    /**
34324a03f6cSstretchyboy     * Count the number of records changed last time
3448da7d805SAndreas Gohr     *
3458da7d805SAndreas Gohr     * @param \PDOStatement $res
3468da7d805SAndreas Gohr     * @return int
34724a03f6cSstretchyboy     */
3488da7d805SAndreas Gohr    public function countChanges($res)
3498da7d805SAndreas Gohr    {
3508da7d805SAndreas Gohr        if (!$res) return 0;
3518da7d805SAndreas Gohr
3528da7d805SAndreas Gohr        return $res->rowCount();
353a1e6784eSAndreas Gohr    }
354a1e6784eSAndreas Gohr
3558da7d805SAndreas Gohr    // endregion
3568da7d805SAndreas Gohr
3578da7d805SAndreas Gohr    // region quoting/escaping functions
3588da7d805SAndreas Gohr
3598da7d805SAndreas Gohr    /**
3608da7d805SAndreas Gohr     * Join the given values and quote them for SQL insertion
3618da7d805SAndreas Gohr     */
3628da7d805SAndreas Gohr    public function quote_and_join($vals, $sep = ',')
3638da7d805SAndreas Gohr    {
36410cb854aSAndreas Gohr        $vals = array_map([$this->adapter->getPdo(), 'quote'], $vals);
365a7a40fb2SAnna Dabrowska        return implode($sep, $vals);
3668da7d805SAndreas Gohr    }
3678da7d805SAndreas Gohr
3688da7d805SAndreas Gohr    /**
3698da7d805SAndreas Gohr     * Quotes a string, by escaping it and adding quotes
3708da7d805SAndreas Gohr     */
3718da7d805SAndreas Gohr    public function quote_string($string)
3728da7d805SAndreas Gohr    {
37310cb854aSAndreas Gohr        return $this->adapter->getPdo()->quote($string);
3748da7d805SAndreas Gohr    }
3758da7d805SAndreas Gohr
3768da7d805SAndreas Gohr    /**
3778da7d805SAndreas Gohr     * Similar to quote_string, but without the quotes, useful to construct LIKE patterns
3788da7d805SAndreas Gohr     */
3798da7d805SAndreas Gohr    public function escape_string($str)
3808da7d805SAndreas Gohr    {
38110cb854aSAndreas Gohr        return trim($this->adapter->getPdo()->quote($str), "'");
3828da7d805SAndreas Gohr    }
3838da7d805SAndreas Gohr
3848da7d805SAndreas Gohr    // endregion
3858da7d805SAndreas Gohr
3868da7d805SAndreas Gohr    // region speciality functions
3878da7d805SAndreas Gohr
3888da7d805SAndreas Gohr    /**
3898da7d805SAndreas Gohr     * Split sql queries on semicolons, unless when semicolons are quoted
3908da7d805SAndreas Gohr     *
3918da7d805SAndreas Gohr     * Usually you don't need this. It's only really needed if you need individual results for
3928da7d805SAndreas Gohr     * multiple queries. For example in the admin interface.
3938da7d805SAndreas Gohr     *
3948da7d805SAndreas Gohr     * @param string $sql
3958da7d805SAndreas Gohr     * @return array sql queries
3968da7d805SAndreas Gohr     * @deprecated
3978da7d805SAndreas Gohr     */
3988da7d805SAndreas Gohr    public function SQLstring2array($sql)
3998da7d805SAndreas Gohr    {
400d0a5ba7aSAndreas Gohr        if (!DOKU_UNITTEST) { // for now we don't want to trigger the deprecation warning in the tests
4018da7d805SAndreas Gohr            dbg_deprecated(Tools::class . '::SQLstring2array');
402d0a5ba7aSAndreas Gohr        }
4038da7d805SAndreas Gohr        return Tools::SQLstring2array($sql);
4048da7d805SAndreas Gohr    }
4058da7d805SAndreas Gohr
4068da7d805SAndreas Gohr    /**
4078da7d805SAndreas Gohr     * @deprecated needs to be fixed in stuct and structpublish
4088da7d805SAndreas Gohr     */
409a7a40fb2SAnna Dabrowska    public function doTransaction($sql, $sqlpreparing = true)
410a7a40fb2SAnna Dabrowska    {
4118da7d805SAndreas Gohr        throw new \Exception(
4128da7d805SAndreas Gohr            'This method seems to never have done what it suggests. Please use the query() function instead.'
4138da7d805SAndreas Gohr        );
4148da7d805SAndreas Gohr    }
4158da7d805SAndreas Gohr
4168da7d805SAndreas Gohr    // endregion
417aa81d781SKlap-in}
418