static async connect(testMode = false, autoloadModels = true, noLog = false) {
        // Patch "mysql2" knex client
        // Workaround: Tried extending the ColumnCompiler class, but it didn't work for unknown reasons, so I override the function via prototype
        const { getDialectByNameOrAlias } = require("knex/lib/dialects");
        const mysql2 = getDialectByNameOrAlias("mysql2");
        mysql2.prototype.columnCompiler = function () {
            return new KumaColumnCompiler(this, ...arguments);
        };

        const acquireConnectionTimeout = 120 * 1000;
        let dbConfig;
        try {
            dbConfig = this.readDBConfig();
            Database.dbConfig = dbConfig;
        } catch (err) {
            log.warn("db", err.message);
            dbConfig = {
                type: "sqlite",
            };
        }

        let config = {};

        let mariadbPoolConfig = {
            min: 0,
            max: 10,
            idleTimeoutMillis: 30000,
        };

        log.info("db", `Database Type: ${dbConfig.type}`);

        if (dbConfig.type === "sqlite") {

            if (! fs.existsSync(Database.sqlitePath)) {
                log.info("server", "Copying Database");
                fs.copyFileSync(Database.templatePath, Database.sqlitePath);
            }

            const Dialect = require("knex/lib/dialects/sqlite3/index.js");
            Dialect.prototype._driver = () => require("@louislam/sqlite3");

            config = {
                client: Dialect,
                connection: {
                    filename: Database.sqlitePath,
                    acquireConnectionTimeout: acquireConnectionTimeout,
                },
                useNullAsDefault: true,
                pool: {
                    min: 1,
                    max: 1,
                    idleTimeoutMillis: 120 * 1000,
                    propagateCreateError: false,
                    acquireTimeoutMillis: acquireConnectionTimeout,
                }
            };
        } else if (dbConfig.type === "mariadb") {
            if (!/^\w+$/.test(dbConfig.dbName)) {
                throw Error("Invalid database name. A database name can only consist of letters, numbers and underscores");
            }

            const connection = await mysql.createConnection({
                host: dbConfig.hostname,
                port: dbConfig.port,
                user: dbConfig.username,
                password: dbConfig.password,
            });

            await connection.execute("CREATE DATABASE IF NOT EXISTS " + dbConfig.dbName + " CHARACTER SET utf8mb4");
            connection.end();

            config = {
                client: "mysql2",
                connection: {
                    host: dbConfig.hostname,
                    port: dbConfig.port,
                    user: dbConfig.username,
                    password: dbConfig.password,
                    database: dbConfig.dbName,
                    timezone: "Z",
                    typeCast: function (field, next) {
                        if (field.type === "DATETIME") {
                            // Do not perform timezone conversion
                            return field.string();
                        }
                        return next();
                    },
                },
                pool: mariadbPoolConfig,
            };
        } else if (dbConfig.type === "embedded-mariadb") {
            let embeddedMariaDB = EmbeddedMariaDB.getInstance();
            await embeddedMariaDB.start();
            log.info("mariadb", "Embedded MariaDB started");
            config = {
                client: "mysql2",
                connection: {
                    socketPath: embeddedMariaDB.socketPath,
                    user: embeddedMariaDB.username,
                    database: "kuma",
                    timezone: "Z",
                    typeCast: function (field, next) {
                        if (field.type === "DATETIME") {
                            // Do not perform timezone conversion
                            return field.string();
                        }
                        return next();
                    },
                },
                pool: mariadbPoolConfig,
            };
        } else {
            throw new Error("Unknown Database type: " + dbConfig.type);
        }

        // Set to utf8mb4 for MariaDB
        if (dbConfig.type.endsWith("mariadb")) {
            config.pool = {
                afterCreate(conn, done) {
                    conn.query("SET CHARACTER SET utf8mb4;", (err) => done(err, conn));
                },
            };
        }

        const knexInstance = knex(config);

        R.setup(knexInstance);

        if (process.env.SQL_LOG === "1") {
            R.debug(true);
        }

        // Auto map the model to a bean object
        R.freeze(true);

        if (autoloadModels) {
            await R.autoloadModels("./server/model");
        }

        if (dbConfig.type === "sqlite") {
            await this.initSQLite(testMode, noLog);
        } else if (dbConfig.type.endsWith("mariadb")) {
            await this.initMariaDB();
        }
    }
Connect to the database
@param {boolean} testMode Should the connection be started in test mode?
@param {boolean} autoloadModels Should models be automatically loaded?
@param {boolean} noLog Should logs not be output?
@returns {Promise<void>}