import mariadb from 'mariadb'; import { loadConfig } from './configLoader'; const config = loadConfig(); class SqlSystem { private static pool: mariadb.Pool = mariadb.createPool({ database: process.env.DATABASE, host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, connectionLimit: config.data.worker_count ?? 10 }) public static async InitDB() { let conn: mariadb.PoolConnection | undefined; try { conn = await this.pool.getConnection(); //Setup tables for auctions and lifetimes await conn.query(` CREATE TABLE if NOT EXISTS auctions ( id INT AUTO_INCREMENT PRIMARY KEY, auctionid VARCHAR(255) NOT NULL, lbin DECIMAL(65,5) SIGNED, UNIQUE INDEX 'auctionid' ('auctionid') ); CREATE TABLE if NOT EXISTS lifetimes (id INT PRIMARY KEY, insertedon DATETIME(2)); `) //Setup lifetime Trigger await conn.query(` DELIMITER $$ CREATE TRIGGER IF NOT EXISTS insertLifetime AFTER INSERT ON auctions FOR EACH ROW BEGIN INSERT INTO lifetimes (id, insertedon) VALUES (NEW.ID, NOW()); END$$ DELIMITER ; DELIMITER $$ CREATE TRIGGER IF NOT EXISTS updateLifetime AFTER UPDATE ON auctions FOR EACH ROW BEGIN UPDATE lifetimes SET insertedon = NOW() WHERE id=NEW.ID; END$$ DELIMITER ; DELIMITER $$ CREATE TRIGGER IF NOT EXISTS removeLifetime BEFORE DELETE ON auctions FOR EACH ROW BEGIN DELETE FROM lifetimes WHERE id=OLD.ID; END$$ DELIMITER ; `) //Setup Table sanitation await conn.query(` DELIMITER $$ CREATE PROCEDURE IF NOT EXISTS CleanupOldEntries() BEGIN DECLARE done INT DEFAULT 0; DECLARE temp_id INT; DECLARE cur CURSOR FOR SELECT id FROM lifetimes WHERE TIMESTAMPDIFF(HOUR, insertedon, NOW()) >= 6; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; OPEN cur; read_loop: LOOP FETCH cur INTO temp_id; IF done THEN LEAVE read_loop; END IF; DELETE FROM auctions WHERE id = temp_id; END LOOP; CLOSE cur; END$$ DELIMITER ; SET GLOBAL event_scheduler = ON; CREATE EVENT IF NOT EXISTS CleanupOldEntriesEvent ON SCHEDULE EVERY 2 MINUTE DO CALL CleanupOldEntries(); `) } catch (error) { console.error("InitDB Error: ", error); throw error; } finally { if (conn) conn.release(); } } //INSERT ELEMENT IN AUCTIONS TABLE OR UPDATE IF IT ALREADY EXISTS public static async Upsert(auctionid: string, lbin: number) { let conn: mariadb.PoolConnection | undefined; try { conn = await this.pool.getConnection(); await conn.beginTransaction(); await conn.query(` INSERT INTO auctions (auctionid, LBin) VALUES (?,?) ON DUPLICATE KEY UPDATE LBin = VALUES(LBin); `, [auctionid, lbin]); await conn.commit(); } catch (error) { console.error("InitDB Error: ", error); if(conn) await conn.rollback(); throw error; } finally { if (conn) conn.release(); } } //REMOVE ELEMENT IN AUCTIONS TABLE public static async Remove(auctionId: string) { let conn: mariadb.PoolConnection | undefined; try { conn = await this.pool.getConnection(); await conn.beginTransaction(); await conn.query(` DELETE FROM auctions WHERE auctionid = ? `,[auctionId]); await conn.commit(); } catch (error) { console.error("InitDB Error: ", error); if(conn) await conn.rollback(); throw error; } finally { if (conn) conn.release(); } } //MATCH PROVIDED ELEMENTS IN AUCTIONS TABLE - returns true/false public static async Match(auctionid:string|string[],lbin:number|number[]): Promise { let conn: mariadb.PoolConnection | undefined; let result; try{ conn = await this.pool.getConnection(); if(Array.isArray(auctionid) && Array.isArray(lbin)) if(auctionid.length === lbin.length) { result = await conn.query(` CREATE TEMPORARY TABLE IF NOT EXISTS TEMP ( auctionID VARCHAR(255) PRIMARY KEY, lbin DECIMAL(65,5) ); INSERT INTO TEMP (auctionID, lbin) VALUES ? SELECT tmp.* FROM TEMP AS tmp WHERE NOT EXISTS ( SELECT 1 FROM auctions AS auc WHERE auc.auctionID = tmp.auctionID AND auc.lbin = tmp.lbin); `,[await this.UnifiedArray(auctionid,lbin)]) return result; } else if(Array.isArray(auctionid) || Array.isArray(lbin)) throw Error('Match SQL Function error - cannot match collection to singlet'); else { result = await conn.query(` SELECT CASE WHEN COUNT(*) > 0 THEN true ELSE false END AS user_exists FROM auctions WHERE auctionID = ?; `,[auctionid]) return Boolean(result[0].result) } } catch(error) { console.error("message_before_error: ", error); throw error; } finally { if (conn) conn.release(); } } //EXAMPLE BLOCK OF CODE FOR ADDING DATABASE FUNCTIONS /* public static async example_name() { let conn:mariadb.PoolConnection|undefined; try{ conn = await this.pool.getConnection(); await conn.beginTransaction(); //CODE HERE// await conn.commit(); } catch(error) { console.error("message_before_error: ", error); if(conn) await conn.rollback(); throw error; } finally { if(conn) conn.release(); } */ private static async UnifiedArray(auctionid: string[], lbin: number[]): Promise<{ x: string[], y: number[] }> { return { x: [...auctionid], y: [...lbin] }; } } export { SqlSystem }