@miatechnet/node-odbc 2.4.10-multiresult.1
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/CHANGELOG.md +179 -0
- package/LICENSE +23 -0
- package/README.md +1314 -0
- package/binding.gyp +100 -0
- package/lib/Connection.js +521 -0
- package/lib/Cursor.js +92 -0
- package/lib/Pool.js +470 -0
- package/lib/Statement.js +207 -0
- package/lib/bindings/napi-v8/odbc.node +0 -0
- package/lib/odbc.d.ts +210 -0
- package/lib/odbc.js +56 -0
- package/package.json +69 -0
- package/src/dynodbc.cpp +189 -0
- package/src/dynodbc.h +383 -0
- package/src/odbc.cpp +850 -0
- package/src/odbc.h +362 -0
- package/src/odbc_connection.cpp +4312 -0
- package/src/odbc_connection.h +114 -0
- package/src/odbc_cursor.cpp +265 -0
- package/src/odbc_cursor.h +52 -0
- package/src/odbc_statement.cpp +610 -0
- package/src/odbc_statement.h +43 -0
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2019, 2021 IBM
|
|
3
|
+
Copyright (c) 2013, Dan VerWeire<dverweire@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#include <napi.h>
|
|
19
|
+
#include <time.h>
|
|
20
|
+
#include <string>
|
|
21
|
+
|
|
22
|
+
#include "odbc.h"
|
|
23
|
+
#include "odbc_connection.h"
|
|
24
|
+
#include "odbc_statement.h"
|
|
25
|
+
#include "odbc_cursor.h"
|
|
26
|
+
|
|
27
|
+
Napi::FunctionReference ODBCStatement::constructor;
|
|
28
|
+
|
|
29
|
+
Napi::Object ODBCStatement::Init(Napi::Env env, Napi::Object exports) {
|
|
30
|
+
|
|
31
|
+
Napi::HandleScope scope(env);
|
|
32
|
+
|
|
33
|
+
Napi::Function constructorFunction = DefineClass(env, "ODBCStatement", {
|
|
34
|
+
InstanceMethod("prepare", &ODBCStatement::Prepare),
|
|
35
|
+
InstanceMethod("bind", &ODBCStatement::Bind),
|
|
36
|
+
InstanceMethod("execute", &ODBCStatement::Execute),
|
|
37
|
+
InstanceMethod("close", &ODBCStatement::Close),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Attach the Database Constructor to the target object
|
|
41
|
+
constructor = Napi::Persistent(constructorFunction);
|
|
42
|
+
constructor.SuppressDestruct();
|
|
43
|
+
|
|
44
|
+
return exports;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
ODBCStatement::ODBCStatement(const Napi::CallbackInfo& info) : Napi::ObjectWrap<ODBCStatement>(info) {
|
|
49
|
+
this->data = new StatementData();
|
|
50
|
+
this->odbcConnection = info[0].As<Napi::External<ODBCConnection>>().Data();
|
|
51
|
+
this->data->hstmt = *(info[1].As<Napi::External<SQLHSTMT>>().Data());
|
|
52
|
+
this->data->fetch_array = this->odbcConnection->connectionOptions.fetchArray;
|
|
53
|
+
this->data->maxColumnNameLength = this->odbcConnection->getInfoResults.max_column_name_length;
|
|
54
|
+
this->data->get_data_supports = this->odbcConnection->getInfoResults.sql_get_data_supports;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
ODBCStatement::~ODBCStatement() {
|
|
58
|
+
this->Free();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
SQLRETURN ODBCStatement::Free() {
|
|
62
|
+
|
|
63
|
+
SQLRETURN return_code = SQL_SUCCESS;
|
|
64
|
+
|
|
65
|
+
if (this->data)
|
|
66
|
+
{
|
|
67
|
+
if (
|
|
68
|
+
this->data->hstmt &&
|
|
69
|
+
this->data->hstmt != SQL_NULL_HANDLE
|
|
70
|
+
)
|
|
71
|
+
{
|
|
72
|
+
uv_mutex_lock(&ODBC::g_odbcMutex);
|
|
73
|
+
return_code =
|
|
74
|
+
SQLFreeHandle
|
|
75
|
+
(
|
|
76
|
+
SQL_HANDLE_STMT,
|
|
77
|
+
this->data->hstmt
|
|
78
|
+
);
|
|
79
|
+
this->data->hstmt = SQL_NULL_HANDLE;
|
|
80
|
+
uv_mutex_unlock(&ODBC::g_odbcMutex);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
delete this->data;
|
|
84
|
+
this->data = NULL;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return return_code;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/******************************************************************************
|
|
91
|
+
********************************* PREPARE ************************************
|
|
92
|
+
*****************************************************************************/
|
|
93
|
+
|
|
94
|
+
// PrepareAsyncWorker, used by Prepare function (see below)
|
|
95
|
+
class PrepareAsyncWorker : public ODBCAsyncWorker {
|
|
96
|
+
|
|
97
|
+
private:
|
|
98
|
+
ODBCStatement *odbcStatement;
|
|
99
|
+
ODBCConnection *odbcConnection;
|
|
100
|
+
StatementData *data;
|
|
101
|
+
|
|
102
|
+
public:
|
|
103
|
+
PrepareAsyncWorker(ODBCStatement *odbcStatement, Napi::Function& callback) : ODBCAsyncWorker(callback),
|
|
104
|
+
odbcStatement(odbcStatement),
|
|
105
|
+
odbcConnection(odbcStatement->odbcConnection),
|
|
106
|
+
data(odbcStatement->data) {}
|
|
107
|
+
|
|
108
|
+
~PrepareAsyncWorker() {}
|
|
109
|
+
|
|
110
|
+
void Execute() {
|
|
111
|
+
|
|
112
|
+
SQLRETURN return_code;
|
|
113
|
+
|
|
114
|
+
return_code = SQLPrepare
|
|
115
|
+
(
|
|
116
|
+
data->hstmt, // StatementHandle
|
|
117
|
+
data->sql, // StatementText
|
|
118
|
+
SQL_NTS // TextLength
|
|
119
|
+
);
|
|
120
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
121
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
122
|
+
SetError("[odbc] Error preparing the statement\0");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// front-load the work of SQLNumParams here, so we can convert
|
|
127
|
+
// NAPI/JavaScript values to C values immediately in Bind
|
|
128
|
+
return_code = SQLNumParams
|
|
129
|
+
(
|
|
130
|
+
data->hstmt, // StatementHandle
|
|
131
|
+
&data->parameterCount // ParameterCountPtr
|
|
132
|
+
);
|
|
133
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
134
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
135
|
+
SetError("[odbc] Error retrieving number of parameter markers to be bound to the statement\0");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
data->parameters = new Parameter*[data->parameterCount];
|
|
140
|
+
for (SQLSMALLINT i = 0; i < data->parameterCount; i++) {
|
|
141
|
+
data->parameters[i] = new Parameter();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
void OnOK() {
|
|
146
|
+
|
|
147
|
+
Napi::Env env = Env();
|
|
148
|
+
Napi::HandleScope scope(env);
|
|
149
|
+
|
|
150
|
+
std::vector<napi_value> callbackArguments;
|
|
151
|
+
callbackArguments.push_back(env.Null());
|
|
152
|
+
Callback().Call(callbackArguments);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/*
|
|
157
|
+
* ODBCStatement:Prepare (Async)
|
|
158
|
+
* Description: Prepares an SQL string so that it can be bound with
|
|
159
|
+
* parameters and then executed.
|
|
160
|
+
*
|
|
161
|
+
* Parameters:
|
|
162
|
+
* const Napi::CallbackInfo& info:
|
|
163
|
+
* The information passed by Napi from the JavaScript call, including
|
|
164
|
+
* arguments from the JavaScript function. In JavaScript, the
|
|
165
|
+
* prepare() function takes two arguments.
|
|
166
|
+
*
|
|
167
|
+
* info[0]: String: the SQL string to prepare.
|
|
168
|
+
* info[1]: Function: callback function:
|
|
169
|
+
* function(error, result)
|
|
170
|
+
* error: An error object if there was a problem getting results,
|
|
171
|
+
* or null if operation was successful.
|
|
172
|
+
* result: The number of rows affected by the executed query.
|
|
173
|
+
*
|
|
174
|
+
* Return:
|
|
175
|
+
* Napi::Value:
|
|
176
|
+
* Undefined (results returned in callback).
|
|
177
|
+
*/
|
|
178
|
+
Napi::Value ODBCStatement::Prepare(const Napi::CallbackInfo& info) {
|
|
179
|
+
|
|
180
|
+
Napi::Env env = info.Env();
|
|
181
|
+
Napi::HandleScope scope(env);
|
|
182
|
+
|
|
183
|
+
this->napiParameters = Napi::Persistent(Napi::Array::New(env));
|
|
184
|
+
|
|
185
|
+
if(!info[0].IsString() || !info[1].IsFunction()){
|
|
186
|
+
Napi::TypeError::New(env, "Argument 0 must be a string , Argument 1 must be a function.").ThrowAsJavaScriptException();
|
|
187
|
+
return env.Null();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
Napi::String sql = info[0].ToString();
|
|
191
|
+
Napi::Function callback = info[1].As<Napi::Function>();
|
|
192
|
+
|
|
193
|
+
if(this->data->hstmt == SQL_NULL_HANDLE) {
|
|
194
|
+
Napi::Error error = Napi::Error::New(env, "Statment handle is no longer valid. Cannot prepare SQL on an invalid statment handle.");
|
|
195
|
+
std::vector<napi_value> callbackArguments;
|
|
196
|
+
callbackArguments.push_back(error.Value());
|
|
197
|
+
callback.Call(callbackArguments);
|
|
198
|
+
return env.Undefined();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
data->sql = ODBC::NapiStringToSQLTCHAR(sql);
|
|
202
|
+
|
|
203
|
+
PrepareAsyncWorker *worker = new PrepareAsyncWorker(this, callback);
|
|
204
|
+
worker->Queue();
|
|
205
|
+
|
|
206
|
+
return env.Undefined();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/******************************************************************************
|
|
210
|
+
*********************************** BIND *************************************
|
|
211
|
+
*****************************************************************************/
|
|
212
|
+
|
|
213
|
+
// BindAsyncWorker, used by Bind function (see below)
|
|
214
|
+
class BindAsyncWorker : public ODBCAsyncWorker {
|
|
215
|
+
|
|
216
|
+
public:
|
|
217
|
+
|
|
218
|
+
BindAsyncWorker(ODBCStatement *odbcStatement, Napi::Function& callback) : ODBCAsyncWorker(callback),
|
|
219
|
+
odbcStatement(odbcStatement),
|
|
220
|
+
odbcConnection(odbcStatement->odbcConnection),
|
|
221
|
+
data(odbcStatement->data) {}
|
|
222
|
+
|
|
223
|
+
private:
|
|
224
|
+
|
|
225
|
+
ODBCStatement *odbcStatement;
|
|
226
|
+
ODBCConnection *odbcConnection;
|
|
227
|
+
StatementData *data;
|
|
228
|
+
|
|
229
|
+
~BindAsyncWorker() { }
|
|
230
|
+
|
|
231
|
+
void Execute() {
|
|
232
|
+
|
|
233
|
+
SQLRETURN return_code;
|
|
234
|
+
|
|
235
|
+
return_code = ODBC::DescribeParameters(data->hstmt, data->parameters, data->parameterCount);
|
|
236
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
237
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
238
|
+
SetError("[odbc] Error retrieving information about the parameters in the statement\0");
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return_code = ODBC::BindParameters(data->hstmt, data->parameters, data->parameterCount);
|
|
243
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
244
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
245
|
+
SetError("[odbc] Error binding parameters to the statement\0");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
void OnOK() {
|
|
251
|
+
|
|
252
|
+
Napi::Env env = Env();
|
|
253
|
+
Napi::HandleScope scope(env);
|
|
254
|
+
|
|
255
|
+
std::vector<napi_value> callbackArguments;
|
|
256
|
+
callbackArguments.push_back(env.Null());
|
|
257
|
+
Callback().Call(callbackArguments);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
Napi::Value ODBCStatement::Bind(const Napi::CallbackInfo& info) {
|
|
262
|
+
|
|
263
|
+
Napi::Env env = info.Env();
|
|
264
|
+
Napi::HandleScope scope(env);
|
|
265
|
+
|
|
266
|
+
if ( !info[0].IsArray() || !info[1].IsFunction() ) {
|
|
267
|
+
Napi::TypeError::New(env, "Function signature is: bind(array, function)").ThrowAsJavaScriptException();
|
|
268
|
+
return env.Null();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
Napi::Array napiArray = info[0].As<Napi::Array>();
|
|
272
|
+
this->napiParameters = Napi::Persistent(napiArray);
|
|
273
|
+
Napi::Function callback = info[1].As<Napi::Function>();
|
|
274
|
+
|
|
275
|
+
if(this->data->hstmt == SQL_NULL_HANDLE) {
|
|
276
|
+
Napi::Error error = Napi::Error::New(env, "Statment handle is no longer valid. Cannot bind SQL on an invalid statment handle.");
|
|
277
|
+
std::vector<napi_value> callbackArguments;
|
|
278
|
+
callbackArguments.push_back(error.Value());
|
|
279
|
+
callback.Call(callbackArguments);
|
|
280
|
+
return env.Undefined();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// if the parameter count isnt right, end right away
|
|
284
|
+
if (data->parameterCount != (SQLSMALLINT)this->napiParameters.Value().Length() || data->parameters == NULL) {
|
|
285
|
+
std::vector<napi_value> callbackArguments;
|
|
286
|
+
|
|
287
|
+
Napi::Error error = Napi::Error::New(env, Napi::String::New(env, "[node-odbc] Error in Statement::BindAsyncWorker::Bind: The number of parameters in the prepared statement (" + std::to_string(data->parameterCount) + ") doesn't match the number of parameters passed to bind (" + std::to_string((SQLSMALLINT)this->napiParameters.Value().Length()) + "}."));
|
|
288
|
+
callbackArguments.push_back(error.Value());
|
|
289
|
+
|
|
290
|
+
callback.Call(callbackArguments);
|
|
291
|
+
return env.Undefined();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// converts NAPI/JavaScript values to values used by SQLBindParameter
|
|
295
|
+
ODBC::StoreBindValues(&napiArray, this->data->parameters);
|
|
296
|
+
|
|
297
|
+
BindAsyncWorker *worker = new BindAsyncWorker(this, callback);
|
|
298
|
+
worker->Queue();
|
|
299
|
+
|
|
300
|
+
return env.Undefined();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/******************************************************************************
|
|
304
|
+
********************************* EXECUTE ************************************
|
|
305
|
+
*****************************************************************************/
|
|
306
|
+
|
|
307
|
+
// ExecuteAsyncWorker, used by Execute function (see below)
|
|
308
|
+
class ExecuteAsyncWorker : public ODBCAsyncWorker {
|
|
309
|
+
|
|
310
|
+
private:
|
|
311
|
+
ODBCConnection *odbcConnection;
|
|
312
|
+
ODBCStatement *odbcStatement;
|
|
313
|
+
StatementData *data;
|
|
314
|
+
|
|
315
|
+
void Execute() {
|
|
316
|
+
|
|
317
|
+
SQLRETURN return_code;
|
|
318
|
+
|
|
319
|
+
// set SQL_ATTR_QUERY_TIMEOUT
|
|
320
|
+
if (data->query_options.timeout > 0) {
|
|
321
|
+
return_code =
|
|
322
|
+
SQLSetStmtAttr
|
|
323
|
+
(
|
|
324
|
+
data->hstmt,
|
|
325
|
+
SQL_ATTR_QUERY_TIMEOUT,
|
|
326
|
+
(SQLPOINTER) data->query_options.timeout,
|
|
327
|
+
IGNORED_PARAMETER
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
// It is possible that SQLSetStmtAttr returns a warning with SQLSTATE
|
|
331
|
+
// 01S02, indicating that the driver changed the value specified.
|
|
332
|
+
// Although we never use the timeout variable again (and so we don't
|
|
333
|
+
// REALLY need it to be correct in the code), its just good to have
|
|
334
|
+
// the correct value if we need it.
|
|
335
|
+
if (return_code == SQL_SUCCESS_WITH_INFO)
|
|
336
|
+
{
|
|
337
|
+
return_code =
|
|
338
|
+
SQLGetStmtAttr
|
|
339
|
+
(
|
|
340
|
+
data->hstmt,
|
|
341
|
+
SQL_ATTR_QUERY_TIMEOUT,
|
|
342
|
+
(SQLPOINTER) &data->query_options.timeout,
|
|
343
|
+
SQL_IS_UINTEGER,
|
|
344
|
+
IGNORED_PARAMETER
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Both of the SQL_ATTR_QUERY_TIMEOUT calls are combined here
|
|
349
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
350
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
351
|
+
SetError("[odbc] Error setting the query timeout on the statement\0");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return_code =
|
|
357
|
+
set_fetch_size
|
|
358
|
+
(
|
|
359
|
+
data,
|
|
360
|
+
data->query_options.fetch_size
|
|
361
|
+
);
|
|
362
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
363
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
364
|
+
SetError("[odbc] Error setting the fetch size\0");
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return_code =
|
|
369
|
+
SQLExecute
|
|
370
|
+
(
|
|
371
|
+
data->hstmt // StatementHandle
|
|
372
|
+
);
|
|
373
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
374
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
375
|
+
SetError("[odbc] Error executing the statement\0");
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (return_code != SQL_NO_DATA) {
|
|
380
|
+
|
|
381
|
+
if (data->query_options.use_cursor)
|
|
382
|
+
{
|
|
383
|
+
if (data->query_options.cursor_name != NULL)
|
|
384
|
+
{
|
|
385
|
+
return_code =
|
|
386
|
+
SQLSetCursorName
|
|
387
|
+
(
|
|
388
|
+
data->hstmt,
|
|
389
|
+
data->query_options.cursor_name,
|
|
390
|
+
data->query_options.cursor_name_length
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
394
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
395
|
+
SetError("[odbc] Error setting the cursor name on the statement\0");
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// set_fetch_size will swallow errors in the case that the driver
|
|
402
|
+
// doesn't implement SQL_ATTR_ROW_ARRAY_SIZE for SQLSetStmtAttr and
|
|
403
|
+
// the fetch size was 1. If the fetch size was set by the user to a
|
|
404
|
+
// value greater than 1, throw an error.
|
|
405
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
406
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
407
|
+
SetError("[odbc] Error setting the fetch size on the statement\0");
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return_code =
|
|
412
|
+
prepare_for_fetch
|
|
413
|
+
(
|
|
414
|
+
data
|
|
415
|
+
);
|
|
416
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
417
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
418
|
+
SetError("[odbc] Error preparing for fetch\0");
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
if (!data->query_options.use_cursor)
|
|
424
|
+
{
|
|
425
|
+
bool alloc_error = false;
|
|
426
|
+
return_code =
|
|
427
|
+
fetch_all_and_store
|
|
428
|
+
(
|
|
429
|
+
data,
|
|
430
|
+
true,
|
|
431
|
+
&alloc_error
|
|
432
|
+
);
|
|
433
|
+
if (alloc_error)
|
|
434
|
+
{
|
|
435
|
+
SetError("[odbc] Error allocating or reallocating memory when fetching data. No ODBC error information available.\0");
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
439
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
440
|
+
SetError("[odbc] Error retrieving the result set from the statement\0");
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
void OnOK() {
|
|
448
|
+
|
|
449
|
+
Napi::Env env = Env();
|
|
450
|
+
Napi::HandleScope scope(env);
|
|
451
|
+
|
|
452
|
+
std::vector<napi_value> callbackArguments;
|
|
453
|
+
|
|
454
|
+
if (data->query_options.use_cursor)
|
|
455
|
+
{
|
|
456
|
+
// arguments for the ODBCCursor constructor
|
|
457
|
+
std::vector<napi_value> cursor_arguments =
|
|
458
|
+
{
|
|
459
|
+
Napi::External<StatementData>::New(env, data),
|
|
460
|
+
Napi::External<ODBCConnection>::New(env, this->odbcConnection),
|
|
461
|
+
this->odbcStatement->napiParameters.Value(),
|
|
462
|
+
Napi::Boolean::New(env, false)
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// create a new ODBCCursor object as a Napi::Value
|
|
466
|
+
Napi::Value cursorObject = ODBCCursor::constructor.New(cursor_arguments);
|
|
467
|
+
|
|
468
|
+
// return cursor
|
|
469
|
+
std::vector<napi_value> callbackArguments =
|
|
470
|
+
{
|
|
471
|
+
env.Null(),
|
|
472
|
+
cursorObject
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
Callback().Call(callbackArguments);
|
|
476
|
+
}
|
|
477
|
+
else
|
|
478
|
+
{
|
|
479
|
+
Napi::Array rows = process_data_for_napi(env, data, odbcStatement->napiParameters.Value());
|
|
480
|
+
|
|
481
|
+
std::vector<napi_value> callbackArguments;
|
|
482
|
+
callbackArguments.push_back(env.Null());
|
|
483
|
+
callbackArguments.push_back(rows);
|
|
484
|
+
|
|
485
|
+
Callback().Call(callbackArguments);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
public:
|
|
492
|
+
ExecuteAsyncWorker(ODBCStatement *odbcStatement, Napi::Function& callback) : ODBCAsyncWorker(callback),
|
|
493
|
+
odbcConnection(odbcStatement->odbcConnection),
|
|
494
|
+
odbcStatement(odbcStatement),
|
|
495
|
+
data(odbcStatement->data) {}
|
|
496
|
+
|
|
497
|
+
~ExecuteAsyncWorker() {}
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
Napi::Value ODBCStatement::Execute(const Napi::CallbackInfo& info) {
|
|
501
|
+
|
|
502
|
+
Napi::Env env = info.Env();
|
|
503
|
+
Napi::HandleScope scope(env);
|
|
504
|
+
|
|
505
|
+
Napi::Function callback;
|
|
506
|
+
|
|
507
|
+
size_t argument_count = info.Length();
|
|
508
|
+
// ensuring the passed parameters are correct
|
|
509
|
+
if ((argument_count == 1 && info[0].IsFunction()) || (argument_count == 2 && info[1].IsFunction())) {
|
|
510
|
+
callback = info[argument_count - 1].As<Napi::Function>();
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
Napi::Value error;
|
|
514
|
+
|
|
515
|
+
// ensuring the passed parameters are correct
|
|
516
|
+
if (argument_count >= 1 && info[0].IsObject()) {
|
|
517
|
+
error =
|
|
518
|
+
parse_query_options
|
|
519
|
+
(
|
|
520
|
+
env,
|
|
521
|
+
info[0].As<Napi::Object>(),
|
|
522
|
+
&this->data->query_options
|
|
523
|
+
);
|
|
524
|
+
} else {
|
|
525
|
+
error =
|
|
526
|
+
parse_query_options
|
|
527
|
+
(
|
|
528
|
+
env,
|
|
529
|
+
env.Null(),
|
|
530
|
+
&this->data->query_options
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (!error.IsNull())
|
|
535
|
+
{
|
|
536
|
+
// Error when parsing the query options. Return the callback with the error
|
|
537
|
+
std::vector<napi_value> callback_argument =
|
|
538
|
+
{
|
|
539
|
+
error
|
|
540
|
+
};
|
|
541
|
+
callback.Call(callback_argument);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if(this->data->hstmt == SQL_NULL_HANDLE) {
|
|
545
|
+
Napi::Error error = Napi::Error::New(env, "Statment handle is no longer valid. Cannot execute SQL on an invalid statment handle.");
|
|
546
|
+
std::vector<napi_value> callbackArguments;
|
|
547
|
+
callbackArguments.push_back(error.Value());
|
|
548
|
+
callback.Call(callbackArguments);
|
|
549
|
+
return env.Undefined();
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
ExecuteAsyncWorker *worker = new ExecuteAsyncWorker(this, callback);
|
|
553
|
+
worker->Queue();
|
|
554
|
+
|
|
555
|
+
return env.Undefined();
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/******************************************************************************
|
|
559
|
+
********************************** CLOSE *************************************
|
|
560
|
+
*****************************************************************************/
|
|
561
|
+
|
|
562
|
+
// CloseStatementAsyncWorker, used by Close function (see below)
|
|
563
|
+
class CloseStatementAsyncWorker : public ODBCAsyncWorker {
|
|
564
|
+
|
|
565
|
+
private:
|
|
566
|
+
ODBCStatement *odbcStatement;
|
|
567
|
+
StatementData *data;
|
|
568
|
+
|
|
569
|
+
void Execute() {
|
|
570
|
+
|
|
571
|
+
SQLRETURN return_code;
|
|
572
|
+
|
|
573
|
+
return_code = odbcStatement->Free();
|
|
574
|
+
if (!SQL_SUCCEEDED(return_code)) {
|
|
575
|
+
this->errors = GetODBCErrors(SQL_HANDLE_STMT, data->hstmt);
|
|
576
|
+
SetError("[odbc] Error closing the Statement\0");
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
void OnOK() {
|
|
582
|
+
|
|
583
|
+
Napi::Env env = Env();
|
|
584
|
+
Napi::HandleScope scope(env);
|
|
585
|
+
|
|
586
|
+
std::vector<napi_value> callbackArguments;
|
|
587
|
+
callbackArguments.push_back(env.Null());
|
|
588
|
+
Callback().Call(callbackArguments);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
public:
|
|
592
|
+
CloseStatementAsyncWorker(ODBCStatement *odbcStatement, Napi::Function& callback) : ODBCAsyncWorker(callback),
|
|
593
|
+
odbcStatement(odbcStatement),
|
|
594
|
+
data(odbcStatement->data) {}
|
|
595
|
+
|
|
596
|
+
~CloseStatementAsyncWorker() {}
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
Napi::Value ODBCStatement::Close(const Napi::CallbackInfo& info) {
|
|
600
|
+
|
|
601
|
+
Napi::Env env = info.Env();
|
|
602
|
+
Napi::HandleScope scope(env);
|
|
603
|
+
|
|
604
|
+
Napi::Function callback = info[0].As<Napi::Function>();
|
|
605
|
+
|
|
606
|
+
CloseStatementAsyncWorker *worker = new CloseStatementAsyncWorker(this, callback);
|
|
607
|
+
worker->Queue();
|
|
608
|
+
|
|
609
|
+
return env.Undefined();
|
|
610
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2019, 2021 IBM
|
|
3
|
+
Copyright (c) 2013, Dan VerWeire<dverweire@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#ifndef _SRC_ODBC_STATEMENT_H
|
|
19
|
+
#define _SRC_ODBC_STATEMENT_H
|
|
20
|
+
|
|
21
|
+
#include <napi.h>
|
|
22
|
+
|
|
23
|
+
class ODBCStatement : public Napi::ObjectWrap<ODBCStatement> {
|
|
24
|
+
public:
|
|
25
|
+
static Napi::FunctionReference constructor;
|
|
26
|
+
|
|
27
|
+
static Napi::Object Init(Napi::Env env, Napi::Object exports);
|
|
28
|
+
|
|
29
|
+
ODBCConnection *odbcConnection;
|
|
30
|
+
Napi::Reference<Napi::Array> napiParameters;
|
|
31
|
+
StatementData *data;
|
|
32
|
+
|
|
33
|
+
SQLRETURN Free();
|
|
34
|
+
|
|
35
|
+
explicit ODBCStatement(const Napi::CallbackInfo& info);
|
|
36
|
+
~ODBCStatement();
|
|
37
|
+
|
|
38
|
+
Napi::Value Prepare(const Napi::CallbackInfo& info);
|
|
39
|
+
Napi::Value Bind(const Napi::CallbackInfo& info);
|
|
40
|
+
Napi::Value Execute(const Napi::CallbackInfo& info);
|
|
41
|
+
Napi::Value Close(const Napi::CallbackInfo& info);
|
|
42
|
+
};
|
|
43
|
+
#endif
|