@areumtecnologia/mysql-db-handler 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // Áreum Tecnologia - Renan Moreira - renan.moreira@areum.com.br
2
+ const DataBaseCache = require('./lib/database-cache');
2
3
  const DataBase = require('./lib/database');
3
4
  const DataBaseHandler = require('./lib/databaseHandler');
4
5
  // We do not export the singleton by default in a library usually,
@@ -6,5 +7,6 @@ const DataBaseHandler = require('./lib/databaseHandler');
6
7
 
7
8
  module.exports = {
8
9
  DataBase,
9
- DataBaseHandler
10
+ DataBaseHandler,
11
+ DataBaseCache
10
12
  };
@@ -2,7 +2,7 @@
2
2
  const mysql = require('mysql2/promise');
3
3
  const NodeCache = require('node-cache');
4
4
 
5
- class DataBase {
5
+ class DataBaseCache {
6
6
  constructor(config = {}) {
7
7
  this.config = config;
8
8
  this.pool = mysql.createPool(this.config); // The pool is created once when the instance is created.
@@ -74,4 +74,4 @@ class DataBase {
74
74
  }
75
75
  }
76
76
 
77
- module.exports = DataBase;
77
+ module.exports = DataBaseCache;
package/lib/database.js CHANGED
@@ -31,8 +31,9 @@ class DataBase {
31
31
  * Executes a SQL query using a pooled connection.
32
32
  * @param {string} sql - The SQL query to execute.
33
33
  * @param {Array} [params] - The parameters for the SQL query.
34
- * @returns {Promise<Array>} The result set from the query.
35
- * @throws {Error} Throws an error if the query fails.
34
+ * @returns {Promise<Array|{error: Error}>} The result set from the query,
35
+ * or an object with an `error` property if the query fails.
36
+ * This prevents unhandled exceptions from crashing the application.
36
37
  */
37
38
  async query(sql, params) {
38
39
  let connection; // Mova a declaração da conexão para fora do bloco try
@@ -44,9 +45,12 @@ class DataBase {
44
45
  const [rows] = await connection.query(sql, params);
45
46
  return rows;
46
47
  } catch (error) {
47
- // Passo 3: Se a query falhar, logar e retornar o erro no formato desejado
48
+ // Passo 3: Logar o erro para diagnóstico
48
49
  console.error('Erro na execução da query:', error);
49
- return { error }; // Mantém o retorno do erro como um objeto
50
+
51
+ // Retorna o erro encapsulado em { error } para evitar crashes não tratados,
52
+ // mantendo o contrato documentado no README.
53
+ return { error };
50
54
  } finally {
51
55
  // Passo 4: Este bloco SEMPRE será executado, independentemente de sucesso ou falha no 'try'
52
56
  if (connection) {
@@ -49,16 +49,37 @@ class DatabaseHandler {
49
49
  const bindings = [];
50
50
  for (const item of strictCondition) {
51
51
  if (typeof item === 'string') {
52
+ // Operadores lógicos (AND / OR) são inseridos como-is, sem parênteses.
52
53
  clauses.push(item.toUpperCase());
53
54
  } else if (typeof item === 'object' && item !== null) {
54
55
  const key = Object.keys(item).find(k => k !== 'operator');
55
56
  if (!key) continue;
56
57
  const value = item[key];
57
- const operator = item.operator || '=';
58
- clauses.push(`\`${key}\` ${operator} ?`);
59
- bindings.push(value);
58
+ let operator = item.operator || '=';
59
+ let upperOp = String(operator).toUpperCase();
60
+
61
+ // Suporte a arrays => IN / NOT IN (consistente com _buildWhereClause)
62
+ if (Array.isArray(value) && upperOp === '=') {
63
+ operator = 'IN';
64
+ upperOp = 'IN';
65
+ }
66
+
67
+ if (Array.isArray(value) && (upperOp === 'IN' || upperOp === 'NOT IN')) {
68
+ if (value.length === 0) {
69
+ clauses.push(upperOp === 'IN' ? '1 = 0' : '1 = 1');
70
+ } else {
71
+ const placeholders = value.map(() => '?').join(', ');
72
+ clauses.push(`\`${key}\` ${operator} (${placeholders})`);
73
+ bindings.push(...value);
74
+ }
75
+ } else {
76
+ clauses.push(`\`${key}\` ${operator} ?`);
77
+ bindings.push(value);
78
+ }
60
79
  }
61
80
  }
81
+ // Junta com espaço para preservar operadores AND/OR intercalados.
82
+ // Ex: "(a = ? AND b = ?)" em vez de "(a = ? b = ?)" que era inválido.
62
83
  return { sql: `(${clauses.join(' ')})`, bindings };
63
84
  }
64
85
 
@@ -182,12 +203,31 @@ class DatabaseHandler {
182
203
  // --- Queries ---
183
204
  const baseWhereSql = baseWhere.sql ? `WHERE ${baseWhere.sql}` : '';
184
205
  const totalCountSql = `SELECT COUNT(*) as total FROM ${this.table} ${baseWhereSql}`;
185
- const [totalResult] = await this.executeQuery(totalCountSql, baseWhere.bindings);
186
- const recordsTotal = totalResult.total;
206
+ const totalResult = await this.executeQuery(totalCountSql, baseWhere.bindings);
207
+ // Contrato: executeQuery propaga { error } em caso de falha (não lança).
208
+ if (totalResult && totalResult.error) {
209
+ return {
210
+ draw: parseInt(draw || 0),
211
+ recordsTotal: 0,
212
+ recordsFiltered: 0,
213
+ data: [],
214
+ error: totalResult.error.message
215
+ };
216
+ }
217
+ const recordsTotal = (totalResult && totalResult[0] && totalResult[0].total) || 0;
187
218
 
188
219
  const filteredCountSql = `SELECT COUNT(*) as total FROM ${this.table} ${whereSql}`;
189
- const [filteredResult] = await this.executeQuery(filteredCountSql, finalBindings);
190
- const recordsFiltered = filteredResult.total;
220
+ const filteredResult = await this.executeQuery(filteredCountSql, finalBindings);
221
+ if (filteredResult && filteredResult.error) {
222
+ return {
223
+ draw: parseInt(draw || 0),
224
+ recordsTotal: parseInt(recordsTotal),
225
+ recordsFiltered: 0,
226
+ data: [],
227
+ error: filteredResult.error.message
228
+ };
229
+ }
230
+ const recordsFiltered = (filteredResult && filteredResult[0] && filteredResult[0].total) || 0;
191
231
 
192
232
  // CORREÇÃO: Construção segura da cláusula SELECT para evitar vírgulas duplas.
193
233
  const selectFields = ['*'];
@@ -197,6 +237,15 @@ class DatabaseHandler {
197
237
  }
198
238
  const dataSql = `SELECT ${selectFields.join(', ')} FROM ${this.table} ${whereSql} ${orderSql} ${limitSql}`;
199
239
  const data = await this.executeQuery(dataSql, finalBindings);
240
+ if (data && data.error) {
241
+ return {
242
+ draw: parseInt(draw || 0),
243
+ recordsTotal: parseInt(recordsTotal),
244
+ recordsFiltered: parseInt(recordsFiltered),
245
+ data: [],
246
+ error: data.error.message
247
+ };
248
+ }
200
249
 
201
250
  return {
202
251
  draw: parseInt(draw || 0),
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@areumtecnologia/mysql-db-handler",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "A powerful MySQL wrapper with connection pooling, singleton support, and advanced query handling for Node.js.",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
7
+ "test": "node tests/update_operators.test.js && node tests/selectToDatatable.test.js"
8
8
  },
9
9
  "author": "Renan Moreira / Áreum Tecnologia",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "mysql2": "^3.22.5"
12
+ "mysql2": "^3.23.1"
13
13
  }
14
14
  }
@@ -1,19 +0,0 @@
1
- // Áreum Tecnologia - Renan Moreira - renan.moreira@areum.com.br
2
- // src/database/database-singleton.js
3
- // Exemplo Singleton para a conexão com o banco de dados
4
- const { DB_HOST, DB_USER, DB_PASS } = require('../constants');
5
- const DataBase = require("./database");
6
-
7
- const dbi = new DataBase({
8
- host: DB_HOST,
9
- user: DB_USER,
10
- password: DB_PASS,
11
- database: '',
12
- charset: "utf8mb4",
13
- timezone: "-03:00",
14
- connectionLimit: 1, // Ajustado para um limite de conexão mais comum
15
- waitForConnections: true,
16
- queueLimit: 0
17
- });
18
- // console.log("DATABASE SINGLETON CREATED", dbi.id);
19
- module.exports = dbi;