fix-knex
Louis Lam 4 weeks ago
parent 3a20e4726d
commit 60d65b18b7

@ -10,6 +10,7 @@ const { Settings } = require("./settings");
const { UptimeCalculator } = require("./uptime-calculator"); const { UptimeCalculator } = require("./uptime-calculator");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const { SimpleMigrationServer } = require("./utils/simple-migration-server"); const { SimpleMigrationServer } = require("./utils/simple-migration-server");
const KumaColumnCompiler = require("./utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler");
/** /**
* Database & App Data Folder * Database & App Data Folder
@ -190,14 +191,6 @@ class Database {
fs.writeFileSync(path.join(Database.dataDir, "db-config.json"), JSON.stringify(dbConfig, null, 4)); 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 * Connect to the database
* @param {boolean} testMode Should the connection be started in test mode? * @param {boolean} testMode Should the connection be started in test mode?
@ -206,6 +199,14 @@ class Database {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async connect(testMode = false, autoloadModels = true, noLog = false) { 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; const acquireConnectionTimeout = 120 * 1000;
let dbConfig; let dbConfig;
try { try {
@ -269,7 +270,7 @@ class Database {
connection.end(); connection.end();
config = { config = {
client: Database.getMySQL2KnexClient(), client: "mysql2",
connection: { connection: {
host: dbConfig.hostname, host: dbConfig.hostname,
port: dbConfig.port, port: dbConfig.port,
@ -292,7 +293,7 @@ class Database {
await embeddedMariaDB.start(); await embeddedMariaDB.start();
log.info("mariadb", "Embedded MariaDB started"); log.info("mariadb", "Embedded MariaDB started");
config = { config = {
client: Database.getMySQL2KnexClient(), client: "mysql2",
connection: { connection: {
socketPath: embeddedMariaDB.socketPath, socketPath: embeddedMariaDB.socketPath,
user: "node", user: "node",

@ -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;

@ -1,29 +1,21 @@
const ColumnCompilerMySQL = require("knex/lib/dialects/mysql/schema/mysql-columncompiler"); 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 { 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 * @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) * @returns {string|void} Default value (Don't understand why it can return void or string, but it's the original code, lol)
*/ */
defaultTo(value) { defaultTo(value) {
// MySQL defaults to null by default, but breaks down if you pass it explicitly if (this.type === "text" && typeof value === "string") {
// Note that in MySQL versions up to 5.7, logic related to updating log.debug("defaultTo", `${this.args[0]}: ${this.type} ${value} ${typeof value}`);
// 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 // 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
if (value === null || value === undefined) { // MariaDB 10.2.1 is required: https://mariadb.com/kb/en/text/
return; return `default (${formatDefault(value, this.type, this.client)})`;
} }
if ((this.type === "json" || this.type === "jsonb") && isObject(value)) { return super.defaultTo.apply(this, arguments);
// 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;
} }
} }

Loading…
Cancel
Save