@miatechnet/node-odbc 2.4.13 → 2.4.15

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@miatechnet/node-odbc",
3
3
  "description": "unixodbc bindings for node - Fork with multi-result set support for stored procedures",
4
- "version": "2.4.13",
4
+ "version": "2.4.15",
5
5
  "homepage": "https://github.com/ppimentela/node-odbc/",
6
6
  "main": "lib/odbc.js",
7
7
  "types": "lib/odbc.d.ts",
package/src/odbc.h CHANGED
@@ -185,6 +185,16 @@ typedef struct QueryOptions {
185
185
 
186
186
  } QueryOptions;
187
187
 
188
+ // ResultSetData - holds a complete result set with its column metadata
189
+ typedef struct ResultSetData {
190
+ std::vector<ColumnData*> rows;
191
+ Column **columns = NULL;
192
+ SQLSMALLINT column_count = 0;
193
+ ColumnBuffer *bound_columns = NULL;
194
+ SQLLEN rowCount = 0;
195
+ SQLUSMALLINT *row_status_array = NULL;
196
+ } ResultSetData;
197
+
188
198
  // StatementData
189
199
  typedef struct StatementData {
190
200
 
@@ -209,7 +219,7 @@ typedef struct StatementData {
209
219
  SQLLEN rowCount;
210
220
 
211
221
  // Multiple result sets support (added by Pablo Pimentel)
212
- std::vector<std::vector<ColumnData*>> allResultSets;
222
+ std::vector<ResultSetData> allResultSets;
213
223
 
214
224
  SQLSMALLINT maxColumnNameLength;
215
225
 
@@ -234,6 +244,7 @@ typedef struct StatementData {
234
244
 
235
245
  ~StatementData() {
236
246
  deleteColumns();
247
+ deleteAllResultSets();
237
248
 
238
249
  for (int i = 0; i < this->parameterCount; i++) {
239
250
  Parameter* parameter = this->parameters[i];
@@ -273,39 +284,106 @@ typedef struct StatementData {
273
284
  delete[] this->procedure; this->procedure = NULL;
274
285
  }
275
286
 
287
+
288
+ // Static helper method to free columns and bound_columns arrays
289
+ // Parameters:
290
+ // columns - Array of Column pointers to free
291
+ // column_count - Number of columns in the array
292
+ // bound_columns - Array of ColumnBuffer to free
293
+ // row_status_array - Array of row status to free
294
+ // Note: This method takes ownership of the pointers and frees them
295
+ static void freeColumnsAndBuffers(Column **columns, SQLSMALLINT column_count, ColumnBuffer *bound_columns, SQLUSMALLINT *row_status_array) {
296
+ if (columns != NULL && bound_columns != NULL) {
297
+ for (int i = 0; i < column_count; i++) {
298
+ switch (columns[i]->bind_type) {
299
+ case SQL_C_CHAR:
300
+ case SQL_C_UTINYINT:
301
+ case SQL_C_BINARY:
302
+ delete[] (SQLCHAR *)bound_columns[i].buffer;
303
+ break;
304
+ case SQL_C_WCHAR:
305
+ delete[] (SQLWCHAR *)bound_columns[i].buffer;
306
+ break;
307
+ case SQL_C_DOUBLE:
308
+ delete[] (SQLDOUBLE *)bound_columns[i].buffer;
309
+ break;
310
+ case SQL_C_USHORT:
311
+ delete[] (SQLUSMALLINT *)bound_columns[i].buffer;
312
+ break;
313
+ case SQL_C_SLONG:
314
+ delete[] (SQLUINTEGER *)bound_columns[i].buffer;
315
+ break;
316
+ case SQL_C_UBIGINT:
317
+ delete[] (SQLUBIGINT *)bound_columns[i].buffer;
318
+ break;
319
+ }
320
+
321
+ delete[] columns[i]->ColumnName;
322
+ delete[] bound_columns[i].length_or_indicator_array;
323
+ delete columns[i];
324
+ }
325
+ delete[] columns;
326
+ delete[] bound_columns;
327
+ }
328
+
329
+ delete[] row_status_array;
330
+ }
331
+
332
+ // Clean up all stored result sets in allResultSets
333
+ // This method frees all rows, columns, and buffers for each result set
334
+ // Called by the destructor to ensure all memory is properly released
335
+ void deleteAllResultSets() {
336
+ for (size_t rs_index = 0; rs_index < allResultSets.size(); rs_index++) {
337
+ ResultSetData &rsData = allResultSets[rs_index];
338
+
339
+ // Free rows
340
+ for (size_t h = 0; h < rsData.rows.size(); h++) {
341
+ delete[] rsData.rows[h];
342
+ }
343
+ rsData.rows.clear();
344
+
345
+ // Free columns and buffers
346
+ freeColumnsAndBuffers(rsData.columns, rsData.column_count, rsData.bound_columns, rsData.row_status_array);
347
+ }
348
+ allResultSets.clear();
349
+ }
350
+
276
351
  void deleteColumns() {
277
352
  for (size_t h = 0; h < this->storedRows.size(); h++) {
278
353
  delete[] storedRows[h];
279
354
  }
280
355
  this->storedRows.clear();
281
356
 
282
- for (int i = 0; i < this->column_count; i++) {
283
- switch (this->columns[i]->bind_type) {
284
- case SQL_C_CHAR:
285
- case SQL_C_UTINYINT:
286
- case SQL_C_BINARY:
287
- delete[] (SQLCHAR *)this->bound_columns[i].buffer;
288
- break;
289
- case SQL_C_WCHAR:
290
- delete[] (SQLWCHAR *)this->bound_columns[i].buffer;
291
- break;
292
- case SQL_C_DOUBLE:
293
- delete[] (SQLDOUBLE *)this->bound_columns[i].buffer;
294
- break;
295
- case SQL_C_USHORT:
296
- delete[] (SQLUSMALLINT *)this->bound_columns[i].buffer;
297
- break;
298
- case SQL_C_SLONG:
299
- delete[] (SQLUINTEGER *)this->bound_columns[i].buffer;
300
- break;
301
- case SQL_C_UBIGINT:
302
- delete[] (SQLUBIGINT *)this->bound_columns[i].buffer;
303
- break;
304
- }
357
+ // NULL guard for columns and bound_columns pointers
358
+ if (this->columns != NULL && this->bound_columns != NULL) {
359
+ for (int i = 0; i < this->column_count; i++) {
360
+ switch (this->columns[i]->bind_type) {
361
+ case SQL_C_CHAR:
362
+ case SQL_C_UTINYINT:
363
+ case SQL_C_BINARY:
364
+ delete[] (SQLCHAR *)this->bound_columns[i].buffer;
365
+ break;
366
+ case SQL_C_WCHAR:
367
+ delete[] (SQLWCHAR *)this->bound_columns[i].buffer;
368
+ break;
369
+ case SQL_C_DOUBLE:
370
+ delete[] (SQLDOUBLE *)this->bound_columns[i].buffer;
371
+ break;
372
+ case SQL_C_USHORT:
373
+ delete[] (SQLUSMALLINT *)this->bound_columns[i].buffer;
374
+ break;
375
+ case SQL_C_SLONG:
376
+ delete[] (SQLUINTEGER *)this->bound_columns[i].buffer;
377
+ break;
378
+ case SQL_C_UBIGINT:
379
+ delete[] (SQLUBIGINT *)this->bound_columns[i].buffer;
380
+ break;
381
+ }
305
382
 
306
- delete[] this->columns[i]->ColumnName;
307
- delete[] this->bound_columns[i].length_or_indicator_array;
308
- delete this->columns[i];
383
+ delete[] this->columns[i]->ColumnName;
384
+ delete[] this->bound_columns[i].length_or_indicator_array;
385
+ delete this->columns[i];
386
+ }
309
387
  }
310
388
  this->column_count = 0;
311
389
 
@@ -1905,20 +1905,23 @@ class CallProcedureAsyncWorker : public ODBCAsyncWorker {
1905
1905
  return;
1906
1906
  }
1907
1907
 
1908
- // Guardar el result set actual en allResultSets
1909
- //if (data->storedRows.size() > 0) {
1910
- std::vector<ColumnData*> currentResultSet;
1911
- for (size_t i = 0; i < data->storedRows.size(); i++) {
1912
- currentResultSet.push_back(data->storedRows[i]);
1913
- }
1914
- data->allResultSets.push_back(currentResultSet);
1915
- //}
1908
+ // Save the result set with column metadata to allResultSets
1909
+ ResultSetData rsData;
1910
+ rsData.rows = data->storedRows;
1911
+ rsData.columns = data->columns;
1912
+ rsData.column_count = data->column_count;
1913
+ rsData.bound_columns = data->bound_columns;
1914
+ rsData.rowCount = data->rowCount;
1915
+ rsData.row_status_array = data->row_status_array;
1916
+
1917
+ data->allResultSets.push_back(rsData);
1916
1918
 
1917
- // Limpiar storedRows para el próximo result set (sin liberar memoria)
1919
+ // Clear storedRows and reset pointers (don't free, they're now in allResultSets)
1918
1920
  data->storedRows.clear();
1919
-
1920
- // Limpiar columnas para el próximo result set
1921
- data->deleteColumns();
1921
+ data->columns = NULL;
1922
+ data->column_count = 0;
1923
+ data->bound_columns = NULL;
1924
+ data->row_status_array = NULL;
1922
1925
 
1923
1926
  // Intentar obtener el siguiente result set
1924
1927
  return_code = SQLMoreResults(data->hstmt);
@@ -1946,8 +1949,14 @@ class CallProcedureAsyncWorker : public ODBCAsyncWorker {
1946
1949
 
1947
1950
  // Procesar cada result set almacenado
1948
1951
  for (size_t rs_index = 0; rs_index < data->allResultSets.size(); rs_index++) {
1949
- // Restaurar temporalmente storedRows para procesarlo
1950
- data->storedRows = data->allResultSets[rs_index];
1952
+ ResultSetData &rsData = data->allResultSets[rs_index];
1953
+
1954
+ // Restore column metadata and rows for this result set
1955
+ data->storedRows = rsData.rows;
1956
+ data->columns = rsData.columns;
1957
+ data->column_count = rsData.column_count;
1958
+ data->bound_columns = rsData.bound_columns;
1959
+ data->rowCount = rsData.rowCount;
1951
1960
 
1952
1961
  Napi::Array singleResult = process_data_for_napi(
1953
1962
  env,
@@ -1957,9 +1966,22 @@ class CallProcedureAsyncWorker : public ODBCAsyncWorker {
1957
1966
 
1958
1967
  allResults.Set(rs_index, singleResult);
1959
1968
 
1960
- // Limpiar storedRows después de procesar
1961
- data->storedRows.clear();
1969
+ // Mark rows as processed (process_data_for_napi frees them internally)
1970
+ data->allResultSets[rs_index].rows.clear();
1971
+ // Mark columns as processed to prevent double-free
1972
+ data->allResultSets[rs_index].columns = NULL;
1973
+ data->allResultSets[rs_index].bound_columns = NULL;
1974
+ data->allResultSets[rs_index].row_status_array = NULL;
1962
1975
  }
1976
+
1977
+ // Clear allResultSets to prevent deleteAllResultSets from attempting to free already-processed memory
1978
+ data->allResultSets.clear();
1979
+
1980
+ // Reset pointers to avoid double-free (deleteColumns is called in destructor)
1981
+ data->columns = NULL;
1982
+ data->column_count = 0;
1983
+ data->bound_columns = NULL;
1984
+ data->row_status_array = NULL;
1963
1985
 
1964
1986
  std::vector<napi_value> callbackArguments;
1965
1987
  callbackArguments.push_back(env.Null());