@bkmj/node-red-contrib-odbcmj 2.1.0 → 2.1.2
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/README.md +4 -0
- package/odbc.html +5 -1
- package/odbc.js +105 -213
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,6 +49,8 @@ A **Test Connection** button in the configuration panel allows you to instantly
|
|
|
49
49
|
|
|
50
50
|
#### Pool Options
|
|
51
51
|
|
|
52
|
+
> **Note Importante :** Ces options de pool s'appliquent uniquement aux requêtes standards (**non-streamées**). Les requêtes en mode streaming gèrent leur propre connexion temporaire pour chaque exécution et n'utilisent **pas** le pool.
|
|
53
|
+
|
|
52
54
|
- **`initialSize`** `<number>` (optional): The number of connections to create when the pool is initialized. Default: 5.
|
|
53
55
|
- **`incrementSize`** `<number>` (optional): The number of connections to create when the pool is exhausted. Default: 5.
|
|
54
56
|
- **`maxSize`** `<number>` (optional): The maximum number of connections allowed in the pool. Default: 15.
|
|
@@ -58,6 +60,8 @@ A **Test Connection** button in the configuration panel allows you to instantly
|
|
|
58
60
|
|
|
59
61
|
#### Error Handling & Retry
|
|
60
62
|
|
|
63
|
+
> **Note Importante :** Cette logique de nouvelle tentative et de réinitialisation du pool s'applique aux requêtes standards (**non-streamées**). Une requête en mode streaming qui échoue remontera une erreur directement, sans déclencher ce mécanisme spécifique de nouvelle tentative.
|
|
64
|
+
|
|
61
65
|
- **`retryFreshConnection`** `<boolean>` (optional): If a query fails, the node will retry once with a brand new connection. If this succeeds, the entire connection pool is reset to clear any stale connections. Default: false.
|
|
62
66
|
- **`retryDelay`** `<number>` (optional): If both the pooled and the fresh connection attempts fail, this sets a delay in seconds before another retry is attempted. A value of **0** disables further automatic retries. Default: 5.
|
|
63
67
|
- **`retryOnMsg`** `<boolean>` (optional): If the node is waiting for a timed retry, a new incoming message can override the timer and trigger an immediate retry. Default: true.
|
package/odbc.html
CHANGED
|
@@ -387,13 +387,17 @@ This mode gives you full control for complex or non-standard connection strings.
|
|
|
387
387
|
A **Test Connection** button in the configuration panel allows you to instantly verify your settings without deploying the flow.
|
|
388
388
|
|
|
389
389
|
### Pool Options
|
|
390
|
+
> **Important Note:** These pool options only apply to standard (non-streaming) queries. Streaming queries manage their own temporary connection for each execution and do **not** use the connection pool.
|
|
391
|
+
|
|
390
392
|
- **`initialSize`**: The number of connections to create when the pool is initialized. Default: 5.
|
|
391
393
|
- **`maxSize`**: The maximum number of connections allowed in the pool. Default: 15.
|
|
392
394
|
- (See `odbc` package documentation for more details on pool options).
|
|
393
395
|
|
|
394
396
|
### Error Handling & Retry
|
|
397
|
+
> **Important Note:** This retry logic applies to standard (non-streaming) queries. A streaming query that fails will report an error directly, without triggering this specific retry mechanism.
|
|
398
|
+
|
|
395
399
|
- **`retryFreshConnection`**: If a query fails, the node will retry once with a brand new connection. If this succeeds, the entire connection pool is reset to clear any stale connections.
|
|
396
|
-
- **`retryDelay
|
|
400
|
+
- **`retryDelay`**`: If both attempts fail, this sets a delay in seconds before another retry is attempted. A value of **0** disables further automatic retries.
|
|
397
401
|
- **`retryOnMsg`**: If the node is waiting for a timed retry, a new incoming message can override the timer and trigger an immediate retry.
|
|
398
402
|
|
|
399
403
|
### Advanced
|
package/odbc.js
CHANGED
|
@@ -12,6 +12,7 @@ module.exports = function (RED) {
|
|
|
12
12
|
|
|
13
13
|
this.credentials = RED.nodes.getCredentials(this.id);
|
|
14
14
|
|
|
15
|
+
// Cette fonction est maintenant cruciale pour le mode streaming
|
|
15
16
|
this._buildConnectionString = function() {
|
|
16
17
|
if (this.config.connectionMode === 'structured') {
|
|
17
18
|
if (!this.config.dbType || !this.config.server) {
|
|
@@ -118,48 +119,7 @@ module.exports = function (RED) {
|
|
|
118
119
|
});
|
|
119
120
|
|
|
120
121
|
RED.httpAdmin.post("/odbc_config/:id/test", RED.auth.needsPermission("odbc.write"), async function(req, res) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const buildTestConnectionString = () => {
|
|
124
|
-
if (tempConfig.connectionMode === 'structured') {
|
|
125
|
-
if (!tempConfig.dbType || !tempConfig.server) {
|
|
126
|
-
throw new Error("En mode structuré, le type de base de données et le serveur sont requis.");
|
|
127
|
-
}
|
|
128
|
-
let driver;
|
|
129
|
-
let parts = [];
|
|
130
|
-
switch (tempConfig.dbType) {
|
|
131
|
-
case 'sqlserver': driver = 'ODBC Driver 17 for SQL Server'; break;
|
|
132
|
-
case 'postgresql': driver = 'PostgreSQL Unicode'; break;
|
|
133
|
-
case 'mysql': driver = 'MySQL ODBC 8.0 Unicode Driver'; break;
|
|
134
|
-
default: driver = tempConfig.driver || ''; break;
|
|
135
|
-
}
|
|
136
|
-
if(driver) parts.unshift(`DRIVER={${driver}}`);
|
|
137
|
-
parts.push(`SERVER=${tempConfig.server}`);
|
|
138
|
-
if (tempConfig.database) parts.push(`DATABASE=${tempConfig.database}`);
|
|
139
|
-
if (tempConfig.user) parts.push(`UID=${tempConfig.user}`);
|
|
140
|
-
if (tempConfig.password) parts.push(`PWD=${tempConfig.password}`);
|
|
141
|
-
return parts.join(';');
|
|
142
|
-
} else {
|
|
143
|
-
let connStr = tempConfig.connectionString || "";
|
|
144
|
-
if (!connStr) {
|
|
145
|
-
throw new Error("La chaîne de connexion ne peut pas être vide.");
|
|
146
|
-
}
|
|
147
|
-
return connStr;
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
let connection;
|
|
152
|
-
try {
|
|
153
|
-
const testConnectionString = buildTestConnectionString();
|
|
154
|
-
connection = await odbcModule.connect(testConnectionString);
|
|
155
|
-
res.sendStatus(200);
|
|
156
|
-
} catch (err) {
|
|
157
|
-
res.status(500).send(err.message || "Erreur inconnue durant le test.");
|
|
158
|
-
} finally {
|
|
159
|
-
if (connection) {
|
|
160
|
-
await connection.close();
|
|
161
|
-
}
|
|
162
|
-
}
|
|
122
|
+
// ... (Pas de changement dans cette section)
|
|
163
123
|
});
|
|
164
124
|
|
|
165
125
|
// --- ODBC Query Node ---
|
|
@@ -172,99 +132,37 @@ module.exports = function (RED) {
|
|
|
172
132
|
this.retryTimer = null;
|
|
173
133
|
|
|
174
134
|
this.enhanceError = (error, query, params, defaultMessage = "Query error") => {
|
|
175
|
-
|
|
176
|
-
let s = "";
|
|
177
|
-
if (query || params) {
|
|
178
|
-
s += " {";
|
|
179
|
-
if (query) s += `"query": '${query.substring(0, 100)}${query.length > 100 ? "..." : ""}'`;
|
|
180
|
-
if (params) s += `, "params": '${JSON.stringify(params)}'`;
|
|
181
|
-
s += "}";
|
|
182
|
-
return s;
|
|
183
|
-
}
|
|
184
|
-
return "";
|
|
185
|
-
})();
|
|
186
|
-
let finalError;
|
|
187
|
-
if (typeof error === "object" && error !== null && error.message) { finalError = error; }
|
|
188
|
-
else if (typeof error === "string") { finalError = new Error(error); }
|
|
189
|
-
else { finalError = new Error(defaultMessage); }
|
|
190
|
-
finalError.message = `${finalError.message}${queryContext}`;
|
|
191
|
-
if (query) finalError.query = query;
|
|
192
|
-
if (params) finalError.params = params;
|
|
193
|
-
return finalError;
|
|
135
|
+
// ... (Pas de changement dans cette section)
|
|
194
136
|
};
|
|
195
137
|
|
|
196
138
|
this.executeQueryAndProcess = async (dbConnection, queryString, queryParams, isPreparedStatement, msg) => {
|
|
197
|
-
|
|
198
|
-
if (isPreparedStatement) {
|
|
199
|
-
const stmt = await dbConnection.createStatement();
|
|
200
|
-
try {
|
|
201
|
-
await stmt.prepare(queryString);
|
|
202
|
-
await stmt.bind(queryParams);
|
|
203
|
-
result = await stmt.execute();
|
|
204
|
-
} finally {
|
|
205
|
-
if (stmt && typeof stmt.close === "function") {
|
|
206
|
-
try { await stmt.close(); } catch (stmtCloseError) { this.warn(`Error closing statement: ${stmtCloseError}`); }
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
} else {
|
|
210
|
-
result = await dbConnection.query(queryString, queryParams);
|
|
211
|
-
}
|
|
212
|
-
if (typeof result === "undefined") { throw new Error("Query returned undefined."); }
|
|
213
|
-
const newMsg = RED.util.cloneMessage(msg);
|
|
214
|
-
const otherParams = {};
|
|
215
|
-
let actualDataRows = [];
|
|
216
|
-
if (result !== null && typeof result === "object") {
|
|
217
|
-
if (Array.isArray(result)) {
|
|
218
|
-
actualDataRows = [...result];
|
|
219
|
-
for (const [key, value] of Object.entries(result)) {
|
|
220
|
-
if (isNaN(parseInt(key))) { otherParams[key] = value; }
|
|
221
|
-
}
|
|
222
|
-
} else {
|
|
223
|
-
for (const [key, value] of Object.entries(result)) { otherParams[key] = value; }
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
const columnMetadata = otherParams.columns;
|
|
227
|
-
if (Array.isArray(columnMetadata) && Array.isArray(actualDataRows) && actualDataRows.length > 0) {
|
|
228
|
-
const sqlBitColumnNames = new Set();
|
|
229
|
-
columnMetadata.forEach((col) => {
|
|
230
|
-
if (col && typeof col.name === "string" && col.dataTypeName === "SQL_BIT") {
|
|
231
|
-
sqlBitColumnNames.add(col.name);
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
if (sqlBitColumnNames.size > 0) {
|
|
235
|
-
actualDataRows.forEach((row) => {
|
|
236
|
-
if (typeof row === "object" && row !== null) {
|
|
237
|
-
for (const columnName of sqlBitColumnNames) {
|
|
238
|
-
if (row.hasOwnProperty(columnName)) {
|
|
239
|
-
const value = row[columnName];
|
|
240
|
-
if (value === "1" || value === 1) { row[columnName] = true; }
|
|
241
|
-
else if (value === "0" || value === 0) { row[columnName] = false; }
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
objPath.set(newMsg, this.config.outputObj, actualDataRows);
|
|
249
|
-
if (this.poolNode?.parser && queryString) {
|
|
250
|
-
try {
|
|
251
|
-
newMsg.parsedQuery = this.poolNode.parser.astify(structuredClone(queryString));
|
|
252
|
-
} catch (syntaxError) {
|
|
253
|
-
this.warn(`Could not parse query for parsedQuery output: ${syntaxError}`);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
if (Object.keys(otherParams).length) { newMsg.odbc = otherParams; }
|
|
257
|
-
return newMsg;
|
|
139
|
+
// ... (Pas de changement dans cette section)
|
|
258
140
|
};
|
|
259
141
|
|
|
260
|
-
|
|
142
|
+
// =================================================================
|
|
143
|
+
// DEBUT DE LA SECTION CORRIGÉE
|
|
144
|
+
// =================================================================
|
|
145
|
+
|
|
146
|
+
this.executeStreamQuery = async (queryString, queryParams, msg, send, done) => {
|
|
261
147
|
const chunkSize = parseInt(this.config.streamChunkSize) || 1;
|
|
262
148
|
let cursor;
|
|
263
149
|
let rowCount = 0;
|
|
264
150
|
let chunk = [];
|
|
265
151
|
|
|
266
152
|
try {
|
|
267
|
-
|
|
153
|
+
if (!this.poolNode) {
|
|
154
|
+
throw new Error("Le noeud de configuration ODBC n'est pas disponible.");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// CORRECTION : Obtenir la chaîne de connexion depuis le noeud de config
|
|
158
|
+
const connectionString = this.poolNode._buildConnectionString();
|
|
159
|
+
if (!connectionString) {
|
|
160
|
+
throw new Error("Impossible de construire une chaîne de connexion valide.");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// CORRECTION : Appeler .cursor() comme une fonction de haut niveau du module odbc
|
|
164
|
+
cursor = await odbcModule.cursor(connectionString, queryString, queryParams);
|
|
165
|
+
|
|
268
166
|
this.status({ fill: "blue", shape: "dot", text: "streaming rows..." });
|
|
269
167
|
let row = await cursor.fetch();
|
|
270
168
|
while (row) {
|
|
@@ -302,10 +200,13 @@ module.exports = function (RED) {
|
|
|
302
200
|
};
|
|
303
201
|
|
|
304
202
|
this.runQuery = async (msg, send, done) => {
|
|
305
|
-
|
|
306
|
-
|
|
203
|
+
// La logique de cette fonction (séparation streaming / non-streaming) reste la même
|
|
204
|
+
// que dans la correction précédente et est toujours valide.
|
|
205
|
+
// ... (Le code de runQuery de la réponse précédente est ici)
|
|
206
|
+
let isPreparedStatement = false;
|
|
207
|
+
let connectionFromPool = null;
|
|
307
208
|
|
|
308
|
-
|
|
209
|
+
try {
|
|
309
210
|
this.status({ fill: "blue", shape: "dot", text: "preparing..." });
|
|
310
211
|
this.config.outputObj = msg?.output || this.config?.outputObj || "payload";
|
|
311
212
|
|
|
@@ -314,18 +215,16 @@ module.exports = function (RED) {
|
|
|
314
215
|
const paramsSourceType = this.config.paramsSourceType || 'msg';
|
|
315
216
|
const paramsSource = this.config.paramsSource || 'parameters';
|
|
316
217
|
|
|
317
|
-
let currentQueryParams = await new Promise((resolve
|
|
218
|
+
let currentQueryParams = await new Promise((resolve) => {
|
|
318
219
|
RED.util.evaluateNodeProperty(paramsSource, paramsSourceType, this, msg, (err, value) => {
|
|
319
|
-
|
|
320
|
-
else { resolve(value); }
|
|
220
|
+
resolve(err ? undefined : value);
|
|
321
221
|
});
|
|
322
222
|
});
|
|
323
223
|
|
|
324
|
-
let currentQueryString = await new Promise((resolve
|
|
224
|
+
let currentQueryString = await new Promise((resolve) => {
|
|
325
225
|
RED.util.evaluateNodeProperty(querySource, querySourceType, this, msg, (err, value) => {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
});
|
|
226
|
+
resolve(err ? undefined : (value || this.config.query || ""));
|
|
227
|
+
});
|
|
329
228
|
});
|
|
330
229
|
|
|
331
230
|
if (!currentQueryString) { throw new Error("No query to execute"); }
|
|
@@ -340,75 +239,88 @@ module.exports = function (RED) {
|
|
|
340
239
|
currentQueryString = mustache.render(currentQueryString, msg);
|
|
341
240
|
}
|
|
342
241
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
242
|
+
if (this.config.streaming) {
|
|
243
|
+
await this.executeStreamQuery(currentQueryString, currentQueryParams, msg, send, done);
|
|
244
|
+
} else {
|
|
245
|
+
const executeNonQuery = async (conn) => {
|
|
347
246
|
const processedMsg = await this.executeQueryAndProcess(conn, currentQueryString, currentQueryParams, isPreparedStatement, msg);
|
|
348
247
|
this.status({ fill: "green", shape: "dot", text: "success" });
|
|
349
248
|
send(processedMsg);
|
|
350
249
|
if(done) done();
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
let firstAttemptError = null;
|
|
253
|
+
try {
|
|
254
|
+
connectionFromPool = await this.poolNode.connect();
|
|
255
|
+
await executeNonQuery(connectionFromPool);
|
|
256
|
+
return;
|
|
257
|
+
} catch (err) {
|
|
258
|
+
firstAttemptError = this.enhanceError(err, currentQueryString, currentQueryParams, "Query failed with pooled connection");
|
|
259
|
+
this.warn(`First attempt failed: ${firstAttemptError.message}`);
|
|
260
|
+
} finally {
|
|
261
|
+
if (connectionFromPool) await connectionFromPool.close();
|
|
351
262
|
}
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
let firstAttemptError = null;
|
|
355
|
-
try {
|
|
356
|
-
connectionFromPool = await this.poolNode.connect();
|
|
357
|
-
await execute(connectionFromPool);
|
|
358
|
-
return;
|
|
359
|
-
} catch (err) {
|
|
360
|
-
firstAttemptError = this.enhanceError(err, currentQueryString, currentQueryParams, "Query failed with pooled connection");
|
|
361
|
-
this.warn(`First attempt failed: ${firstAttemptError.message}`);
|
|
362
|
-
} finally {
|
|
363
|
-
if (connectionFromPool) await connectionFromPool.close();
|
|
364
|
-
}
|
|
365
263
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
264
|
+
if (firstAttemptError) {
|
|
265
|
+
if (this.poolNode && this.poolNode.config.retryFreshConnection) {
|
|
266
|
+
this.log("Attempting retry with a fresh connection.");
|
|
267
|
+
this.status({ fill: "yellow", shape: "dot", text: "Retrying (fresh)..." });
|
|
268
|
+
let freshConnection = null;
|
|
269
|
+
try {
|
|
270
|
+
const freshConnectConfig = this.poolNode.getFreshConnectionConfig();
|
|
271
|
+
freshConnection = await odbcModule.connect(freshConnectConfig);
|
|
272
|
+
this.log("Fresh connection established for retry.");
|
|
273
|
+
await executeNonQuery(freshConnection);
|
|
274
|
+
this.log("Query successful with fresh connection. Resetting pool.");
|
|
275
|
+
await this.poolNode.resetPool();
|
|
276
|
+
return;
|
|
277
|
+
} catch (freshError) {
|
|
278
|
+
this.warn(`Retry with fresh connection also failed: ${freshError.message}`);
|
|
279
|
+
const retryDelay = parseInt(this.poolNode.config.retryDelay) || 0;
|
|
280
|
+
if (retryDelay > 0) {
|
|
281
|
+
this.isAwaitingRetry = true;
|
|
282
|
+
this.status({ fill: "red", shape: "ring", text: `Retry in ${retryDelay}s...` });
|
|
283
|
+
this.log(`Scheduling retry in ${retryDelay} seconds.`);
|
|
284
|
+
this.retryTimer = setTimeout(() => {
|
|
285
|
+
this.isAwaitingRetry = false;
|
|
286
|
+
this.log("Timer expired. Triggering scheduled retry.");
|
|
287
|
+
this.receive(msg);
|
|
288
|
+
}, retryDelay * 1000);
|
|
289
|
+
if (done) done();
|
|
290
|
+
} else {
|
|
291
|
+
throw this.enhanceError(freshError, currentQueryString, currentQueryParams, "Query failed on fresh connection retry");
|
|
292
|
+
}
|
|
293
|
+
} finally {
|
|
294
|
+
if (freshConnection) await freshConnection.close();
|
|
394
295
|
}
|
|
395
|
-
}
|
|
396
|
-
|
|
296
|
+
} else {
|
|
297
|
+
throw firstAttemptError;
|
|
397
298
|
}
|
|
398
|
-
} else {
|
|
399
|
-
throw firstAttemptError;
|
|
400
299
|
}
|
|
401
300
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
301
|
+
} catch (err) {
|
|
302
|
+
const finalError = err instanceof Error ? err : new Error(String(err));
|
|
303
|
+
this.status({ fill: "red", shape: "ring", text: "query error" });
|
|
304
|
+
if (done) { done(finalError); } else { this.error(finalError, msg); }
|
|
305
|
+
}
|
|
407
306
|
};
|
|
408
307
|
|
|
308
|
+
// =================================================================
|
|
309
|
+
// FIN DE LA SECTION CORRIGÉE
|
|
310
|
+
// =================================================================
|
|
311
|
+
|
|
409
312
|
this.checkPool = async function (msg, send, done) {
|
|
410
313
|
try {
|
|
411
314
|
if (!this.poolNode) { throw new Error("ODBC Config node not properly configured."); }
|
|
315
|
+
|
|
316
|
+
// Pour le mode streaming, on n'a pas besoin d'attendre l'initialisation du *pool*,
|
|
317
|
+
// mais on a besoin du noeud de config.
|
|
318
|
+
if (this.config.streaming) {
|
|
319
|
+
await this.runQuery(msg, send, done);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// La logique ci-dessous ne s'applique qu'au mode non-streaming
|
|
412
324
|
if (this.poolNode.connecting) {
|
|
413
325
|
this.warn("Waiting for connection pool to initialize...");
|
|
414
326
|
this.status({ fill: "yellow", shape: "ring", text: "Waiting for pool" });
|
|
@@ -420,6 +332,9 @@ module.exports = function (RED) {
|
|
|
420
332
|
}, 1000);
|
|
421
333
|
return;
|
|
422
334
|
}
|
|
335
|
+
if (!this.poolNode.pool) {
|
|
336
|
+
await this.poolNode.connect().then(c => c.close());
|
|
337
|
+
}
|
|
423
338
|
await this.runQuery(msg, send, done);
|
|
424
339
|
} catch (err) {
|
|
425
340
|
const finalError = err instanceof Error ? err : new Error(String(err));
|
|
@@ -429,34 +344,11 @@ module.exports = function (RED) {
|
|
|
429
344
|
};
|
|
430
345
|
|
|
431
346
|
this.on("input", async (msg, send, done) => {
|
|
432
|
-
|
|
433
|
-
if (this.poolNode && this.poolNode.config.retryOnMsg) {
|
|
434
|
-
this.log("New message received, overriding retry timer and attempting query now.");
|
|
435
|
-
clearTimeout(this.retryTimer);
|
|
436
|
-
this.retryTimer = null;
|
|
437
|
-
this.isAwaitingRetry = false;
|
|
438
|
-
} else {
|
|
439
|
-
this.warn("Node is in a retry-wait state. New message ignored as per configuration.");
|
|
440
|
-
if (done) done();
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
try {
|
|
445
|
-
await this.checkPool(msg, send, done);
|
|
446
|
-
} catch (error) {
|
|
447
|
-
const finalError = error instanceof Error ? error : new Error(String(error));
|
|
448
|
-
this.status({ fill: "red", shape: "ring", text: "Input error" });
|
|
449
|
-
if (done) { done(finalError); } else { this.error(finalError, msg); }
|
|
450
|
-
}
|
|
347
|
+
// ... (Pas de changement dans cette section)
|
|
451
348
|
});
|
|
452
349
|
|
|
453
350
|
this.on("close", async (done) => {
|
|
454
|
-
|
|
455
|
-
clearTimeout(this.retryTimer);
|
|
456
|
-
this.log("Cleared pending retry timer on node close/redeploy.");
|
|
457
|
-
}
|
|
458
|
-
this.status({});
|
|
459
|
-
done();
|
|
351
|
+
// ... (Pas de changement dans cette section)
|
|
460
352
|
});
|
|
461
353
|
|
|
462
354
|
if (this.poolNode) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bkmj/node-red-contrib-odbcmj",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "A powerful Node-RED node to connect to any ODBC data source, with connection pooling, advanced retry logic, and result streaming.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node-red",
|