hypixel-auction-notifier/src/sqlFunctions.ts

272 lines
7.9 KiB
TypeScript

import { Pool, PoolClient } from 'pg';
import { loadConfig } from './configLoader';
const config = loadConfig();
class SqlSystem {
private static pool: Pool = new Pool({
database: process.env.DATABASE,
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
max: config.data.worker_count ?? 10
})
public static async InitDB() {
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
//Setup tables for auctions and lifetimes
await conn.query(`
CREATE TABLE if NOT EXISTS auctions (
id UUID PRIMARY KEY,
auctionid VARCHAR(255) NOT NULL,
lbin DECIMAL(65,5),
UNIQUE (auctionid)
);
CREATE TABLE if NOT EXISTS lifetimes (id UUID PRIMARY KEY, insertedon TIMESTAMP(2));
`);
//Setup lifetime Trigger
await conn.query(`
-- Insert trigger function
CREATE OR REPLACE FUNCTION public.fn_insert_lifetime()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO lifetimes (id, insertedon)
VALUES (NEW.id, NOW());
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Update trigger function
CREATE OR REPLACE FUNCTION public.fn_update_lifetime()
RETURNS TRIGGER AS $$
BEGIN
UPDATE lifetimes
SET insertedon = NOW()
WHERE id = NEW.id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Remove trigger function
CREATE OR REPLACE FUNCTION public.fn_remove_lifetime()
RETURNS TRIGGER AS $$
BEGIN
DELETE FROM lifetimes
WHERE id = OLD.id;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
`);
await conn.query(`
-- Insert trigger
DROP TRIGGER IF EXISTS insert_lifetime ON auctions;
CREATE TRIGGER insert_lifetime
AFTER INSERT ON auctions
FOR EACH ROW
EXECUTE PROCEDURE public.fn_insert_lifetime();
-- Update trigger
DROP TRIGGER IF EXISTS update_lifetime ON auctions;
CREATE TRIGGER update_lifetime
AFTER UPDATE ON auctions
FOR EACH ROW
EXECUTE PROCEDURE public.fn_update_lifetime();
-- Remove trigger
DROP TRIGGER IF EXISTS remove_lifetime ON auctions;
CREATE TRIGGER remove_lifetime
BEFORE DELETE ON auctions
FOR EACH ROW
EXECUTE PROCEDURE public.fn_remove_lifetime();
`);
//Setup Table sanitation
await conn.query(`
CREATE OR REPLACE FUNCTION public.cleanup_old_entries()
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
temp_id INT;
BEGIN
FOR temp_id IN
SELECT id
FROM lifetimes
WHERE EXTRACT(EPOCH FROM (NOW() - insertedon))/3600 >= 6 -- 6 hours
LOOP
-- Removes matching entries from auctions
DELETE FROM auctions
WHERE auctions.id = temp_id;
END LOOP;
RETURN;
END;
$$;
`);
}
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|string[], lbin: number|number[]) {
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
await conn.query('BEGIN');
if(Array.isArray(auctionid) && Array.isArray(lbin))
if(auctionid.length === lbin.length) {
await conn.query(`
INSERT INTO auctions (auctionid, LBin)
VALUES (?,?)
ON DUPLICATE KEY UPDATE
LBin = VALUES(LBin);
`,[await this.UnifiedArray(auctionid,lbin)]);
}
else if(Array.isArray(auctionid) || Array.isArray(lbin))
throw Error(`Upsert SQL Function error - cannot unify collection ${Array.isArray(auctionid) ? auctionid : lbin} to singlet ${!Array.isArray(auctionid) ? auctionid : lbin}`);
else {
await conn.query(`
INSERT INTO auctions (auctionid, LBin)
VALUES (?,?)
ON DUPLICATE KEY UPDATE
LBin = VALUES(LBin);
`, [auctionid, lbin]);
}
await conn.query('COMMIT');
}
catch(error)
{
console.error("message_before_error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally
{
if (conn) conn.release();
}
}
//REMOVE ELEMENT IN AUCTIONS TABLE
public static async Remove(auctionid: string|string[]) {
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
await conn.query('BEGIN');
await conn.query(`
DELETE FROM auctions WHERE auctionid = ?
`,[auctionid]);
await conn.query('COMMIT');
}
catch (error) {
console.error("InitDB Error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally {
if (conn) conn.release();
}
}
public static async Query(query:string,use_transaction:boolean = true) {
let conn: PoolClient | undefined;
try{
conn = await this.pool.connect()
if (use_transaction) await conn.query('BEGIN');
await conn.query(query);
if (use_transaction) await conn.query('COMMIT');
}
catch(error) {
console.error("InitDB Error: ", error);
if (use_transaction && conn) await conn.query('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<boolean|any> {
let conn: PoolClient | undefined;
let result;
try{
conn = await this.pool.connect();
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 unify collection ${Array.isArray(auctionid) ? auctionid : lbin} to singlet ${!Array.isArray(auctionid) ? auctionid : lbin}`);
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.rows.length);
}
}
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:PoolClient|undefined;
try{
conn = await this.pool.connect();
await conn.query('BEGIN');
//CODE HERE//
await conn.query('COMMIT');
}
catch(error)
{
console.error("message_before_error: ", error);
if(conn) await conn.query('ROLLBACK');
throw error;
}
finally
{
if(conn) conn.release();
}
*/
private static async UnifiedArray<T1, T2>(auctionid: string[], lbin: number[]): Promise<{ x: string[], y: number[] }> {
return { x: [...auctionid], y: [...lbin] };
}
}
export {
SqlSystem
}