diff --git a/server/database.js b/server/database.js index 4ba53bb8..3927d6db 100644 --- a/server/database.js +++ b/server/database.js @@ -10,6 +10,7 @@ const { Settings } = require("./settings"); const { UptimeCalculator } = require("./uptime-calculator"); const dayjs = require("dayjs"); const { SimpleMigrationServer } = require("./utils/simple-migration-server"); +const KumaColumnCompiler = require("./utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler"); /** * Database & App Data Folder @@ -190,14 +191,6 @@ class Database { fs.writeFileSync(path.join(Database.dataDir, "db-config.json"), JSON.stringify(dbConfig, null, 4)); } - /** - * Get the MySQL2 Knex client - * @returns {KumaMySQL2} MySQL2 Knex client - */ - static getMySQL2KnexClient() { - return require("./utils/knex/lib/dialects/mysql2/index"); - } - /** * Connect to the database * @param {boolean} testMode Should the connection be started in test mode? @@ -206,6 +199,14 @@ class Database { * @returns {Promise} */ 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 { @@ -269,7 +270,7 @@ class Database { connection.end(); config = { - client: Database.getMySQL2KnexClient(), + client: "mysql2", connection: { host: dbConfig.hostname, port: dbConfig.port, @@ -292,7 +293,7 @@ class Database { await embeddedMariaDB.start(); log.info("mariadb", "Embedded MariaDB started"); config = { - client: Database.getMySQL2KnexClient(), + client: "mysql2", connection: { socketPath: embeddedMariaDB.socketPath, user: "node", diff --git a/server/utils/knex/lib/dialects/mysql2/index.js b/server/utils/knex/lib/dialects/mysql2/index.js deleted file mode 100644 index bfdc3f8b..00000000 --- a/server/utils/knex/lib/dialects/mysql2/index.js +++ /dev/null @@ -1,20 +0,0 @@ -const ClientMySQL2 = require("knex/lib/dialects/mysql2/index"); -const KumaColumnCompiler = require("./schema/mysql2-columncompiler"); - -/** - * Fixed MySQL2 client class. - * - Fix: Default value for TEXT fields is not supported. - */ -class KumaMySQL2 extends ClientMySQL2 { - - driverName = "mysql2"; - - /** - * - */ - columnCompiler() { - return new KumaColumnCompiler(this, ...arguments); - } -} - -module.exports = KumaMySQL2; diff --git a/server/utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler.js b/server/utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler.js index b1cb2e1b..6fe40fca 100644 --- a/server/utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler.js +++ b/server/utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler.js @@ -1,29 +1,21 @@ const ColumnCompilerMySQL = require("knex/lib/dialects/mysql/schema/mysql-columncompiler"); -const { isObject } = require("knex/lib/util/is"); +const { formatDefault } = require("knex/lib/formatter/formatterUtils"); +const { log } = require("../../../../../../../src/util"); class KumaColumnCompiler extends ColumnCompilerMySQL { /** - * Diff: Override defaultTo method to handle default value for TEXT fields + * Override defaultTo method to handle default value for TEXT fields * @param {any} value Value * @returns {string|void} Default value (Don't understand why it can return void or string, but it's the original code, lol) */ defaultTo(value) { - // MySQL defaults to null by default, but breaks down if you pass it explicitly - // Note that in MySQL versions up to 5.7, logic related to updating - // timestamps when no explicit value is passed is quite insane - https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp - if (value === null || value === undefined) { - return; + if (this.type === "text" && typeof value === "string") { + log.debug("defaultTo", `${this.args[0]}: ${this.type} ${value} ${typeof value}`); + // MySQL 8.0 is required and only if the value is written as an expression: https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html + // MariaDB 10.2.1 is required: https://mariadb.com/kb/en/text/ + return `default (${formatDefault(value, this.type, this.client)})`; } - if ((this.type === "json" || this.type === "jsonb") && isObject(value)) { - // Default value for json will work only it is an expression - return `default ('${JSON.stringify(value)}')`; - } - const defaultVal = super.defaultTo.apply(this, arguments); - - // louislam deleted: (this.type !== "blob" && this.type.indexOf("text") === -1) - // Other code is the same as the original code - // See: https://github.com/louislam/uptime-kuma/pull/5048#discussion_r1818076626 - return defaultVal; + return super.defaultTo.apply(this, arguments); } }