144 lines
6.3 KiB
JavaScript
144 lines
6.3 KiB
JavaScript
import pg from 'pg';
|
|
const { Pool } = pg;
|
|
|
|
// Simple query translator from MySQL dialect to PostgreSQL
|
|
function translateQuery(query, params) {
|
|
if (typeof query !== 'string') return { sql: query, pgParams: params };
|
|
|
|
let sql = query;
|
|
let pgParams = params ? [...params] : [];
|
|
|
|
// Remove or convert backticks
|
|
sql = sql.replace(/`([^`]+)`/g, '"$1"');
|
|
|
|
// INSERT INTO table SET ?
|
|
if (sql.match(/INSERT\s+INTO\s+([^\s]+)\s+SET\s+\?/i)) {
|
|
const table = sql.match(/INSERT\s+INTO\s+([^\s]+)\s+SET\s+\?/i)[1].replace(/"/g, '');
|
|
const dataObj = pgParams[0];
|
|
if (dataObj && typeof dataObj === 'object') {
|
|
const keys = Object.keys(dataObj);
|
|
const cols = keys.map(k => `"${k}"`).join(', ');
|
|
const valuesPlaceholder = keys.map((_, idx) => `$${idx + 1}`).join(', ');
|
|
sql = `INSERT INTO "${table}" (${cols}) VALUES (${valuesPlaceholder})`;
|
|
pgParams = keys.map(k => dataObj[k]);
|
|
}
|
|
}
|
|
|
|
// UPDATE table SET ? WHERE ...
|
|
if (sql.match(/UPDATE\s+([^\s]+)\s+SET\s+\?\s+WHERE\s+(.+)/i)) {
|
|
const match = sql.match(/UPDATE\s+([^\s]+)\s+SET\s+\?\s+WHERE\s+(.+)/i);
|
|
const table = match[1].replace(/"/g, '');
|
|
const whereClause = match[2];
|
|
const dataObj = pgParams[0];
|
|
const extraParams = pgParams.slice(1);
|
|
|
|
if (dataObj && typeof dataObj === 'object') {
|
|
const keys = Object.keys(dataObj);
|
|
let paramIdx = 1;
|
|
const setClause = keys.map(k => `"${k}" = $${paramIdx++}`).join(', ');
|
|
|
|
let postgresWhere = whereClause;
|
|
let tempIdx = paramIdx;
|
|
postgresWhere = postgresWhere.replace(/\?/g, () => `$${tempIdx++}`);
|
|
|
|
sql = `UPDATE "${table}" SET ${setClause} WHERE ${postgresWhere}`;
|
|
pgParams = [...keys.map(k => dataObj[k]), ...extraParams];
|
|
}
|
|
}
|
|
|
|
// Translate standard '?' to '$1', '$2', etc.
|
|
let count = 1;
|
|
sql = sql.replace(/\?/g, () => `$${count++}`);
|
|
|
|
// ON DUPLICATE KEY UPDATE -> ON CONFLICT
|
|
if (sql.match(/ON\s+DUPLICATE\s+KEY\s+UPDATE/i)) {
|
|
if (sql.toLowerCase().includes('settings')) {
|
|
sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE\s+data\s*=\s*VALUES\(data\)|ON\s+DUPLICATE\s+KEY\s+UPDATE\s+data\s*=\s*\$4|ON\s+DUPLICATE\s+KEY\s+UPDATE\s+data\s*=\s*EXCLUDED\.data/i, 'ON CONFLICT (id) DO UPDATE SET data = EXCLUDED.data');
|
|
} else if (sql.toLowerCase().includes('google_tokens')) {
|
|
sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (owner_id) DO UPDATE SET refresh_token = EXCLUDED.refresh_token, access_token = EXCLUDED.access_token, expiry_date = EXCLUDED.expiry_date');
|
|
} else if (sql.toLowerCase().includes('usuarios')) {
|
|
sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (id) DO UPDATE SET nome = EXCLUDED.nome, cro = EXCLUDED.cro, cro_uf = EXCLUDED.cro_uf, celular = EXCLUDED.celular, especialidade = EXCLUDED.especialidade');
|
|
} else if (sql.toLowerCase().includes('vinculos')) {
|
|
sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (usuario_id, clinica_id) DO UPDATE SET role = EXCLUDED.role');
|
|
} else if (sql.toLowerCase().includes('dentistas_horarios')) {
|
|
sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (id) DO UPDATE SET dia_semana = EXCLUDED.dia_semana, hora_inicio = EXCLUDED.hora_inicio, hora_fim = EXCLUDED.hora_fim, ativo = EXCLUDED.ativo');
|
|
} else if (sql.toLowerCase().includes('dentistas')) {
|
|
sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (id) DO UPDATE SET nome = EXCLUDED.nome, especialidade = EXCLUDED.especialidade');
|
|
} else if (sql.toLowerCase().includes('procedimentos_valores_planos')) {
|
|
sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (id) DO UPDATE SET valor = EXCLUDED.valor');
|
|
} else if (sql.toLowerCase().includes('procedimentos')) {
|
|
sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (id) DO UPDATE SET nome = EXCLUDED.nome, "especialidadeId" = EXCLUDED."especialidadeId", "especialidadeNome" = EXCLUDED."especialidadeNome", "valorParticular" = EXCLUDED."valorParticular", "codigoInterno" = EXCLUDED."codigoInterno", categoria = EXCLUDED.categoria, tipo_regiao = EXCLUDED.tipo_regiao, exige_face = EXCLUDED.exige_face, codigo = EXCLUDED.codigo');
|
|
}
|
|
}
|
|
|
|
// DATE_FORMAT -> TO_CHAR
|
|
sql = sql.replace(/DATE_FORMAT\(([^,]+),\s*['"]%Y-%m['"]\)/gi, "TO_CHAR($1, 'YYYY-MM')");
|
|
// DATE_SUB -> CURRENT_DATE - INTERVAL
|
|
sql = sql.replace(/DATE_SUB\(CURDATE\(\),\s*INTERVAL\s+(\d+)\s+MONTH\)/gi, "CURRENT_DATE - INTERVAL '$1 MONTH'");
|
|
|
|
// SET FOREIGN_KEY_CHECKS
|
|
if (sql.match(/SET\s+FOREIGN_KEY_CHECKS\s*=\s*\d+/i)) {
|
|
sql = "SELECT 1";
|
|
}
|
|
|
|
return { sql, pgParams };
|
|
}
|
|
|
|
class PostgresConnection {
|
|
constructor(client) {
|
|
this.client = client;
|
|
}
|
|
|
|
async query(query, params) {
|
|
const { sql, pgParams } = translateQuery(query, params);
|
|
const res = await this.client.query(sql, pgParams);
|
|
return [res.rows, res.fields];
|
|
}
|
|
|
|
async beginTransaction() {
|
|
await this.client.query('BEGIN');
|
|
}
|
|
|
|
async commit() {
|
|
await this.client.query('COMMIT');
|
|
}
|
|
|
|
async rollback() {
|
|
await this.client.query('ROLLBACK');
|
|
}
|
|
|
|
release() {
|
|
this.client.release();
|
|
}
|
|
}
|
|
|
|
class PostgresPool {
|
|
constructor(config) {
|
|
// Convert connection string from postgresql:// to pool config
|
|
const connectionString = process.env.DATABASE_URL;
|
|
this.pool = new Pool({
|
|
connectionString: connectionString || `postgresql://${config.user}:${config.password}@${config.host}:5432/${config.database}`,
|
|
ssl: connectionString && connectionString.includes('localhost') ? false : { rejectUnauthorized: false }
|
|
});
|
|
}
|
|
|
|
async query(query, params) {
|
|
const { sql, pgParams } = translateQuery(query, params);
|
|
const res = await this.pool.query(sql, pgParams);
|
|
return [res.rows, res.fields];
|
|
}
|
|
|
|
async getConnection() {
|
|
const client = await this.pool.connect();
|
|
return new PostgresConnection(client);
|
|
}
|
|
|
|
async end() {
|
|
await this.pool.end();
|
|
}
|
|
}
|
|
|
|
export default {
|
|
createPool: (config) => new PostgresPool(config)
|
|
};
|