Prima importazione
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
const { Pool } = require('pg');
|
||||
|
||||
const dbType = (process.env.DB_TYPE || 'postgres').toLowerCase();
|
||||
|
||||
let pool;
|
||||
|
||||
if (dbType === 'mysql' || dbType === 'mariadb') {
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
// Query translation helper for MySQL/MariaDB compatibility
|
||||
function translateQuery(sql, params = []) {
|
||||
let translatedSql = sql;
|
||||
let translatedParams = [...params];
|
||||
|
||||
// 1. Replace Postgres placeholders ($1, $2, ...) with MySQL placeholders (?)
|
||||
// Reorder and duplicate parameters to match the sequence of ? placeholders
|
||||
const placeholders = [...translatedSql.matchAll(/\$([0-9]+)/g)];
|
||||
if (placeholders.length > 0) {
|
||||
const newParams = [];
|
||||
for (const match of placeholders) {
|
||||
const index = parseInt(match[1], 10) - 1;
|
||||
newParams.push(params[index]);
|
||||
}
|
||||
translatedParams = newParams;
|
||||
translatedSql = translatedSql.replace(/\$[0-9]+/g, '?');
|
||||
}
|
||||
|
||||
// 2. ILIKE -> LIKE (MySQL LIKE is case-insensitive by default)
|
||||
translatedSql = translatedSql.replace(/\bILIKE\b/gi, 'LIKE');
|
||||
|
||||
// 3. PostgreSQL string concatenation '||' -> CONCAT(...) in ticket number generator
|
||||
translatedSql = translatedSql.replace(/md5\(random\(\)::text\s*\|\|\s*clock_timestamp\(\)::text\)/gi, 'MD5(CONCAT(RAND(), NOW()))');
|
||||
|
||||
// 4. EXTRACT(EPOCH FROM NOW()) -> UNIX_TIMESTAMP()
|
||||
translatedSql = translatedSql.replace(/EXTRACT\(EPOCH\s+FROM\s+NOW\(\)\)::INTEGER/gi, 'UNIX_TIMESTAMP()');
|
||||
translatedSql = translatedSql.replace(/EXTRACT\(EPOCH\s+FROM\s+NOW\(\)\)/gi, 'UNIX_TIMESTAMP()');
|
||||
|
||||
// 5. date_trunc('week', CURRENT_DATE) -> DATE_SUB(CURRENT_DATE, INTERVAL WEEKDAY(CURRENT_DATE) DAY)
|
||||
translatedSql = translatedSql.replace(/date_trunc\('week',\s*CURRENT_DATE\)/gi, 'DATE_SUB(CURRENT_DATE, INTERVAL WEEKDAY(CURRENT_DATE) DAY)');
|
||||
|
||||
// 6. Transaction commands
|
||||
if (translatedSql.trim().toUpperCase() === 'BEGIN') {
|
||||
translatedSql = 'START TRANSACTION';
|
||||
}
|
||||
|
||||
// 7. RETURNING clauses (MySQL doesn't support them)
|
||||
let returningId = false;
|
||||
let returningCounter = false;
|
||||
let returningIdTn = false;
|
||||
|
||||
const returningMatch = translatedSql.match(/\bRETURNING\s+(.+)$/i);
|
||||
if (returningMatch) {
|
||||
const fields = returningMatch[1].trim().toLowerCase();
|
||||
if (fields === 'id') {
|
||||
returningId = true;
|
||||
} else if (fields === 'counter') {
|
||||
returningCounter = true;
|
||||
} else if (fields === 'id, tn' || fields === 'id,tn') {
|
||||
returningIdTn = true;
|
||||
}
|
||||
translatedSql = translatedSql.replace(/\bRETURNING\s+.+$/i, '');
|
||||
}
|
||||
|
||||
// 8. Subquery replacement to avoid MySQL "target table twice" error in counter insert
|
||||
translatedSql = translatedSql.replace(/SELECT\s+MAX\(counter\)\s+FROM\s+ticket_number_counter/gi, 'SELECT MAX(counter) FROM (SELECT counter FROM ticket_number_counter) AS tmp_counter_val');
|
||||
|
||||
// 9. Remove Postgres-specific casts
|
||||
translatedSql = translatedSql.replace(/::text/gi, '');
|
||||
translatedSql = translatedSql.replace(/::integer/gi, '');
|
||||
translatedSql = translatedSql.replace(/::bigint/gi, '');
|
||||
translatedSql = translatedSql.replace(/::numeric/gi, '');
|
||||
|
||||
return {
|
||||
translatedSql,
|
||||
translatedParams,
|
||||
postProcess: async (result, connection) => {
|
||||
const [rowsOrHeader] = result;
|
||||
let rows = [];
|
||||
let rowCount = 0;
|
||||
|
||||
if (Array.isArray(rowsOrHeader)) {
|
||||
rows = rowsOrHeader;
|
||||
rowCount = rows.length;
|
||||
} else if (rowsOrHeader) {
|
||||
rowCount = rowsOrHeader.affectedRows || 0;
|
||||
if (returningId) {
|
||||
rows = [{ id: rowsOrHeader.insertId }];
|
||||
} else if (returningIdTn) {
|
||||
rows = [{ id: rowsOrHeader.insertId, tn: params[0] }];
|
||||
} else if (returningCounter) {
|
||||
const [counterResult] = await connection.query('SELECT MAX(counter) AS counter FROM ticket_number_counter');
|
||||
rows = [{ counter: counterResult[0] ? counterResult[0].counter : 1 }];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
rows,
|
||||
rowCount,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class CompatPool {
|
||||
constructor() {
|
||||
this.mysqlPool = mysql.createPool({
|
||||
host: process.env.DB_HOST || '127.0.0.1',
|
||||
port: parseInt(process.env.DB_PORT, 10) || 3306,
|
||||
database: process.env.DB_NAME || 'otrs',
|
||||
user: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
connectionLimit: 20,
|
||||
idleTimeout: 30000,
|
||||
connectTimeout: 5000,
|
||||
});
|
||||
}
|
||||
|
||||
async query(sql, params = []) {
|
||||
const { translatedSql, translatedParams, postProcess } = translateQuery(sql, params);
|
||||
const conn = await this.mysqlPool.getConnection();
|
||||
try {
|
||||
const res = await conn.query(translatedSql, translatedParams);
|
||||
return await postProcess(res, conn);
|
||||
} finally {
|
||||
conn.release();
|
||||
}
|
||||
}
|
||||
|
||||
async connect() {
|
||||
const conn = await this.mysqlPool.getConnection();
|
||||
return {
|
||||
query: async (sql, params = []) => {
|
||||
const { translatedSql, translatedParams, postProcess } = translateQuery(sql, params);
|
||||
const res = await conn.query(translatedSql, translatedParams);
|
||||
return await postProcess(res, conn);
|
||||
},
|
||||
release: () => {
|
||||
conn.release();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async end() {
|
||||
await this.mysqlPool.end();
|
||||
}
|
||||
}
|
||||
|
||||
pool = new CompatPool();
|
||||
} else {
|
||||
// Standard PostgreSQL Pool
|
||||
pool = new Pool({
|
||||
host: process.env.DB_HOST || '127.0.0.1',
|
||||
port: parseInt(process.env.DB_PORT, 10) || 5432,
|
||||
database: process.env.DB_NAME || 'otrs',
|
||||
user: process.env.DB_USER || 'otrs',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
max: 20,
|
||||
idleTimeoutMillis: 30000,
|
||||
connectionTimeoutMillis: 5000,
|
||||
});
|
||||
|
||||
pool.on('error', (err) => {
|
||||
console.error('Unexpected error on idle client', err);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = pool;
|
||||
Reference in New Issue
Block a user