@miatechnet/node-odbc 2.4.13 → 2.4.16
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 +1 -1
- package/src/odbc.h +105 -27
- package/src/odbc_connection.cpp +38 -16
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.
|
|
4
|
+
"version": "2.4.16",
|
|
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<
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
|
package/src/odbc_connection.cpp
CHANGED
|
@@ -1905,20 +1905,23 @@ class CallProcedureAsyncWorker : public ODBCAsyncWorker {
|
|
|
1905
1905
|
return;
|
|
1906
1906
|
}
|
|
1907
1907
|
|
|
1908
|
-
//
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
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
|
-
//
|
|
1919
|
+
// Clear storedRows and reset pointers (don't free, they're now in allResultSets)
|
|
1918
1920
|
data->storedRows.clear();
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
data->
|
|
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
|
-
|
|
1950
|
-
|
|
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
|
-
//
|
|
1961
|
-
data->
|
|
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());
|