From 8bf0155142bf316f7b4983d422e3999966c2e9df Mon Sep 17 00:00:00 2001 From: Wolvan Date: Fri, 4 Feb 2022 20:34:21 +0100 Subject: [PATCH] Fix multiple MySQL issues The first issue is an invalid option to the MySQL connector in the form of the table prefix. It instead gets set as a private prop on the storage class and then removed from the options object. The second and more important issue is that the field to store the duplication check data ended up being too small too quickly, causing it to quickly fail to write a full JSON string and leading to an error while retrieving the poll. An `ALTER` statement during init is used to make sure the datatype is now a `MEDIUMTEXT`. With 16MB it should fill up way less quickly than previously. --- src/MySQLStorage.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/MySQLStorage.ts b/src/MySQLStorage.ts index 7cc4d6a..3d28fae 100644 --- a/src/MySQLStorage.ts +++ b/src/MySQLStorage.ts @@ -7,7 +7,8 @@ import { BackendPoll as Poll } from "./Poll"; export default class MySQLStorage extends Storage { #db: mysql.Connection; - #options: mysql.ConnectionOptions & { tablePrefix?: string }; + #options: mysql.ConnectionOptions; + #tablePrefix: string; #createConnection(mysqlInstance?: mysql.Connection): void { if (!mysqlInstance) this.#db = mysql.createConnection(this.#options); this.#db.on("error", (err: mysql.QueryError) => { @@ -17,6 +18,9 @@ export default class MySQLStorage extends Storage { constructor(options: mysql.ConnectionOptions & { tablePrefix?: string }) { super(); + options = Object.assign({}, options); + this.#tablePrefix = options.tablePrefix || ""; + delete options["tablePrefix"]; this.#options = options; console.debug("Initiating MySQLStorage."); this.#db = mysql.createConnection(this.#options); @@ -25,7 +29,7 @@ export default class MySQLStorage extends Storage { async init(): Promise { await this.#db.promise().query(` - CREATE TABLE IF NOT EXISTS ${ this.#options.tablePrefix || "" }polls ( + CREATE TABLE IF NOT EXISTS ${ this.#tablePrefix || "" }polls ( id_str VARCHAR(32) NOT NULL PRIMARY KEY, title VARCHAR(${MAX_CHARACTER_LENGTH}) NOT NULL DEFAULT '', dupe_check_mode ENUM('none', 'ip', 'cookie') NOT NULL DEFAULT 'ip', @@ -38,11 +42,14 @@ export default class MySQLStorage extends Storage { updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=INNODB; `); + await this.#db.promise().query(` + ALTER TABLE ${ this.#tablePrefix || "" }polls CHANGE dupe_data dupe_data MEDIUMTEXT CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL; + `); return this; } async getItem(key: string): Promise { - const [rows] = await this.#db.promise().execute(`SELECT * FROM ${ this.#options.tablePrefix || "" }polls WHERE id_str = ? AND deleted_at IS NULL;`, [key]); + const [rows] = await this.#db.promise().execute(`SELECT * FROM ${ this.#tablePrefix || "" }polls WHERE id_str = ? AND deleted_at IS NULL;`, [key]); if (!rows || !Array.isArray(rows) || !rows.length) return null; const row = rows[0] as { id_str: string, @@ -68,7 +75,7 @@ export default class MySQLStorage extends Storage { async setItem(key: string, value: Poll): Promise { await this.#db.promise().execute(` - INSERT INTO ${ this.#options.tablePrefix || "" }polls + INSERT INTO ${ this.#tablePrefix || "" }polls (id_str, title, dupe_check_mode, multi_select, captcha, dupe_data, options, creation_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE