xref: /plugin/combo/ComboStrap/Sqlite.php (revision d899a2a62926ce6e7f78d85d882eb464caef0a95)
1c3437056SNickeau<?php /** @noinspection SpellCheckingInspection */
2c3437056SNickeau
337748cd8SNickeau/**
437748cd8SNickeau * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved.
537748cd8SNickeau *
637748cd8SNickeau * This source code is licensed under the GPL license found in the
737748cd8SNickeau * COPYING  file in the root directory of this source tree.
837748cd8SNickeau *
937748cd8SNickeau * @license  GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
1037748cd8SNickeau * @author   ComboStrap <support@combostrap.com>
1137748cd8SNickeau *
1237748cd8SNickeau */
1337748cd8SNickeau
1437748cd8SNickeaunamespace ComboStrap;
1537748cd8SNickeau
1637748cd8SNickeau
17*d899a2a6Sgerardnicouse dokuwiki\plugin\sqlite\SQLiteDB;
1837748cd8SNickeauuse helper_plugin_sqlite;
1937748cd8SNickeau
2037748cd8SNickeauclass Sqlite
2137748cd8SNickeau{
2237748cd8SNickeau
23c3437056SNickeau
24c3437056SNickeau    /**
25c3437056SNickeau     * Principal database
26c3437056SNickeau     * (Backup)
27c3437056SNickeau     */
28c3437056SNickeau    private const  MAIN_DATABASE_NAME = "combo";
29c3437056SNickeau    /**
30c3437056SNickeau     * Backend Databse
31c3437056SNickeau     * (Log, Pub/Sub,...)
32c3437056SNickeau     */
33c3437056SNickeau    private const  SECONDARY_DB = "combo-secondary";
34c3437056SNickeau
35c3437056SNickeau    private static $sqliteVersion;
36c3437056SNickeau
3704fd306cSNickeau
3804fd306cSNickeau    private helper_plugin_sqlite $sqlitePlugin;
3904fd306cSNickeau
40c3437056SNickeau    /**
4104fd306cSNickeau     * @var SqliteRequest the actual request. If not closed, it will be close.
4204fd306cSNickeau     * Otherwise, it's not possible to delete the database file. See {@link self::deleteDatabasesFile()}
43c3437056SNickeau     */
4404fd306cSNickeau    private SqliteRequest $actualRequest;
4504fd306cSNickeau
46c3437056SNickeau
47c3437056SNickeau    /**
48c3437056SNickeau     * Sqlite constructor.
49c3437056SNickeau     * @var helper_plugin_sqlite $sqlitePlugin
50c3437056SNickeau     */
51c3437056SNickeau    public function __construct(helper_plugin_sqlite $sqlitePlugin)
52c3437056SNickeau    {
53c3437056SNickeau        $this->sqlitePlugin = $sqlitePlugin;
54c3437056SNickeau    }
55c3437056SNickeau
5637748cd8SNickeau
5737748cd8SNickeau    /**
5837748cd8SNickeau     *
59c3437056SNickeau     * @return Sqlite $sqlite
6004fd306cSNickeau     * @throws ExceptionSqliteNotAvailable
6137748cd8SNickeau     */
6204fd306cSNickeau    public static function createOrGetSqlite($databaseName = self::MAIN_DATABASE_NAME): Sqlite
6337748cd8SNickeau    {
64c3437056SNickeau
6504fd306cSNickeau        $sqliteExecutionObjectIdentifier = Sqlite::class . "-$databaseName";
6604fd306cSNickeau        $executionContext = ExecutionContext::getActualOrCreateFromEnv();
6704fd306cSNickeau
6804fd306cSNickeau        try {
6904fd306cSNickeau            /**
7004fd306cSNickeau             * @var Sqlite $sqlite
7104fd306cSNickeau             *
7204fd306cSNickeau             *
7304fd306cSNickeau             * sqlite is stored globally
7404fd306cSNickeau             * because when we create a new instance, it will open the
7504fd306cSNickeau             * sqlite file.
7604fd306cSNickeau             *
7704fd306cSNickeau             * In a {@link cli_plugin_combo} run, you will run in the error:
7804fd306cSNickeau             * ``
7904fd306cSNickeau             * failed to open stream: Too many open files
8004fd306cSNickeau             * ``
8104fd306cSNickeau             * As there is by default a limit of 1024 open files
8204fd306cSNickeau             * which means that if there is more than 1024 pages
8304fd306cSNickeau             * that you replicate using a new sqlite instance each time,
8404fd306cSNickeau             * you fail.
8504fd306cSNickeau             *
8604fd306cSNickeau             */
8704fd306cSNickeau            $sqlite = $executionContext->getRuntimeObject($sqliteExecutionObjectIdentifier);
8804fd306cSNickeau        } catch (ExceptionNotFound $e) {
8904fd306cSNickeau            $sqlite = null;
9004fd306cSNickeau        }
9104fd306cSNickeau
92c3437056SNickeau        if ($sqlite !== null) {
93c3437056SNickeau            $res = $sqlite->doWeNeedToCreateNewInstance();
94c3437056SNickeau            if ($res === false) {
95c3437056SNickeau                return $sqlite;
9637748cd8SNickeau            }
9737748cd8SNickeau        }
98c3437056SNickeau
9937748cd8SNickeau        /**
10037748cd8SNickeau         * Init
101c3437056SNickeau         * @var helper_plugin_sqlite $sqlitePlugin
10237748cd8SNickeau         */
103c3437056SNickeau        $sqlitePlugin = plugin_load('helper', 'sqlite');
104c3437056SNickeau        /**
105c3437056SNickeau         * Not enabled / loaded
106c3437056SNickeau         */
107c3437056SNickeau        if ($sqlitePlugin === null) {
108c3437056SNickeau
10937748cd8SNickeau            $sqliteMandatoryMessage = "The Sqlite Plugin is mandatory. Some functionalities of the ComboStrap Plugin may not work.";
11004fd306cSNickeau            throw new ExceptionSqliteNotAvailable($sqliteMandatoryMessage);
11137748cd8SNickeau        }
112c3437056SNickeau
113c3437056SNickeau        $adapter = $sqlitePlugin->getAdapter();
11437748cd8SNickeau        if ($adapter == null) {
11537748cd8SNickeau            self::sendMessageAsNotAvailable();
11637748cd8SNickeau        }
11737748cd8SNickeau
11837748cd8SNickeau        $adapter->setUseNativeAlter(true);
11937748cd8SNickeau
12004fd306cSNickeau        list($databaseName, $databaseDefinitionDir) = self::getDatabaseNameAndDefinitionDirectory($databaseName);
12104fd306cSNickeau        $init = $sqlitePlugin->init($databaseName, $databaseDefinitionDir);
12237748cd8SNickeau        if (!$init) {
12337748cd8SNickeau            $message = "Unable to initialize Sqlite";
12404fd306cSNickeau            throw new ExceptionSqliteNotAvailable($message);
125c3437056SNickeau        }
12637748cd8SNickeau        // regexp implementation
12737748cd8SNickeau        // https://stackoverflow.com/questions/5071601/how-do-i-use-regex-in-a-sqlite-query/18484596#18484596
128c3437056SNickeau        $adapter = $sqlitePlugin->getAdapter();
129*d899a2a6Sgerardnico        $regexFunctioName = 'regexp';
130*d899a2a6Sgerardnico        $regexpClosure = function ($pattern, $data, $delimiter = '~', $modifiers = 'isuS') {
13137748cd8SNickeau            if (isset($pattern, $data) === true) {
13237748cd8SNickeau                return (preg_match(sprintf('%1$s%2$s%1$s%3$s', $delimiter, $pattern, $modifiers), $data) > 0);
13337748cd8SNickeau            }
13437748cd8SNickeau            return null;
135*d899a2a6Sgerardnico        };
136*d899a2a6Sgerardnico        $regexArgCount = 4;
137*d899a2a6Sgerardnico        if (!self::isJuneVersion($adapter)) {
138*d899a2a6Sgerardnico            /** @noinspection PhpUndefinedMethodInspection */
139*d899a2a6Sgerardnico            $adapter->create_function($regexFunctioName, $regexpClosure, $regexArgCount);
140*d899a2a6Sgerardnico        } else {
141*d899a2a6Sgerardnico            $adapter->getPdo()->sqliteCreateFunction($regexFunctioName, $regexpClosure, $regexArgCount);
142*d899a2a6Sgerardnico        }
143c3437056SNickeau
144c3437056SNickeau        $sqlite = new Sqlite($sqlitePlugin);
14504fd306cSNickeau        $executionContext->setRuntimeObject($sqliteExecutionObjectIdentifier, $sqlite);
146c3437056SNickeau        return $sqlite;
147c3437056SNickeau
14837748cd8SNickeau    }
149c3437056SNickeau
15004fd306cSNickeau    /**
15104fd306cSNickeau     * @throws ExceptionSqliteNotAvailable
15204fd306cSNickeau     */
153c3437056SNickeau    public static function createOrGetBackendSqlite(): ?Sqlite
154c3437056SNickeau    {
155c3437056SNickeau        return self::createOrGetSqlite(self::SECONDARY_DB);
15637748cd8SNickeau    }
157c3437056SNickeau
1582becc3acSgerardnico
159c3437056SNickeau    public static function createSelectFromTableAndColumns(string $tableName, array $columns = null): string
160c3437056SNickeau    {
161c3437056SNickeau        if ($columns === null) {
162c3437056SNickeau            $columnStatement = "*";
163c3437056SNickeau        } else {
164c3437056SNickeau            $columnsStatement = [];
165c3437056SNickeau            foreach ($columns as $columnName) {
166c3437056SNickeau                $columnsStatement[] = "$columnName as \"$columnName\"";
167c3437056SNickeau            }
168c3437056SNickeau            $columnStatement = implode(", ", $columnsStatement);
169c3437056SNickeau        }
170031d4b49Sgerardnico        /**
1712becc3acSgerardnico         * TODO: We had added the `rowid` on all query
1722becc3acSgerardnico         *  but the underlining code was not supporting it,
1732becc3acSgerardnico         *  adding it in the next release to be able to locate the row
174031d4b49Sgerardnico         */
1752becc3acSgerardnico        return "select $columnStatement from $tableName";
17637748cd8SNickeau
17737748cd8SNickeau    }
17837748cd8SNickeau
17937748cd8SNickeau    /**
18004fd306cSNickeau     * Used in test to delete the database file
18104fd306cSNickeau     * @return void
18204fd306cSNickeau     * @throws ExceptionFileSystem - if we can delete the databases
18304fd306cSNickeau     */
18404fd306cSNickeau    public static function deleteDatabasesFile()
18504fd306cSNickeau    {
18604fd306cSNickeau        /**
18704fd306cSNickeau         * The plugin does not give you the option to
18804fd306cSNickeau         * where to create the database file
18904fd306cSNickeau         * See {@link \helper_plugin_sqlite_adapter::initdb()}
19004fd306cSNickeau         * $this->dbfile = $conf['metadir'].'/'.$dbname.$this->fileextension;
19104fd306cSNickeau         *
19204fd306cSNickeau         * If error on delete, see {@link self::close()}
19304fd306cSNickeau         */
19404fd306cSNickeau        $metadatDirectory = ExecutionContext::getActualOrCreateFromEnv()
19504fd306cSNickeau            ->getConfig()
19604fd306cSNickeau            ->getMetaDataDirectory();
19704fd306cSNickeau        $fileChildren = FileSystems::getChildrenLeaf($metadatDirectory);
19804fd306cSNickeau        foreach ($fileChildren as $child) {
19904fd306cSNickeau            try {
20004fd306cSNickeau                $extension = $child->getExtension();
20104fd306cSNickeau            } catch (ExceptionNotFound $e) {
20204fd306cSNickeau                // ok no extension
20304fd306cSNickeau                continue;
20404fd306cSNickeau            }
20504fd306cSNickeau            if (in_array($extension, ["sqlite", "sqlite3"])) {
20604fd306cSNickeau                FileSystems::delete($child);
20704fd306cSNickeau            }
20804fd306cSNickeau
20904fd306cSNickeau        }
21004fd306cSNickeau    }
21104fd306cSNickeau
21204fd306cSNickeau    private static function getDatabaseNameAndDefinitionDirectory($databaseName): array
21304fd306cSNickeau    {
21404fd306cSNickeau        global $conf;
21504fd306cSNickeau
21604fd306cSNickeau        if ($databaseName === self::MAIN_DATABASE_NAME) {
21704fd306cSNickeau            $oldDbName = '404manager';
21804fd306cSNickeau            $oldDbFile = $conf['metadir'] . "/{$oldDbName}.sqlite";
21904fd306cSNickeau            $oldDbFileSqlite3 = $conf['metadir'] . "/{$oldDbName}.sqlite3";
22004fd306cSNickeau            if (file_exists($oldDbFile) || file_exists($oldDbFileSqlite3)) {
22104fd306cSNickeau                $databaseName = $oldDbName;
22204fd306cSNickeau            }
22304fd306cSNickeau        }
22404fd306cSNickeau
22504fd306cSNickeau        $databaseDir = DOKU_PLUGIN . PluginUtility::PLUGIN_BASE_NAME . "/db/$databaseName";
22604fd306cSNickeau        return [$databaseName, $databaseDir];
22704fd306cSNickeau
22804fd306cSNickeau    }
22904fd306cSNickeau
23004fd306cSNickeau    /**
23137748cd8SNickeau     * Print debug info to the console in order to resolve
23237748cd8SNickeau     * RuntimeException: HY000 8 attempt to write a readonly database
23337748cd8SNickeau     * https://phpunit.readthedocs.io/en/latest/writing-tests-for-phpunit.html#error-output
23437748cd8SNickeau     */
235c3437056SNickeau    public function printDbInfoAtConsole()
23637748cd8SNickeau    {
237c3437056SNickeau        $dbFile = $this->sqlitePlugin->getAdapter()->getDbFile();
23837748cd8SNickeau        fwrite(STDERR, "Stderr DbFile: " . $dbFile . "\n");
23937748cd8SNickeau        if (file_exists($dbFile)) {
24037748cd8SNickeau            fwrite(STDERR, "File does exists\n");
24137748cd8SNickeau            fwrite(STDERR, "Permission " . substr(sprintf('%o', fileperms($dbFile)), -4) . "\n");
24237748cd8SNickeau        } else {
24337748cd8SNickeau            fwrite(STDERR, "File does not exist\n");
24437748cd8SNickeau        }
24537748cd8SNickeau
24637748cd8SNickeau        global $conf;
24737748cd8SNickeau        $metadir = $conf['metadir'];
24837748cd8SNickeau        fwrite(STDERR, "MetaDir: " . $metadir . "\n");
24937748cd8SNickeau        $subdir = strpos($dbFile, $metadir) === 0;
25037748cd8SNickeau        if ($subdir) {
25137748cd8SNickeau            fwrite(STDERR, "Meta is a subdirectory of the db \n");
25237748cd8SNickeau        } else {
25337748cd8SNickeau            fwrite(STDERR, "Meta is a not subdirectory of the db \n");
25437748cd8SNickeau        }
25537748cd8SNickeau
25637748cd8SNickeau    }
25737748cd8SNickeau
25837748cd8SNickeau    /**
25937748cd8SNickeau     * Json support
26037748cd8SNickeau     */
261c3437056SNickeau    public function supportJson(): bool
26237748cd8SNickeau    {
26337748cd8SNickeau
26437748cd8SNickeau
265c3437056SNickeau        $res = $this->sqlitePlugin->query("PRAGMA compile_options");
26637748cd8SNickeau        $isJsonEnabled = false;
267c3437056SNickeau        foreach ($this->sqlitePlugin->res2arr($res) as $row) {
26837748cd8SNickeau            if ($row["compile_option"] === "ENABLE_JSON1") {
26937748cd8SNickeau                $isJsonEnabled = true;
27037748cd8SNickeau                break;
27137748cd8SNickeau            }
27237748cd8SNickeau        };
273c3437056SNickeau        $this->sqlitePlugin->res_close($res);
27437748cd8SNickeau        return $isJsonEnabled;
27537748cd8SNickeau    }
27637748cd8SNickeau
27737748cd8SNickeau
27804fd306cSNickeau    /**
27904fd306cSNickeau     * @throws ExceptionSqliteNotAvailable
28004fd306cSNickeau     */
281c3437056SNickeau    public
282c3437056SNickeau    static function sendMessageAsNotAvailable(): void
28337748cd8SNickeau    {
28437748cd8SNickeau        $sqliteMandatoryMessage = "The Sqlite Php Extension is mandatory. It seems that it's not available on this installation.";
28504fd306cSNickeau        throw new ExceptionSqliteNotAvailable($sqliteMandatoryMessage);
28637748cd8SNickeau    }
287c3437056SNickeau
288c3437056SNickeau    /**
28904fd306cSNickeau     *
29004fd306cSNickeau     * Old check when there was no {@link ExecutionContext}
29104fd306cSNickeau     * to reset the Sqlite variable
29204fd306cSNickeau     * TODO: delete ?
293c3437056SNickeau     *
294c3437056SNickeau     *
295c3437056SNickeau     */
296c3437056SNickeau    private function doWeNeedToCreateNewInstance(): bool
297c3437056SNickeau    {
298c3437056SNickeau
299c3437056SNickeau        global $conf;
300c3437056SNickeau        $metaDir = $conf['metadir'];
301c3437056SNickeau
302c3437056SNickeau        /**
303c3437056SNickeau         * Adapter may be null
304c3437056SNickeau         * when the SQLite & PDO SQLite
305c3437056SNickeau         * are not installed
306c3437056SNickeau         * ie: SQLite & PDO SQLite support missing
307c3437056SNickeau         */
308c3437056SNickeau        $adapter = $this->sqlitePlugin->getAdapter();
309c3437056SNickeau        if ($adapter === null) {
310c3437056SNickeau            return true;
311c3437056SNickeau        }
312c3437056SNickeau
313c3437056SNickeau        /**
314c3437056SNickeau         * When the database is {@link \helper_plugin_sqlite_adapter::closedb()}
315c3437056SNickeau         */
316*d899a2a6Sgerardnico        if (!self::isJuneVersion($adapter)) {
317*d899a2a6Sgerardnico            /** @noinspection PhpUndefinedMethodInspection */
318*d899a2a6Sgerardnico            $db = $adapter->getDb();
319*d899a2a6Sgerardnico        } else {
320*d899a2a6Sgerardnico            $db = $adapter->getPdo();
321*d899a2a6Sgerardnico        }
322*d899a2a6Sgerardnico        if ($db === null) {
323c3437056SNickeau            /**
324c3437056SNickeau             * We may also open it again
325c3437056SNickeau             * {@link \helper_plugin_sqlite_adapter::opendb()}
326c3437056SNickeau             * for now, reinit
327c3437056SNickeau             */
328c3437056SNickeau            return true;
329c3437056SNickeau        }
330*d899a2a6Sgerardnico
331c3437056SNickeau        /**
332c3437056SNickeau         * In test, we are running in different context (ie different root
333c3437056SNickeau         * directory for DokuWiki and therefore different $conf
334c3437056SNickeau         * and therefore different metadir where sqlite is stored)
335c3437056SNickeau         * Because a sql file may be deleted, we may get:
336c3437056SNickeau         * ```
337c3437056SNickeau         * RuntimeException: HY000 8 attempt to write a readonly database:
338c3437056SNickeau         * ```
339c3437056SNickeau         * To avoid this error, we check that we are still in the same metadir
340c3437056SNickeau         * where the sqlite database is stored. If not, we create a new instance
341c3437056SNickeau         */
342c3437056SNickeau        $dbFile = $adapter->getDbFile();
343c3437056SNickeau        if (!file_exists($dbFile)) {
344c3437056SNickeau            $this->close();
345c3437056SNickeau            return true;
346c3437056SNickeau        }
347c3437056SNickeau        // the file is in the meta directory
348c3437056SNickeau        if (strpos($dbFile, $metaDir) === 0) {
349c3437056SNickeau            // we are still in a class run
350c3437056SNickeau            return false;
351c3437056SNickeau        }
352c3437056SNickeau        $this->close();
353c3437056SNickeau        return true;
354c3437056SNickeau    }
355c3437056SNickeau
35604fd306cSNickeau    public function close()
357c3437056SNickeau    {
358c3437056SNickeau
35904fd306cSNickeau        /**
36004fd306cSNickeau         * https://www.php.net/manual/en/pdo.connections.php#114822
36104fd306cSNickeau         * You put the variable connection on null
36204fd306cSNickeau         * the {@link \helper_plugin_sqlite_adapter::closedb() function} do that
36304fd306cSNickeau         *
36404fd306cSNickeau         * If we don't do that, the file is still locked
36504fd306cSNickeau         * by the sqlite process and the clean up process
36604fd306cSNickeau         * of dokuwiki test cannot delete it
36704fd306cSNickeau         *
36804fd306cSNickeau         * ie to avoid
36904fd306cSNickeau         * RuntimeException: Unable to delete the file
37004fd306cSNickeau         * (C:/Users/GERARD~1/AppData/Local/Temp/dwtests-1676813655.6773/data/meta/combo-secondary.sqlite3) in D:\dokuwiki\_test\core\TestUtils.php on line 58
37104fd306cSNickeau         * {@link TestUtils::rdelete}
37204fd306cSNickeau         *
37304fd306cSNickeau         * Windows sort of handling/ bug explained here
37404fd306cSNickeau         * https://bugs.php.net/bug.php?id=78930&edit=3
37504fd306cSNickeau         *
37604fd306cSNickeau         * Null to close the db explanation and bug
37704fd306cSNickeau         * https://bugs.php.net/bug.php?id=62065
37804fd306cSNickeau         *
37904fd306cSNickeau         */
38004fd306cSNickeau
38104fd306cSNickeau        $this->closeActualRequestIfNotClosed();
38204fd306cSNickeau
383c3437056SNickeau        $adapter = $this->sqlitePlugin->getAdapter();
384c3437056SNickeau        if ($adapter !== null) {
38504fd306cSNickeau
386*d899a2a6Sgerardnico            if (!$this->isJuneVersion($adapter)) {
387*d899a2a6Sgerardnico                /** @noinspection PhpUndefinedMethodInspection */
388c3437056SNickeau                $adapter->closedb();
389*d899a2a6Sgerardnico            } else {
390*d899a2a6Sgerardnico                $adapter->__sleep();
391*d899a2a6Sgerardnico            }
392c3437056SNickeau
39304fd306cSNickeau            unset($adapter);
39404fd306cSNickeau
39504fd306cSNickeau            gc_collect_cycles();
396c3437056SNickeau
397c3437056SNickeau        }
398c3437056SNickeau
399c3437056SNickeau    }
400c3437056SNickeau
401c3437056SNickeau    public function getDbName(): string
402c3437056SNickeau    {
403c3437056SNickeau        return $this->sqlitePlugin->getAdapter()->getName();
404c3437056SNickeau    }
405c3437056SNickeau
406c3437056SNickeau
407c3437056SNickeau    public function getSqlitePlugin(): helper_plugin_sqlite
408c3437056SNickeau    {
409c3437056SNickeau        return $this->sqlitePlugin;
410c3437056SNickeau    }
411c3437056SNickeau
412c3437056SNickeau    public function createRequest(): SqliteRequest
413c3437056SNickeau    {
41404fd306cSNickeau        $this->closeActualRequestIfNotClosed();
41504fd306cSNickeau        $this->actualRequest = new SqliteRequest($this);
41604fd306cSNickeau        return $this->actualRequest;
417c3437056SNickeau    }
418c3437056SNickeau
419c3437056SNickeau    public function getVersion()
420c3437056SNickeau    {
421c3437056SNickeau        if (self::$sqliteVersion === null) {
422c3437056SNickeau            $request = $this->createRequest()
423c3437056SNickeau                ->setQuery("select sqlite_version()");
424c3437056SNickeau            try {
425c3437056SNickeau                self::$sqliteVersion = $request
426c3437056SNickeau                    ->execute()
427c3437056SNickeau                    ->getFirstCellValue();
42804fd306cSNickeau            } catch (ExceptionCompile $e) {
429c3437056SNickeau                self::$sqliteVersion = "unknown";
430c3437056SNickeau            } finally {
431c3437056SNickeau                $request->close();
432c3437056SNickeau            }
433c3437056SNickeau        }
434c3437056SNickeau        return self::$sqliteVersion;
435c3437056SNickeau    }
436c3437056SNickeau
437c3437056SNickeau    /**
438c3437056SNickeau     * @param string $option
439c3437056SNickeau     * @return bool - true if the option is available
440c3437056SNickeau     */
441c3437056SNickeau    public function hasOption(string $option): bool
442c3437056SNickeau    {
443c3437056SNickeau        try {
444c3437056SNickeau            $present = $this->createRequest()
44504fd306cSNickeau                ->setQueryParametrized("select count(1) from pragma_compile_options() where compile_options = ?", [$option])
446c3437056SNickeau                ->execute()
447c3437056SNickeau                ->getFirstCellValueAsInt();
448c3437056SNickeau            return $present === 1;
44904fd306cSNickeau        } catch (ExceptionCompile $e) {
450c3437056SNickeau            LogUtility::msg("Error while trying to see if the sqlite option is available");
451c3437056SNickeau            return false;
452c3437056SNickeau        }
453c3437056SNickeau
454c3437056SNickeau    }
45504fd306cSNickeau
45604fd306cSNickeau    /**
45704fd306cSNickeau     * Internal function that closes the actual request
45804fd306cSNickeau     * This is to be able to close all resources even if the developer
45904fd306cSNickeau     * forget.
46004fd306cSNickeau     *
46104fd306cSNickeau     * This is needed to be able to delete the database file.
46204fd306cSNickeau     * See {@link self::close()} for more information
46304fd306cSNickeau     *
46404fd306cSNickeau     * @return void
46504fd306cSNickeau     */
46604fd306cSNickeau    private function closeActualRequestIfNotClosed()
46704fd306cSNickeau    {
46804fd306cSNickeau        if (isset($this->actualRequest)) {
46904fd306cSNickeau            $this->actualRequest->close();
47004fd306cSNickeau            unset($this->actualRequest);
47104fd306cSNickeau        }
47204fd306cSNickeau    }
473*d899a2a6Sgerardnico
474*d899a2a6Sgerardnico    /**
475*d899a2a6Sgerardnico     * Version 2023-06-21 brings error
476*d899a2a6Sgerardnico     * https://github.com/cosmocode/sqlite/releases/tag/2023-06-21
477*d899a2a6Sgerardnico     * https://www.dokuwiki.org/plugin:sqlite#changes_from_earlier_releases
478*d899a2a6Sgerardnico     * @param $adapter
479*d899a2a6Sgerardnico     * @return bool
480*d899a2a6Sgerardnico     */
481*d899a2a6Sgerardnico    public static function isJuneVersion($adapter): bool
482*d899a2a6Sgerardnico    {
483*d899a2a6Sgerardnico        return get_class($adapter) === SQLiteDB::class;
484*d899a2a6Sgerardnico    }
485*d899a2a6Sgerardnico
48637748cd8SNickeau}
487