@zowe/db2-for-zowe-cli 6.1.17 → 6.1.18

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.
Files changed (232) hide show
  1. package/README.md +12 -10
  2. package/lib/Db2PluginLifeCycle.d.ts +11 -9
  3. package/lib/Db2PluginLifeCycle.js +18 -13
  4. package/lib/Db2PluginLifeCycle.js.map +1 -1
  5. package/node_modules/axios/CHANGELOG.md +62 -0
  6. package/node_modules/axios/README.md +233 -5
  7. package/node_modules/axios/dist/axios.js +387 -89
  8. package/node_modules/axios/dist/axios.min.js +3 -3
  9. package/node_modules/axios/dist/axios.min.js.map +1 -1
  10. package/node_modules/axios/dist/browser/axios.cjs +458 -98
  11. package/node_modules/axios/dist/esm/axios.js +458 -98
  12. package/node_modules/axios/dist/esm/axios.min.js +2 -2
  13. package/node_modules/axios/dist/esm/axios.min.js.map +1 -1
  14. package/node_modules/axios/dist/node/axios.cjs +568 -116
  15. package/node_modules/axios/index.d.cts +4 -0
  16. package/node_modules/axios/index.d.ts +4 -0
  17. package/node_modules/axios/lib/adapters/fetch.js +64 -11
  18. package/node_modules/axios/lib/adapters/http.js +134 -50
  19. package/node_modules/axios/lib/adapters/xhr.js +52 -3
  20. package/node_modules/axios/lib/core/Axios.js +19 -14
  21. package/node_modules/axios/lib/core/InterceptorManager.js +107 -8
  22. package/node_modules/axios/lib/core/buildFullPath.js +1 -13
  23. package/node_modules/axios/lib/core/dispatchRequest.js +8 -2
  24. package/node_modules/axios/lib/core/mergeConfig.js +1 -1
  25. package/node_modules/axios/lib/core/methodList.js +17 -0
  26. package/node_modules/axios/lib/defaults/index.js +2 -1
  27. package/node_modules/axios/lib/env/data.js +1 -1
  28. package/node_modules/axios/lib/helpers/Http2Sessions.js +2 -0
  29. package/node_modules/axios/lib/helpers/HttpStatusCode.js +11 -1
  30. package/node_modules/axios/lib/helpers/fromDataURI.js +5 -4
  31. package/node_modules/axios/lib/helpers/normalizeURLForProtocolCheck.js +24 -0
  32. package/node_modules/axios/lib/helpers/progressEventReducer.js +1 -1
  33. package/node_modules/axios/lib/helpers/resolveConfig.js +4 -2
  34. package/node_modules/axios/lib/helpers/shouldBypassProxy.js +182 -6
  35. package/node_modules/axios/lib/helpers/throttle.js +3 -2
  36. package/node_modules/axios/lib/helpers/toFormData.js +14 -21
  37. package/node_modules/axios/lib/utils.js +134 -13
  38. package/node_modules/axios/package.json +1 -1
  39. package/node_modules/ibm_db/APIDocumentation.md +2883 -2024
  40. package/node_modules/ibm_db/LICENSE +23 -23
  41. package/node_modules/ibm_db/README.md +735 -735
  42. package/node_modules/ibm_db/binding.gyp +99 -98
  43. package/node_modules/ibm_db/installer/driverInstall.js +984 -958
  44. package/node_modules/ibm_db/installer/ifx.sh +38 -38
  45. package/node_modules/ibm_db/installer/setenv.sh +23 -23
  46. package/node_modules/ibm_db/installer/testODBCConnection.bat +51 -51
  47. package/node_modules/ibm_db/installer/testODBCConnection.sh +61 -61
  48. package/node_modules/ibm_db/lib/climacros.js +431 -431
  49. package/node_modules/ibm_db/lib/odbc.js +4293 -3487
  50. package/node_modules/ibm_db/lib/simple-queue.js +41 -41
  51. package/node_modules/ibm_db/package.json +70 -70
  52. package/node_modules/ibm_db/src/odbc.cpp +1495 -1396
  53. package/node_modules/ibm_db/src/odbc.h +477 -473
  54. package/node_modules/ibm_db/src/odbc_connection.cpp +1762 -1612
  55. package/node_modules/ibm_db/src/odbc_connection.h +228 -215
  56. package/node_modules/ibm_db/src/odbc_result.cpp +1547 -1547
  57. package/node_modules/ibm_db/src/odbc_result.h +152 -152
  58. package/node_modules/ibm_db/src/odbc_statement.cpp +2047 -861
  59. package/node_modules/ibm_db/src/odbc_statement.h +257 -135
  60. package/node_modules/ibm_db/src/strptime.c +389 -389
  61. package/node_modules/ibm_db/src/strptime.h +46 -46
  62. package/node_modules/ibm_db/test/README +23 -23
  63. package/node_modules/ibm_db/test/bench-insert.js +62 -62
  64. package/node_modules/ibm_db/test/bench-insertBatch10.js +74 -74
  65. package/node_modules/ibm_db/test/bench-insertBatch100.js +75 -75
  66. package/node_modules/ibm_db/test/bench-insertBatch1000.js +75 -75
  67. package/node_modules/ibm_db/test/bench-insertBatch10000.js +75 -75
  68. package/node_modules/ibm_db/test/bench-prepare-bind-execute-closeSync.js +60 -60
  69. package/node_modules/ibm_db/test/bench-prepare-bind-executeNonQuery.js +53 -53
  70. package/node_modules/ibm_db/test/bench-prepare-bindSync-execute-closeSync.js +50 -50
  71. package/node_modules/ibm_db/test/bench-prepare-bindSync-executeNonQuery.js +48 -48
  72. package/node_modules/ibm_db/test/bench-prepare-execute-closeSync.js +51 -51
  73. package/node_modules/ibm_db/test/bench-prepare-executeNonQuery.js +47 -47
  74. package/node_modules/ibm_db/test/bench-prepare-not.js +43 -43
  75. package/node_modules/ibm_db/test/bench-query-fetch.js +59 -59
  76. package/node_modules/ibm_db/test/bench-query-fetchAll.js +49 -49
  77. package/node_modules/ibm_db/test/bench-query-fetchAllSync.js +43 -43
  78. package/node_modules/ibm_db/test/bench-query.js +40 -40
  79. package/node_modules/ibm_db/test/bench-querySync-fetchArray.js +28 -28
  80. package/node_modules/ibm_db/test/bench-querySync.js +29 -29
  81. package/node_modules/ibm_db/test/common.d.ts +10 -10
  82. package/node_modules/ibm_db/test/common.js +127 -127
  83. package/node_modules/ibm_db/test/config.json +7 -7
  84. package/node_modules/ibm_db/test/config.zos.json +4 -4
  85. package/node_modules/ibm_db/test/data/sample1.txt +9 -9
  86. package/node_modules/ibm_db/test/data/trc.fmt +30114 -30114
  87. package/node_modules/ibm_db/test/nodeEE-leak-huge-query.js +152 -152
  88. package/node_modules/ibm_db/test/nodeEE-leak-multiple-query-multiple-connections.js +148 -148
  89. package/node_modules/ibm_db/test/nodeEE-leak-multiple-query-pool-connections.js +154 -154
  90. package/node_modules/ibm_db/test/nodeEE-leak-multiple-query.js +133 -133
  91. package/node_modules/ibm_db/test/nodeEE-stress-async-waterfall-multiple-connections.js +174 -174
  92. package/node_modules/ibm_db/test/nodeEE-stress-async-waterfall.js +169 -169
  93. package/node_modules/ibm_db/test/nodeEE-stress-mixed-query-multiple-connections.js +159 -159
  94. package/node_modules/ibm_db/test/nodeEE-stress-mixed-query.js +148 -148
  95. package/node_modules/ibm_db/test/odbc-bench.c +123 -123
  96. package/node_modules/ibm_db/test/perfTest.js +53 -53
  97. package/node_modules/ibm_db/test/quick-example.sh +30 -30
  98. package/node_modules/ibm_db/test/quick-example.ts +80 -80
  99. package/node_modules/ibm_db/test/run-bench.js +96 -96
  100. package/node_modules/ibm_db/test/run-nodeEE.js +132 -132
  101. package/node_modules/ibm_db/test/run-tests.js +132 -138
  102. package/node_modules/ibm_db/test/sql-cli.js +28 -28
  103. package/node_modules/ibm_db/test/test-all-data-types.js +109 -109
  104. package/node_modules/ibm_db/test/test-array-insert.js +178 -178
  105. package/node_modules/ibm_db/test/test-async-await.js +105 -105
  106. package/node_modules/ibm_db/test/test-attributes.js +34 -34
  107. package/node_modules/ibm_db/test/test-bad-pool-connection-prepare-execute.js +57 -57
  108. package/node_modules/ibm_db/test/test-basic-test.js +138 -138
  109. package/node_modules/ibm_db/test/test-binary-array-insert.js +73 -73
  110. package/node_modules/ibm_db/test/test-blob-from-file.js +90 -90
  111. package/node_modules/ibm_db/test/test-blob-insert.js +144 -144
  112. package/node_modules/ibm_db/test/test-blob-stream-insert.js +321 -299
  113. package/node_modules/ibm_db/test/test-blob-to-file.js +370 -370
  114. package/node_modules/ibm_db/test/test-block-fetch.js +364 -364
  115. package/node_modules/ibm_db/test/test-blocking-issue210.js +123 -123
  116. package/node_modules/ibm_db/test/test-call-async.js +64 -64
  117. package/node_modules/ibm_db/test/test-call-stmt.js +53 -53
  118. package/node_modules/ibm_db/test/test-cancel.js +703 -0
  119. package/node_modules/ibm_db/test/test-chinese-char.js +59 -59
  120. package/node_modules/ibm_db/test/test-connection.js +306 -0
  121. package/node_modules/ibm_db/test/test-date.js +32 -32
  122. package/node_modules/ibm_db/test/test-dbclob.js +93 -93
  123. package/node_modules/ibm_db/test/test-decimals.js +135 -135
  124. package/node_modules/ibm_db/test/test-describe-metadata.js +148 -0
  125. package/node_modules/ibm_db/test/test-executeFile.js +167 -167
  126. package/node_modules/ibm_db/test/test-executeFileSync.js +85 -85
  127. package/node_modules/ibm_db/test/test-fetch-apis.js +146 -146
  128. package/node_modules/ibm_db/test/test-fetchN.js +130 -130
  129. package/node_modules/ibm_db/test/test-getinfo.js +197 -197
  130. package/node_modules/ibm_db/test/test-ibm-db-issue14.js +58 -58
  131. package/node_modules/ibm_db/test/test-ibm-db-issue17.js +54 -54
  132. package/node_modules/ibm_db/test/test-ibm-db-issue18.js +54 -54
  133. package/node_modules/ibm_db/test/test-issue-1059-fetchAll-undefined.js +67 -67
  134. package/node_modules/ibm_db/test/test-issue-1080-array-blob-insert.js +40 -0
  135. package/node_modules/ibm_db/test/test-issue-54.js +39 -39
  136. package/node_modules/ibm_db/test/test-issue-get-column-value-2.js +44 -44
  137. package/node_modules/ibm_db/test/test-issue211.js +80 -80
  138. package/node_modules/ibm_db/test/test-issue253.js +39 -39
  139. package/node_modules/ibm_db/test/test-max-pool-size.js +96 -96
  140. package/node_modules/ibm_db/test/test-memory-leaks-new-objects.js +32 -32
  141. package/node_modules/ibm_db/test/test-multi-open-query-close.js +75 -75
  142. package/node_modules/ibm_db/test/test-param-put-data.js +713 -0
  143. package/node_modules/ibm_db/test/test-pool-idle-connection.js +72 -72
  144. package/node_modules/ibm_db/test/test-pool-operations.js +325 -0
  145. package/node_modules/ibm_db/test/test-pool-stale-connections.js +111 -111
  146. package/node_modules/ibm_db/test/test-pool-uncommited.js +33 -33
  147. package/node_modules/ibm_db/test/test-prepare-execute.js +339 -0
  148. package/node_modules/ibm_db/test/test-prepareSync-multiple-execution.js +93 -93
  149. package/node_modules/ibm_db/test/test-primary-foreign-keys.js +246 -0
  150. package/node_modules/ibm_db/test/test-procedures.js +243 -0
  151. package/node_modules/ibm_db/test/test-promise-all-data-types.js +42 -42
  152. package/node_modules/ibm_db/test/test-query-operations.js +405 -0
  153. package/node_modules/ibm_db/test/test-quick-example.js +25 -25
  154. package/node_modules/ibm_db/test/test-row-array-insert.js +167 -167
  155. package/node_modules/ibm_db/test/test-sp-array.js +54 -54
  156. package/node_modules/ibm_db/test/test-sp-dbc-6000.js +88 -88
  157. package/node_modules/ibm_db/test/test-sp-memleak.js +136 -136
  158. package/node_modules/ibm_db/test/test-sp-resultset-execute.js +269 -269
  159. package/node_modules/ibm_db/test/test-sp-resultset.js +228 -228
  160. package/node_modules/ibm_db/test/test-transactions.js +261 -0
  161. package/node_modules/ibm_db/test/test-typescript.js +48 -48
  162. package/node_modules/ibm_db/test/test-xml-datatype.js +70 -70
  163. package/node_modules/ibm_db/test/testWebApp.js +56 -56
  164. package/node_modules/ibm_db/test/trace +51 -51
  165. package/node_modules/ibm_db/test/typescript-apis-test.js +978 -978
  166. package/node_modules/ibm_db/test/webapp.html +17 -17
  167. package/node_modules/ibm_db/test/webapp.js +79 -79
  168. package/node_modules/ibm_db/typescript/README.md +28 -28
  169. package/node_modules/ibm_db/typescript/dist/Database.d.ts +26 -0
  170. package/node_modules/ibm_db/typescript/dist/ODBCStatement.d.ts +93 -0
  171. package/package.json +10 -6
  172. package/node_modules/ibm_db/test/disabled/test-binding-statement-rebinding.js +0 -59
  173. package/node_modules/ibm_db/test/disabled/test-open-dont-close.js +0 -12
  174. package/node_modules/ibm_db/test/disabled/test-param-select-with-booleans-only.js +0 -21
  175. package/node_modules/ibm_db/test/disabled/test-param-select-with-decimals-only.js +0 -20
  176. package/node_modules/ibm_db/test/disabled/test-param-select-with-null.js +0 -21
  177. package/node_modules/ibm_db/test/disabled/test-param-select-with-nulls-mixed.js +0 -23
  178. package/node_modules/ibm_db/test/disabled/test-param-select-with-numbers-mixed.js +0 -22
  179. package/node_modules/ibm_db/test/disabled/test-param-select-with-numbers-only.js +0 -24
  180. package/node_modules/ibm_db/test/disabled/test-param-select-with-strings-only.js +0 -23
  181. package/node_modules/ibm_db/test/disabled/test-param-select-with-unicode.js +0 -15
  182. package/node_modules/ibm_db/test/disabled/test-prepare-bind-execute-closeSync.js +0 -62
  183. package/node_modules/ibm_db/test/disabled/test-prepare-bind-execute-error.js +0 -49
  184. package/node_modules/ibm_db/test/disabled/test-prepare-bind-execute-long-string.js +0 -63
  185. package/node_modules/ibm_db/test/disabled/test-prepare-bind-executeNonQuery.js +0 -63
  186. package/node_modules/ibm_db/test/disabled/test-prepare-bindSync-execute-closeSync.js +0 -46
  187. package/node_modules/ibm_db/test/disabled/test-prepare-bindSync-executeNonQuery.js +0 -50
  188. package/node_modules/ibm_db/test/disabled/test-prepare.js +0 -34
  189. package/node_modules/ibm_db/test/disabled/test-prepareSync.js +0 -29
  190. package/node_modules/ibm_db/test/disabled/test-query-select-fetchAll.js +0 -23
  191. package/node_modules/ibm_db/test/disabled/test-query-select-fetchAllSync.js +0 -23
  192. package/node_modules/ibm_db/test/disabled/test-query-select-unicode.js +0 -15
  193. package/node_modules/ibm_db/test/disabled/test-querySync-select-unicode.js +0 -20
  194. package/node_modules/ibm_db/test/test-affected-rows.js +0 -55
  195. package/node_modules/ibm_db/test/test-bad-connection-string.js +0 -30
  196. package/node_modules/ibm_db/test/test-binding-connection-timeOut.js +0 -31
  197. package/node_modules/ibm_db/test/test-binding-statement-executeSync.js +0 -84
  198. package/node_modules/ibm_db/test/test-binding-transaction-commit.js +0 -79
  199. package/node_modules/ibm_db/test/test-binding-transaction-commitSync.js +0 -53
  200. package/node_modules/ibm_db/test/test-closed.js +0 -14
  201. package/node_modules/ibm_db/test/test-connection-object.js +0 -16
  202. package/node_modules/ibm_db/test/test-describe-column.js +0 -92
  203. package/node_modules/ibm_db/test/test-describe-database.js +0 -18
  204. package/node_modules/ibm_db/test/test-describe-table.js +0 -31
  205. package/node_modules/ibm_db/test/test-domains-open.js +0 -19
  206. package/node_modules/ibm_db/test/test-global-open-close.js +0 -11
  207. package/node_modules/ibm_db/test/test-instantiate-one-and-end.js +0 -9
  208. package/node_modules/ibm_db/test/test-multi-open-close.js +0 -45
  209. package/node_modules/ibm_db/test/test-multi-openSync-closeSync.js +0 -52
  210. package/node_modules/ibm_db/test/test-open-close.js +0 -29
  211. package/node_modules/ibm_db/test/test-open-connectTimeout.js +0 -33
  212. package/node_modules/ibm_db/test/test-openSync.js +0 -31
  213. package/node_modules/ibm_db/test/test-pool-close.js +0 -120
  214. package/node_modules/ibm_db/test/test-pool-connect.js +0 -36
  215. package/node_modules/ibm_db/test/test-pool-rollbackTransaction.js +0 -35
  216. package/node_modules/ibm_db/test/test-prepareSync-bad-sql.js +0 -34
  217. package/node_modules/ibm_db/test/test-promise-open-close.js +0 -33
  218. package/node_modules/ibm_db/test/test-query-create-table-fetchSync.js +0 -23
  219. package/node_modules/ibm_db/test/test-query-create-table.js +0 -12
  220. package/node_modules/ibm_db/test/test-query-drop-table.js +0 -13
  221. package/node_modules/ibm_db/test/test-query-insert.js +0 -38
  222. package/node_modules/ibm_db/test/test-query-select-fetch.js +0 -21
  223. package/node_modules/ibm_db/test/test-query-select-fetchMode-array.js +0 -17
  224. package/node_modules/ibm_db/test/test-query-select.js +0 -64
  225. package/node_modules/ibm_db/test/test-queryResult.js +0 -35
  226. package/node_modules/ibm_db/test/test-queryStream.js +0 -43
  227. package/node_modules/ibm_db/test/test-querySync-select-with-exception.js +0 -25
  228. package/node_modules/ibm_db/test/test-querySync-select.js +0 -28
  229. package/node_modules/ibm_db/test/test-require-and-end.js +0 -8
  230. package/node_modules/ibm_db/test/test-statement-close.js +0 -118
  231. package/node_modules/ibm_db/test/test-transaction-commit-sync.js +0 -54
  232. package/node_modules/ibm_db/test/test-transaction-commit.js +0 -79
@@ -1,1396 +1,1495 @@
1
- /*
2
- Copyright (c) 2013, Dan VerWeire <dverweire@gmail.com>
3
- Copyright (c) 2010, Lee Smith<notwink@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
- #ifdef __MVS__
18
- #define _AE_BIMODAL
19
- #include <_Nascii.h>
20
- #endif
21
-
22
- // On AIX, GCC auto-generates _GLOBAL__FI_*/_GLOBAL__FD_* init/fini functions
23
- // in every shared library via crtcxa.o. The fini function calls __cxa_finalize
24
- // which crashes with SIGILL (jump to 0x0) because atexit handlers registered
25
- // by libdb2.a become invalid during process exit. (#439, #1045)
26
- // We override the init/fini with this no-op to prevent the crash.
27
- #ifdef _AIX
28
- extern "C" void __ibmdb_aix_noop(void) {}
29
- #endif
30
-
31
- #include <string.h>
32
- #include <stdint.h>
33
- #include <type_traits>
34
- #include <time.h>
35
- #include <cassert>
36
- #include <cstdlib>
37
- #include <uv.h>
38
- #ifdef _AIX
39
- #include <signal.h>
40
- #include <unistd.h>
41
- #endif
42
-
43
- #include "odbc.h"
44
- #include "odbc_connection.h"
45
- #include "odbc_result.h"
46
- #include "odbc_statement.h"
47
-
48
- #ifdef _WIN32
49
- #include "strptime.h"
50
- #endif
51
-
52
- #define FILE_PARAM 3
53
- #define DATA_AT_EXEC_PARAM 5
54
-
55
- namespace {
56
- template <typename HandleType>
57
- unsigned long long HandleToLogValueImpl(HandleType handle, std::true_type)
58
- {
59
- return static_cast<unsigned long long>(reinterpret_cast<uintptr_t>(handle));
60
- }
61
-
62
- template <typename HandleType>
63
- unsigned long long HandleToLogValueImpl(HandleType handle, std::false_type)
64
- {
65
- return static_cast<unsigned long long>(handle);
66
- }
67
-
68
- template <typename HandleType>
69
- unsigned long long HandleToLogValue(HandleType handle)
70
- {
71
- return HandleToLogValueImpl(handle, typename std::is_pointer<HandleType>::type());
72
- }
73
- }
74
-
75
- uv_mutex_t ODBC::g_odbcMutex;
76
- uv_async_t ODBC::g_async;
77
-
78
- Napi::FunctionReference ODBC::constructor;
79
-
80
- Napi::Object ODBC::Init(Napi::Env env, Napi::Object exports)
81
- {
82
- DEBUG_PRINTF("ODBC::Init\n");
83
-
84
- Napi::Function func = DefineClass(env, "ODBC", {
85
- InstanceMethod("createConnection", &ODBC::CreateConnection, NAPI_METHOD_ATTR),
86
- InstanceMethod("createConnectionSync", &ODBC::CreateConnectionSync, NAPI_METHOD_ATTR),
87
- StaticValue("SQL_CLOSE", Napi::Number::New(env, SQL_CLOSE), napi_enumerable),
88
- StaticValue("SQL_DROP", Napi::Number::New(env, SQL_DROP), napi_enumerable),
89
- StaticValue("SQL_UNBIND", Napi::Number::New(env, SQL_UNBIND), napi_enumerable),
90
- StaticValue("SQL_RESET_PARAMS", Napi::Number::New(env, SQL_RESET_PARAMS), napi_enumerable),
91
- StaticValue("SQL_DESTROY", Napi::Number::New(env, SQL_DESTROY), napi_enumerable),
92
- StaticValue("FETCH_ARRAY", Napi::Number::New(env, FETCH_ARRAY), napi_enumerable),
93
- StaticValue("FETCH_OBJECT", Napi::Number::New(env, FETCH_OBJECT), napi_enumerable),
94
- });
95
-
96
- constructor = Napi::Persistent(func);
97
- constructor.SuppressDestruct();
98
- exports.Set("ODBC", func);
99
-
100
- // Initialize the cross platform mutex provided by libuv
101
- uv_mutex_init(&ODBC::g_odbcMutex);
102
-
103
- return exports;
104
- }
105
-
106
- ODBC::~ODBC()
107
- {
108
- DEBUG_PRINTF("ODBC::~ODBC\n");
109
- this->Free();
110
- }
111
-
112
- void ODBC::Free()
113
- {
114
- DEBUG_PRINTF("ODBC::Free: m_hEnv = 0x%llx\n", HandleToLogValue(m_hEnv));
115
- if (m_hEnv)
116
- {
117
- SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv);
118
- m_hEnv = (SQLHENV)NULL;
119
- }
120
- }
121
-
122
- ODBC::ODBC(const Napi::CallbackInfo &info) : Napi::ObjectWrap<ODBC>(info)
123
- {
124
- Napi::Env env = info.Env();
125
- DEBUG_PRINTF("ODBC::New - Entry\n");
126
-
127
- m_hEnv = (SQLHENV)NULL;
128
-
129
- #ifdef __MVS__
130
- int ori_mode = __ae_thread_swapmode(__AE_EBCDIC_MODE);
131
- #endif
132
- int ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_hEnv);
133
- #ifdef __MVS__
134
- __ae_thread_swapmode(ori_mode);
135
- #endif
136
- if (!SQL_SUCCEEDED(ret))
137
- {
138
- DEBUG_PRINTF("ODBC::New - ERROR ALLOCATING ENV HANDLE!!\n");
139
- Napi::Value objError = ODBC::GetSQLError(env, SQL_HANDLE_ENV, m_hEnv);
140
- napi_throw(env, objError);
141
- return;
142
- }
143
-
144
- ret = SQLSetEnvAttr(m_hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_UINTEGER);
145
-
146
- if (!SQL_SUCCEEDED(ret))
147
- {
148
- DEBUG_PRINTF("ODBC::New - ERROR SETTING ODBC VERSION!!\n");
149
- Napi::Value objError = ODBC::GetSQLError(env, SQL_HANDLE_ENV, m_hEnv);
150
- SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv);
151
- m_hEnv = (SQLHENV)NULL;
152
- napi_throw(env, objError);
153
- return;
154
- }
155
-
156
- DEBUG_PRINTF("ODBC::New - Exit\n");
157
- }
158
-
159
- /*
160
- * CreateConnection
161
- */
162
- Napi::Value ODBC::CreateConnection(const Napi::CallbackInfo &info)
163
- {
164
- DEBUG_PRINTF("ODBC::CreateConnection - Entry\n");
165
- Napi::Env env = info.Env();
166
-
167
- REQ_FUN_ARG(0, cb);
168
-
169
- uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
170
- MEMCHECK(work_req);
171
-
172
- create_connection_work_data *data =
173
- (create_connection_work_data *)(calloc(1, sizeof(create_connection_work_data)));
174
- if (!data) free(work_req);
175
- MEMCHECK(data);
176
-
177
- data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
178
- data->dbo = this;
179
- data->env = env;
180
-
181
- work_req->data = data;
182
-
183
- uv_queue_work(uv_default_loop(), work_req, UV_CreateConnection, (uv_after_work_cb)UV_AfterCreateConnection);
184
-
185
- this->Ref();
186
-
187
- DEBUG_PRINTF("ODBC::CreateConnection - Exit\n");
188
- return env.Undefined();
189
- }
190
-
191
- void ODBC::UV_CreateConnection(uv_work_t *req)
192
- {
193
- DEBUG_PRINTF("ODBC::UV_CreateConnection - Entry\n");
194
- create_connection_work_data *data = (create_connection_work_data *)(req->data);
195
- data->result = SQLAllocHandle(SQL_HANDLE_DBC, data->dbo->m_hEnv, &data->hDBC);
196
- DEBUG_PRINTF("ODBC::UV_CreateConnection - Exit: hDBC = 0x%llx\n", HandleToLogValue(data->hDBC));
197
- }
198
-
199
- void ODBC::UV_AfterCreateConnection(uv_work_t *req, int status)
200
- {
201
- DEBUG_PRINTF("ODBC::UV_AfterCreateConnection - Entry\n");
202
- create_connection_work_data *data = (create_connection_work_data *)(req->data);
203
- Napi::Env env(data->env);
204
- Napi::HandleScope scope(env);
205
-
206
- if (!SQL_SUCCEEDED(data->result))
207
- {
208
- Napi::Value error = ODBC::GetSQLError(env, SQL_HANDLE_ENV, data->dbo->m_hEnv);
209
- data->cb->Call({error});
210
- }
211
- else
212
- {
213
- Napi::Value args[2] = {
214
- Napi::External<void>::New(env, (void *)(intptr_t)data->dbo->m_hEnv),
215
- Napi::External<void>::New(env, (void *)(intptr_t)data->hDBC)
216
- };
217
- Napi::Object js_result = ODBCConnection::constructor.New({args[0], args[1]});
218
- data->cb->Call({env.Null(), js_result});
219
- }
220
- PropagateCallbackException(env);
221
-
222
- data->dbo->Unref();
223
- delete data->cb;
224
- free(data);
225
- free(req);
226
- DEBUG_PRINTF("ODBC::UV_AfterCreateConnection - Exit\n");
227
- }
228
-
229
- /*
230
- * CreateConnectionSync
231
- */
232
- Napi::Value ODBC::CreateConnectionSync(const Napi::CallbackInfo &info)
233
- {
234
- DEBUG_PRINTF("ODBC::CreateConnectionSync - Entry\n");
235
- Napi::Env env = info.Env();
236
-
237
- SQLHDBC hDBC;
238
- SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_DBC, m_hEnv, &hDBC);
239
-
240
- if (!SQL_SUCCEEDED(ret))
241
- {
242
- // TODO: do something!
243
- }
244
-
245
- Napi::Value params[2] = {
246
- Napi::External<void>::New(env, (void *)(intptr_t)m_hEnv),
247
- Napi::External<void>::New(env, (void *)(intptr_t)hDBC)
248
- };
249
- Napi::Object js_result = ODBCConnection::constructor.New({params[0], params[1]});
250
-
251
- DEBUG_PRINTF("ODBC::CreateConnectionSync - Exit: hDBC = 0x%llx\n", HandleToLogValue(hDBC));
252
- return js_result;
253
- }
254
-
255
- /*
256
- * GetColumns
257
- */
258
- Column *ODBC::GetColumns(SQLHSTMT hStmt, short *colCount)
259
- {
260
- SQLRETURN ret;
261
- SQLSMALLINT buflen;
262
- SQLSMALLINT typebuflen;
263
- DEBUG_PRINTF("ODBC::GetColumns - Entry\n");
264
-
265
- *colCount = 0;
266
- ret = SQLNumResultCols(hStmt, colCount);
267
-
268
- if (!SQL_SUCCEEDED(ret))
269
- {
270
- return new Column[0];
271
- }
272
-
273
- Column *columns = new Column[*colCount];
274
- char colname[MAX_FIELD_SIZE];
275
-
276
- for (int i = 0; i < *colCount; i++)
277
- {
278
- columns[i].index = i + 1;
279
- columns[i].getData = false;
280
- columns[i].isFileBound = false;
281
- colname[0] = '\0';
282
- buflen = 0;
283
-
284
- ret = SQLColAttribute(hStmt, columns[i].index,
285
- #ifdef STRICT_COLUMN_NAMES
286
- SQL_DESC_NAME,
287
- #else
288
- SQL_DESC_LABEL,
289
- #endif
290
- colname, (SQLSMALLINT)MAX_FIELD_SIZE, &buflen, NULL);
291
-
292
- columns[i].name_len = buflen;
293
- if (buflen > 0)
294
- {
295
- columns[i].name = new unsigned char[buflen + 2];
296
- memcpy(columns[i].name, colname, buflen);
297
- columns[i].name[buflen] = '\0';
298
- columns[i].name[buflen + 1] = '\0';
299
- }
300
- DEBUG_PRINTF("ODBC::GetColumns index = %i, buflen=%i\n", columns[i].index, buflen);
301
-
302
- ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_DISPLAY_SIZE,
303
- NULL, 0, NULL, &columns[i].max_display_len);
304
- ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_PRECISION,
305
- NULL, 0, NULL, &columns[i].precision);
306
- ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_SCALE,
307
- NULL, 0, NULL, &columns[i].scale);
308
- ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_LENGTH,
309
- NULL, 0, NULL, &columns[i].field_len);
310
- ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_CONCISE_TYPE,
311
- NULL, 0, NULL, &columns[i].type);
312
-
313
- columns[i].type_name = new unsigned char[(MAX_FIELD_SIZE)];
314
- columns[i].type_name[0] = '\0';
315
-
316
- ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_TYPE_NAME,
317
- columns[i].type_name, (SQLSMALLINT)(MAX_FIELD_SIZE),
318
- &typebuflen, NULL);
319
- }
320
- DEBUG_PRINTF("ODBC::GetColumns - Exit\n");
321
- return columns;
322
- }
323
-
324
- /*
325
- * FreeColumns
326
- */
327
- void ODBC::FreeColumns(Column *&columns, short *colCount)
328
- {
329
- DEBUG_PRINTF("ODBC::FreeColumns - Entry\n");
330
- for (int i = 0; i < *colCount; i++)
331
- {
332
- delete[] columns[i].name;
333
- delete[] columns[i].type_name;
334
- }
335
- if (columns != NULL)
336
- {
337
- delete[] columns;
338
- columns = NULL;
339
- }
340
- *colCount = 0;
341
- DEBUG_PRINTF("ODBC::FreeColumns - Exit\n");
342
- }
343
-
344
- /*
345
- * GetColumnValue
346
- */
347
- Napi::Value ODBC::GetColumnValue(Napi::Env env, SQLHSTMT hStmt, Column column,
348
- uint16_t *buffer, size_t bufferLength)
349
- {
350
- if (column.isFileBound) {
351
- return env.Null();
352
- }
353
-
354
- SQLLEN len = 0;
355
- SQLLEN maxBuffSize = 0;
356
- unsigned short terCharLen = 1;
357
- SQLRETURN ret = SQL_SUCCESS;
358
- SQLSMALLINT ctype = SQL_C_TCHAR;
359
- char *errmsg = (char *)"[node-ibm_db] Error in ODBC::GetColumnValue";
360
-
361
- #ifdef UNICODE
362
- terCharLen = 2;
363
- #endif
364
-
365
- DEBUG_PRINTF("Column Type : %i\t%i\t%i\t%i\n", column.type, SQL_DATETIME,
366
- SQL_TIMESTAMP, SQL_TYPE_TIME);
367
-
368
- switch (column.type)
369
- {
370
- case SQL_BLOB:
371
- case SQL_BINARY:
372
- case SQL_VARBINARY:
373
- case SQL_LONGVARBINARY:
374
- terCharLen = 0;
375
- ctype = SQL_C_BINARY;
376
- break;
377
- }
378
-
379
- switch (column.type)
380
- {
381
- case SQL_INTEGER:
382
- case SQL_SMALLINT:
383
- case SQL_TINYINT:
384
- {
385
- SQLINTEGER value;
386
- ret = SQLGetData(hStmt, column.index, SQL_C_SLONG, &value, sizeof(value), &len);
387
-
388
- if (len == SQL_NULL_DATA) return env.Null();
389
- return Napi::Number::New(env, (double)value);
390
- }
391
- break;
392
-
393
- case SQL_NUMERIC:
394
- case SQL_DECIMAL:
395
- case SQL_DECFLOAT:
396
- case SQL_FLOAT:
397
- case SQL_REAL:
398
- case SQL_DOUBLE:
399
- {
400
- double value;
401
- ret = SQLGetData(hStmt, column.index, SQL_C_DOUBLE, &value, sizeof(value), &len);
402
-
403
- if (len == SQL_NULL_DATA) return env.Null();
404
- return Napi::Number::New(env, value);
405
- }
406
- break;
407
-
408
- case SQL_DATETIME:
409
- case SQL_TIMESTAMP:
410
- {
411
- SQL_TIMESTAMP_STRUCT odbcTime;
412
- #ifdef _WIN32
413
- struct tm timeInfo = {};
414
- ret = SQLGetData(hStmt, column.index, SQL_C_CHAR, &odbcTime, sizeof(odbcTime), &len);
415
- #else
416
- #if defined(_AIX) || defined(__MVS__)
417
- struct tm timeInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0};
418
- #else
419
- struct tm timeInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
420
- #endif
421
- ret = SQLGetData(hStmt, column.index, SQL_C_TYPE_TIMESTAMP, &odbcTime, sizeof(odbcTime), &len);
422
- #endif
423
-
424
- if (len == SQL_NULL_DATA) return env.Null();
425
-
426
- timeInfo.tm_year = odbcTime.year - 1900;
427
- timeInfo.tm_mon = odbcTime.month - 1;
428
- timeInfo.tm_mday = odbcTime.day;
429
- timeInfo.tm_hour = odbcTime.hour;
430
- timeInfo.tm_min = odbcTime.minute;
431
- timeInfo.tm_sec = odbcTime.second;
432
- timeInfo.tm_isdst = -1;
433
-
434
- #ifdef TIMEGM
435
- return Napi::Date::New(env, (double(timegm(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000));
436
- #else
437
- return Napi::Date::New(env, (double(mktime(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000));
438
- #endif
439
- }
440
- break;
441
-
442
- case SQL_BIT:
443
- {
444
- char bit[4] = {'\0'};
445
- ret = SQLGetData(hStmt, column.index, SQL_C_CHAR, &bit, 4, &len);
446
- if (len == SQL_NULL_DATA) return env.Null();
447
- return Napi::Boolean::New(env, (*bit == '0') ? false : true);
448
- }
449
- break;
450
-
451
- case SQL_TYPE_TIME:
452
- case SQL_BIGINT:
453
- if (column.type == SQL_BIGINT) ctype = SQL_C_CHAR;
454
- /* fall through */
455
- case SQL_DBCLOB:
456
- if (column.type == SQL_DBCLOB) ctype = SQL_C_CHAR;
457
- /* fall through */
458
- default:
459
- {
460
- uint16_t *tmp_out_ptr = NULL;
461
- uint32_t bufferSize = 0;
462
- uint32_t newbufflen = 0;
463
- unsigned short secondGetData = 0;
464
- len = 0;
465
- bufferSize = bufferLength + terCharLen;
466
-
467
- if (buffer == NULL)
468
- {
469
- buffer = (uint16_t *)malloc(bufferSize);
470
- if (buffer == NULL)
471
- {
472
- errmsg = (char *)"Failed to allocate memory buffer for column data.";
473
- napi_throw(env, ODBC::GetSQLError(env, SQL_HANDLE_STMT, hStmt, errmsg));
474
- return env.Undefined();
475
- }
476
- }
477
- maxBuffSize = bufferSize;
478
- memset(buffer, '\0', bufferSize);
479
- ret = SQLGetData(hStmt, column.index, ctype, (char *)buffer, maxBuffSize, &len);
480
-
481
- if (column.getData) { secondGetData = 1; }
482
- else if (ret == SQL_SUCCESS_WITH_INFO && len > 0)
483
- {
484
- if (len + terCharLen > 0x3fffffff)
485
- newbufflen = 0x3fffffff;
486
- else
487
- newbufflen = len + terCharLen;
488
-
489
- tmp_out_ptr = (uint16_t *)malloc(newbufflen);
490
- if (tmp_out_ptr == NULL)
491
- {
492
- ret = (SQLRETURN)-3;
493
- newbufflen = bufferSize;
494
- }
495
- else
496
- {
497
- memcpy(tmp_out_ptr, (char *)buffer, bufferLength);
498
- free((uint16_t *)buffer);
499
- buffer = tmp_out_ptr;
500
- maxBuffSize = newbufflen - bufferLength;
501
- len = 0;
502
- ret = SQLGetData(hStmt, column.index, ctype, (char *)buffer + bufferLength, maxBuffSize, &len);
503
- ret = SQL_SUCCESS;
504
- len = newbufflen;
505
- secondGetData = 1;
506
- }
507
- }
508
- if (len >= 0) newbufflen = len;
509
-
510
- if (len == SQL_NULL_DATA)
511
- {
512
- FREE(buffer);
513
- return env.Null();
514
- }
515
- else if (SQL_SUCCEEDED(ret) || secondGetData)
516
- {
517
- if (ctype == SQL_C_BINARY)
518
- {
519
- // Return binary data as buffer - transfer ownership to Node
520
- Napi::Buffer<char> buf = Napi::Buffer<char>::Copy(env, (char *)buffer, newbufflen);
521
- FREE(buffer);
522
- return buf;
523
- }
524
- else
525
- {
526
- #ifdef UNICODE
527
- Napi::Value str = Napi::String::New(env, (char16_t *)buffer);
528
- #else
529
- Napi::Value str = Napi::String::New(env, (char *)buffer);
530
- #endif
531
- FREE(buffer);
532
- return str;
533
- }
534
- }
535
- else
536
- {
537
- FREE(buffer);
538
- if (ret == SQL_INVALID_HANDLE)
539
- {
540
- unsigned long long hstmt_value = HandleToLogValue(hStmt);
541
- #ifdef __MVS__
542
- __fprintf_a(stdout, "Invalid Handle: SQLGetData return code = %i, stmt handle = 0x%llx"
543
- ", columnType = %i, index = %i\n",
544
- ret, hstmt_value, (int)column.type, column.index);
545
- #else
546
- fprintf(stdout, "Invalid Handle: SQLGetData return code = %i, stmt handle = 0x%llx"
547
- ", columnType = %i, index = %i\n",
548
- ret, hstmt_value, (int)column.type, column.index);
549
- #endif
550
- assert(ret != SQL_INVALID_HANDLE);
551
- }
552
- napi_throw(env, ODBC::GetSQLError(env, SQL_HANDLE_STMT, hStmt, errmsg));
553
- return env.Undefined();
554
- }
555
- }
556
- }
557
- }
558
-
559
- /*
560
- * GetOutputParameter
561
- */
562
- Napi::Value ODBC::GetOutputParameter(Napi::Env env, Parameter *prm)
563
- {
564
- bool sqlTypeBinary = false;
565
-
566
- switch (prm->type)
567
- {
568
- case SQL_BLOB:
569
- case SQL_BINARY:
570
- case SQL_VARBINARY:
571
- case SQL_LONGVARBINARY:
572
- sqlTypeBinary = true;
573
- break;
574
- }
575
-
576
- if (((int)prm->length == SQL_NULL_DATA) || !prm->buffer)
577
- {
578
- return env.Null();
579
- }
580
-
581
- switch (prm->type)
582
- {
583
- case SQL_INTEGER:
584
- case SQL_SMALLINT:
585
- case SQL_TINYINT:
586
- case SQL_BOOLEAN:
587
- case SQL_NUMERIC:
588
- case SQL_BIGINT:
589
- if (prm->length == sizeof(int))
590
- return Napi::Number::New(env, *(int *)prm->buffer);
591
- else if (prm->length == sizeof(short))
592
- return Napi::Number::New(env, *(short *)prm->buffer);
593
- else if (prm->length == sizeof(long))
594
- return Napi::Number::New(env, (double)*(long *)prm->buffer);
595
- else if (prm->length == sizeof(long long))
596
- return Napi::Number::New(env, (double)*(long long *)prm->buffer);
597
- else
598
- return Napi::Number::New(env, *(char *)prm->buffer);
599
- break;
600
-
601
- case SQL_DECIMAL:
602
- case SQL_FLOAT:
603
- case SQL_DECFLOAT:
604
- case SQL_REAL:
605
- case SQL_DOUBLE:
606
- return Napi::Number::New(env, *(double *)prm->buffer);
607
- break;
608
-
609
- case SQL_BIT:
610
- return Napi::Boolean::New(env, (*((char *)prm->buffer) == '0') ? false : true);
611
- break;
612
-
613
- default:
614
- if (prm->isBuffer)
615
- {
616
- return Napi::Buffer<char>::Copy(env, (char *)prm->buffer, prm->length);
617
- }
618
- else if (sqlTypeBinary || prm->c_type == SQL_C_BINARY)
619
- {
620
- prm->isBuffer = true;
621
- return Napi::Buffer<char>::Copy(env, (char *)prm->buffer, prm->length);
622
- }
623
- else
624
- {
625
- #ifdef UNICODE
626
- return Napi::String::New(env, (char16_t *)prm->buffer);
627
- #else
628
- return Napi::String::New(env, (char *)prm->buffer);
629
- #endif
630
- }
631
- }
632
- }
633
-
634
- /*
635
- * GetRecordTuple
636
- */
637
- Napi::Object ODBC::GetRecordTuple(Napi::Env env, SQLHSTMT hStmt, Column *columns,
638
- short *colCount, uint16_t *buffer, size_t bufferLength)
639
- {
640
- Napi::Object tuple = Napi::Object::New(env);
641
-
642
- for (int i = 0; i < *colCount; i++)
643
- {
644
- #ifdef UNICODE
645
- tuple.Set(Napi::String::New(env, (char16_t *)columns[i].name),
646
- GetColumnValue(env, hStmt, columns[i], buffer, bufferLength));
647
- #else
648
- tuple.Set(Napi::String::New(env, (const char *)columns[i].name),
649
- GetColumnValue(env, hStmt, columns[i], buffer, bufferLength));
650
- #endif
651
- }
652
-
653
- return tuple;
654
- }
655
-
656
- /*
657
- * GetRecordArray
658
- */
659
- Napi::Value ODBC::GetRecordArray(Napi::Env env, SQLHSTMT hStmt, Column *columns,
660
- short *colCount, uint16_t *buffer, size_t bufferLength)
661
- {
662
- Napi::Array array = Napi::Array::New(env);
663
-
664
- for (int i = 0; i < *colCount; i++)
665
- {
666
- array.Set(i, GetColumnValue(env, hStmt, columns[i], buffer, bufferLength));
667
- }
668
-
669
- return array;
670
- }
671
-
672
- /*
673
- * GetParametersFromArray
674
- */
675
- Parameter *ODBC::GetParametersFromArray(Napi::Env env, Napi::Array values, int *paramCount)
676
- {
677
- DEBUG_PRINTF("ODBC::GetParametersFromArray\n");
678
- *paramCount = values.Length();
679
-
680
- Parameter *params = (Parameter *)malloc(*paramCount * sizeof(Parameter));
681
- if (*paramCount != 0 && !params)
682
- {
683
- Napi::Error::New(env, "Could not allocate enough memory for params").ThrowAsJavaScriptException();
684
- return params;
685
- }
686
- memset(params, '\0', *paramCount * sizeof(Parameter));
687
-
688
- for (int i = 0; i < *paramCount; i++)
689
- {
690
- Napi::Value value = values.Get(i);
691
- params[i].paramtype = SQL_PARAM_INPUT;
692
- params[i].size = 0;
693
- params[i].length = SQL_NULL_DATA;
694
- params[i].buffer_length = 0;
695
- params[i].decimals = 0;
696
- params[i].isBuffer = false;
697
-
698
- if (value.IsArray())
699
- {
700
- Napi::Array paramArray = value.As<Napi::Array>();
701
- int arrlen = paramArray.Length();
702
- if (arrlen < 4)
703
- {
704
- Napi::Error::New(env, "Wrong param format!").ThrowAsJavaScriptException();
705
- return params;
706
- }
707
-
708
- Napi::Value val = paramArray.Get((uint32_t)0);
709
- if (val.IsNumber()) params[i].paramtype = val.As<Napi::Number>().Int32Value();
710
-
711
- val = paramArray.Get((uint32_t)1);
712
- if (val.IsNumber()) params[i].c_type = val.As<Napi::Number>().Int32Value();
713
- else params[i].c_type = SQL_C_CHAR;
714
-
715
- val = paramArray.Get((uint32_t)2);
716
- if (val.IsNumber()) params[i].type = val.As<Napi::Number>().Int32Value();
717
- else params[i].type = SQL_CHAR;
718
-
719
- if (arrlen == 5)
720
- {
721
- val = paramArray.Get((uint32_t)4);
722
- if (val.IsNumber()) params[i].buffer_length = val.As<Napi::Number>().Int32Value();
723
- }
724
-
725
- val = paramArray.Get((uint32_t)3);
726
- if (params[i].paramtype == DATA_AT_EXEC_PARAM && val.IsArray())
727
- {
728
- GetDataAtExecParam(env, val, &params[i], i + 1);
729
- }
730
- else if (val.IsNull() || val.IsUndefined())
731
- {
732
- GetNullParam(&params[i], i + 1);
733
- }
734
- else if (val.IsNumber() && !val.IsNull() && params[i].type != 3)
735
- {
736
- // Check if integer
737
- double dval = val.As<Napi::Number>().DoubleValue();
738
- int32_t ival = val.As<Napi::Number>().Int32Value();
739
- if (dval == (double)ival && params[i].type != 3)
740
- GetInt32Param(env, val, &params[i], i + 1);
741
- else
742
- GetNumberParam(env, val, &params[i], i + 1);
743
- }
744
- else if (val.IsBoolean())
745
- {
746
- GetBoolParam(env, val, &params[i], i + 1);
747
- }
748
- else if (val.IsArray())
749
- {
750
- GetArrayParam(env, val, &params[i], i + 1);
751
- }
752
- else if (val.IsBuffer())
753
- {
754
- GetBufferParam(val, &params[i], i + 1);
755
- }
756
- else
757
- {
758
- GetStringParam(env, val, &params[i], i + 1);
759
- }
760
- }
761
- else if (value.IsBuffer())
762
- {
763
- GetBufferParam(value, &params[i], i + 1);
764
- }
765
- else if (value.IsString())
766
- {
767
- GetStringParam(env, value, &params[i], i + 1);
768
- }
769
- else if (value.IsNull() || value.IsUndefined())
770
- {
771
- GetNullParam(&params[i], i + 1);
772
- }
773
- else if (value.IsNumber())
774
- {
775
- double dval = value.As<Napi::Number>().DoubleValue();
776
- int32_t ival = value.As<Napi::Number>().Int32Value();
777
- if (dval == (double)ival)
778
- GetInt32Param(env, value, &params[i], i + 1);
779
- else
780
- GetNumberParam(env, value, &params[i], i + 1);
781
- }
782
- else if (value.IsBoolean())
783
- {
784
- GetBoolParam(env, value, &params[i], i + 1);
785
- }
786
- }
787
- return params;
788
- }
789
-
790
- void ODBC::GetStringParam(Napi::Env env, Napi::Value value, Parameter *param, int num)
791
- {
792
- int length = 0;
793
- int bufflen = 0;
794
- int terCharLen = 1;
795
- bool isBinary = (param->c_type == SQL_C_BINARY);
796
-
797
- // Determine string length using encoding that matches NAN behavior:
798
- // SQL_C_BINARY -> Latin-1 (WriteOneByte), UNICODE -> UTF-16 (Write), else -> UTF-8
799
- if (isBinary) {
800
- size_t latin1_len = 0;
801
- napi_get_value_string_latin1(env, value, NULL, 0, &latin1_len);
802
- length = (int)latin1_len;
803
- }
804
- #ifdef UNICODE
805
- else {
806
- size_t utf16_len = 0;
807
- napi_get_value_string_utf16(env, value, NULL, 0, &utf16_len);
808
- length = (int)utf16_len;
809
- }
810
- #else
811
- else {
812
- std::string utf8str = value.ToString().Utf8Value();
813
- length = (int)utf8str.length();
814
- }
815
- #endif
816
-
817
- bufflen = length;
818
- param->length = length;
819
- if (!param->c_type || (param->c_type == SQL_CHAR))
820
- param->c_type = SQL_C_TCHAR;
821
- #ifdef UNICODE
822
- param->length = SQL_NTS;
823
- if (!param->type || (param->type == SQL_CHAR))
824
- param->type = (length >= 8000) ? SQL_WLONGVARCHAR : SQL_WVARCHAR;
825
- if (!isBinary)
826
- bufflen = (length + 1) * sizeof(uint16_t);
827
- terCharLen = 2;
828
- #else
829
- if (!param->type || (param->type == SQL_CHAR))
830
- param->type = (length >= 8000) ? SQL_LONGVARCHAR : SQL_VARCHAR;
831
- if (!isBinary)
832
- {
833
- std::string utf8str = value.ToString().Utf8Value();
834
- bufflen = (int)utf8str.length() + 1;
835
- param->length = bufflen - 1;
836
- }
837
- #endif
838
- if (isBinary) { bufflen = length + 1; }
839
- if (bufflen <= param->buffer_length &&
840
- ((param->paramtype % 2 == 0) || (param->arraySize > 0)))
841
- {
842
- bufflen = param->buffer_length + terCharLen;
843
- }
844
- param->buffer_length = bufflen;
845
- param->size = bufflen;
846
-
847
- param->buffer = malloc(param->buffer_length);
848
- if (!param->buffer) return;
849
- memset(param->buffer, 0, param->buffer_length);
850
-
851
- // Copy string data using correct encoding (matching NAN behavior)
852
- if (param->paramtype == FILE_PARAM) {
853
- // FILE_PARAM: use UTF-8 (matches NAN's WriteUtf8)
854
- std::string utf8str = value.ToString().Utf8Value();
855
- memcpy(param->buffer, utf8str.c_str(), utf8str.length());
856
- } else if (isBinary) {
857
- // SQL_C_BINARY: use Latin-1 (matches NAN's WriteOneByte)
858
- size_t copied = 0;
859
- napi_get_value_string_latin1(env, value, (char *)param->buffer, param->buffer_length, &copied);
860
- } else {
861
- #ifdef UNICODE
862
- // UNICODE text: use UTF-16 (matches NAN's String::Write)
863
- size_t copied = 0;
864
- napi_get_value_string_utf16(env, value, (char16_t *)param->buffer, length + 1, &copied);
865
- #else
866
- // Non-UNICODE text: use UTF-8 (matches NAN's WriteUtf8)
867
- std::string utf8str = value.ToString().Utf8Value();
868
- memcpy(param->buffer, utf8str.c_str(), utf8str.length());
869
- #endif
870
- }
871
-
872
- #ifdef UNICODE
873
- ((char *)param->buffer)[param->buffer_length - 2] = '\0';
874
- #endif
875
- ((char *)param->buffer)[param->buffer_length - 1] = '\0';
876
-
877
- if (param->paramtype == FILE_PARAM)
878
- {
879
- param->decimals = param->buffer_length;
880
- param->fileOption = SQL_FILE_READ;
881
- param->fileIndicator = 0;
882
- }
883
- }
884
-
885
- void ODBC::GetBufferParam(Napi::Value value, Parameter *param, int num)
886
- {
887
- Napi::Buffer<char> buffer = value.As<Napi::Buffer<char>>();
888
- param->buffer = (char *)buffer.Data();
889
- param->length = buffer.Length();
890
- int bufflen = param->length;
891
- param->isBuffer = true;
892
-
893
- if (bufflen <= param->buffer_length &&
894
- ((param->paramtype % 2 == 0) || (param->arraySize > 0)))
895
- {
896
- bufflen = param->buffer_length;
897
- }
898
- param->buffer_length = bufflen;
899
- param->size = bufflen;
900
-
901
- if (!param->c_type || (param->c_type == SQL_CHAR))
902
- param->c_type = SQL_C_TCHAR;
903
- #ifdef UNICODE
904
- param->length = SQL_NTS;
905
- if (!param->type || (param->type == SQL_CHAR))
906
- param->type = (param->length >= 8000) ? SQL_WLONGVARCHAR : SQL_WVARCHAR;
907
- if (param->c_type != SQL_C_BINARY) param->size /= 2;
908
- #else
909
- if (!param->type || (param->type == SQL_CHAR))
910
- param->type = (param->length >= 8000) ? SQL_LONGVARCHAR : SQL_VARCHAR;
911
- #endif
912
-
913
- if (param->paramtype == FILE_PARAM)
914
- {
915
- param->decimals = param->buffer_length;
916
- param->fileOption = SQL_FILE_READ;
917
- param->fileIndicator = 0;
918
- }
919
- }
920
-
921
- void ODBC::GetDataAtExecParam(Napi::Env env, Napi::Value value, Parameter *param, int num)
922
- {
923
- Napi::Array chunkArray = value.As<Napi::Array>();
924
- int numChunks = chunkArray.Length();
925
-
926
- if (numChunks == 0)
927
- {
928
- Napi::Error::New(env, "DATA_AT_EXEC: Data array must not be empty").ThrowAsJavaScriptException();
929
- return;
930
- }
931
-
932
- param->chunks = (void **)calloc(numChunks, sizeof(void *));
933
- param->chunkLens = (SQLLEN *)calloc(numChunks, sizeof(SQLLEN));
934
- if (!param->chunks || !param->chunkLens)
935
- {
936
- Napi::Error::New(env, "Failed to allocate chunk arrays for DATA_AT_EXEC").ThrowAsJavaScriptException();
937
- return;
938
- }
939
- param->chunkCount = numChunks;
940
-
941
- SQLLEN totalSize = 0;
942
- for (int j = 0; j < numChunks; j++)
943
- {
944
- Napi::Value chunk = chunkArray.Get(j);
945
- if (!chunk.IsBuffer())
946
- {
947
- Napi::Error::New(env, "DATA_AT_EXEC: each element in Data array must be a Buffer").ThrowAsJavaScriptException();
948
- return;
949
- }
950
- Napi::Buffer<char> buf = chunk.As<Napi::Buffer<char>>();
951
- param->chunks[j] = buf.Data();
952
- param->chunkLens[j] = (SQLLEN)buf.Length();
953
- totalSize += param->chunkLens[j];
954
- }
955
-
956
- if (param->buffer_length > 0)
957
- param->size = param->buffer_length;
958
- else
959
- param->size = totalSize;
960
-
961
- param->buffer = NULL;
962
- param->buffer_length = 0;
963
- param->length = SQL_LEN_DATA_AT_EXEC((SQLLEN)param->size);
964
- param->isBuffer = true;
965
- }
966
-
967
- void ODBC::GetNullParam(Parameter *param, int num)
968
- {
969
- param->c_type = param->c_type ? param->c_type : SQL_C_DEFAULT;
970
- param->type = param->type ? param->type : SQL_VARCHAR;
971
- param->length = SQL_NULL_DATA;
972
-
973
- if (param->paramtype % 2 == 0)
974
- {
975
- if (param->type == SQL_BIGINT || param->type == SQL_INTEGER ||
976
- param->type == SQL_BOOLEAN || param->type == SQL_SMALLINT ||
977
- param->type == SQL_TINYINT)
978
- {
979
- param->buffer = new int64_t(0);
980
- }
981
- else if (param->type == SQL_DECIMAL || param->type == SQL_DOUBLE ||
982
- param->type == SQL_REAL || param->type == SQL_FLOAT ||
983
- param->type == SQL_NUMERIC || param->type == SQL_DECFLOAT)
984
- {
985
- param->buffer = new double(0);
986
- }
987
- else if (param->buffer_length > 0)
988
- {
989
- param->buffer = malloc(param->buffer_length);
990
- }
991
- else
992
- param->buffer = malloc(8);
993
- }
994
- }
995
-
996
- void ODBC::GetInt32Param(Napi::Env env, Napi::Value value, Parameter *param, int num)
997
- {
998
- int64_t *number = new int64_t(value.As<Napi::Number>().Int32Value());
999
- param->c_type = SQL_C_SBIGINT;
1000
- if (!param->type || (param->type == 1)) param->type = SQL_BIGINT;
1001
- param->buffer = number;
1002
- param->length = sizeof(number);
1003
- }
1004
-
1005
- void ODBC::GetNumberParam(Napi::Env env, Napi::Value value, Parameter *param, int num)
1006
- {
1007
- double *number = new double(value.As<Napi::Number>().DoubleValue());
1008
- std::string string = value.ToString().Utf8Value();
1009
- int length = (int)string.length();
1010
- char *ptr = strchr((char *)string.c_str(), '.');
1011
-
1012
- if (!param->c_type || (param->c_type == SQL_C_CHAR)) param->c_type = SQL_C_DOUBLE;
1013
- if (!param->type || (param->type == SQL_CHAR)) param->type = SQL_DECIMAL;
1014
- param->buffer = number;
1015
- param->buffer_length = sizeof(double);
1016
- param->length = param->buffer_length;
1017
- param->size = sizeof(double);
1018
- if (ptr)
1019
- {
1020
- param->decimals = length - (ptr - string.c_str()) - 1;
1021
- if (param->type == SQL_DECIMAL) param->size = length - 1;
1022
- }
1023
- else
1024
- {
1025
- param->decimals = 0;
1026
- if (param->type == SQL_DECIMAL) param->size = length;
1027
- }
1028
- }
1029
-
1030
- void ODBC::GetBoolParam(Napi::Env env, Napi::Value value, Parameter *param, int num)
1031
- {
1032
- bool boolean = value.As<Napi::Boolean>().Value();
1033
- int64_t *number = new int64_t(boolean ? 1 : 0);
1034
- param->c_type = SQL_C_SBIGINT;
1035
- if (!param->type || (param->type == 1)) param->type = SQL_BIGINT;
1036
- param->buffer = number;
1037
- param->length = sizeof(number);
1038
- }
1039
-
1040
- void ODBC::GetArrayParam(Napi::Env env, Napi::Value value, Parameter *param, int num)
1041
- {
1042
- Napi::Array valueArray = value.As<Napi::Array>();
1043
- int arrlen = valueArray.Length();
1044
-
1045
- param->arraySize = arrlen;
1046
- if (arrlen <= 0)
1047
- {
1048
- param->buffer = new int[0];
1049
- return;
1050
- }
1051
-
1052
- param->strLenArray = new SQLINTEGER[arrlen];
1053
-
1054
- for (int j = 0; j < arrlen; j++)
1055
- {
1056
- Napi::Value val = valueArray.Get(j);
1057
- if (val.IsNull() || val.IsUndefined())
1058
- {
1059
- param->strLenArray[j] = SQL_NULL_DATA;
1060
- if (j == arrlen - 1)
1061
- {
1062
- GetNullParam(param, num);
1063
- param->buffer = new int[arrlen];
1064
- }
1065
- continue;
1066
- }
1067
- else if (val.IsNumber())
1068
- {
1069
- double dval = val.As<Napi::Number>().DoubleValue();
1070
- int32_t ival = val.As<Napi::Number>().Int32Value();
1071
- if (dval == (double)ival && param->type != 3)
1072
- {
1073
- int64_t *number = new int64_t[arrlen];
1074
- for (int i = j; i < arrlen; i++)
1075
- {
1076
- if (i != j)
1077
- {
1078
- val = valueArray.Get(i);
1079
- if (val.IsNull() || val.IsUndefined()) { param->strLenArray[i] = SQL_NULL_DATA; continue; }
1080
- }
1081
- number[i] = val.As<Napi::Number>().Int64Value();
1082
- param->strLenArray[i] = sizeof(int64_t);
1083
- }
1084
- param->c_type = SQL_C_SBIGINT;
1085
- if (!param->type || (param->type == 1)) param->type = SQL_BIGINT;
1086
- param->buffer = number;
1087
- }
1088
- else
1089
- {
1090
- double *number = new double[arrlen];
1091
- int decimals = 0;
1092
- for (int i = j; i < arrlen; i++)
1093
- {
1094
- if (i != j)
1095
- {
1096
- val = valueArray.Get(i);
1097
- if (val.IsNull() || val.IsUndefined()) { param->strLenArray[i] = SQL_NULL_DATA; continue; }
1098
- }
1099
- GetNumberParam(env, val, param, num);
1100
- number[i] = *(double *)param->buffer;
1101
- param->strLenArray[i] = param->length;
1102
- delete (double *)param->buffer;
1103
- param->buffer = NULL;
1104
- decimals = (decimals < param->decimals) ? param->decimals : decimals;
1105
- }
1106
- param->decimals = decimals;
1107
- param->buffer = number;
1108
- }
1109
- }
1110
- else if (val.IsBoolean())
1111
- {
1112
- int64_t *boolean = new int64_t[arrlen];
1113
- for (int i = j; i < arrlen; i++)
1114
- {
1115
- if (i != j)
1116
- {
1117
- val = valueArray.Get(i);
1118
- if (val.IsNull() || val.IsUndefined()) { param->strLenArray[i] = SQL_NULL_DATA; continue; }
1119
- }
1120
- GetBoolParam(env, val, param, num);
1121
- boolean[i] = *(int64_t *)(param->buffer);
1122
- param->strLenArray[i] = (SQLINTEGER)param->length;
1123
- delete (int64_t *)param->buffer;
1124
- param->buffer = NULL;
1125
- }
1126
- param->buffer = boolean;
1127
- }
1128
- else
1129
- {
1130
- SQLPOINTER *buff = (SQLPOINTER *)NULL;
1131
- SQLULEN bufflen = 0;
1132
- SQLULEN cbValueMax = 0;
1133
-
1134
- SQLPOINTER *elemBufs = new SQLPOINTER[arrlen]();
1135
- SQLULEN *elemLens = new SQLULEN[arrlen]();
1136
- SQLLEN *elemDataLens = new SQLLEN[arrlen]();
1137
- bool *elemIsBuf = new bool[arrlen]();
1138
-
1139
- for (int i = j; i < arrlen; i++)
1140
- {
1141
- if (i != j)
1142
- {
1143
- val = valueArray.Get(i);
1144
- if (val.IsNull() || val.IsUndefined()) { param->strLenArray[i] = SQL_NULL_DATA; continue; }
1145
- }
1146
- if (val.IsBuffer())
1147
- {
1148
- GetBufferParam(val, param, num);
1149
- }
1150
- else
1151
- {
1152
- GetStringParam(env, val, param, num);
1153
- #ifdef UNICODE
1154
- param->buffer_length -= 2;
1155
- #else
1156
- param->buffer_length -= 1;
1157
- #endif
1158
- }
1159
- elemBufs[i] = param->buffer;
1160
- elemLens[i] = param->buffer_length;
1161
- elemDataLens[i] = param->length;
1162
- elemIsBuf[i] = param->isBuffer;
1163
- param->buffer = NULL;
1164
-
1165
- if ((SQLULEN)param->buffer_length > cbValueMax)
1166
- cbValueMax = (SQLULEN)param->buffer_length;
1167
- }
1168
-
1169
- bufflen = (SQLULEN)arrlen * cbValueMax;
1170
- buff = (SQLPOINTER *)malloc(bufflen);
1171
- memset(buff, 0, bufflen);
1172
-
1173
- for (int i = j; i < arrlen; i++)
1174
- {
1175
- if (elemBufs[i] == NULL) continue;
1176
- SQLULEN copyLen = cbValueMax;
1177
- if (copyLen > (SQLULEN)elemDataLens[i]) copyLen = (SQLULEN)elemDataLens[i];
1178
- memcpy((char *)buff + (SQLULEN)i * cbValueMax, elemBufs[i], copyLen);
1179
- param->strLenArray[i] = (SQLINTEGER)copyLen;
1180
- if (!elemIsBuf[i]) FREE(elemBufs[i]);
1181
- }
1182
-
1183
- delete[] elemBufs;
1184
- delete[] elemLens;
1185
- delete[] elemDataLens;
1186
- delete[] elemIsBuf;
1187
-
1188
- param->buffer = buff;
1189
- param->buffer_length = cbValueMax;
1190
- param->size = cbValueMax;
1191
- }
1192
- break;
1193
- }
1194
- }
1195
-
1196
- SQLRETURN ODBC::BindParameters(SQLHSTMT hSTMT, Parameter params[], int count)
1197
- {
1198
- SQLRETURN ret = SQL_SUCCESS;
1199
- Parameter prm;
1200
-
1201
- for (int i = 0; i < count; i++)
1202
- {
1203
- prm = params[i];
1204
-
1205
- if (prm.paramtype == FILE_PARAM)
1206
- {
1207
- ret = SQLBindFileToParam(hSTMT, i + 1, prm.type, (SQLCHAR *)prm.buffer,
1208
- NULL, &params[i].fileOption, prm.decimals, &params[i].fileIndicator);
1209
- }
1210
- else if (prm.paramtype == DATA_AT_EXEC_PARAM)
1211
- {
1212
- ret = SQLBindParameter(hSTMT, i + 1, SQL_PARAM_INPUT, prm.c_type, prm.type,
1213
- prm.size, 0, (SQLPOINTER)(intptr_t)(i + 1), 0, &params[i].length);
1214
- }
1215
- else if (prm.arraySize > 0)
1216
- {
1217
- ret = SQLBindParameter(hSTMT, i + 1, prm.paramtype, prm.c_type, prm.type,
1218
- prm.size, prm.decimals, prm.buffer, prm.buffer_length, prm.strLenArray);
1219
- }
1220
- else
1221
- {
1222
- ret = SQLBindParameter(hSTMT, i + 1, prm.paramtype, prm.c_type, prm.type,
1223
- prm.size, prm.decimals, prm.buffer, prm.buffer_length, &params[i].length);
1224
- }
1225
-
1226
- if (ret == SQL_ERROR) break;
1227
- }
1228
- return ret;
1229
- }
1230
-
1231
- SQLRETURN ODBC::PutDataLoop(SQLHSTMT hSTMT, Parameter params[], int count)
1232
- {
1233
- SQLRETURN ret;
1234
- SQLPOINTER token;
1235
-
1236
- DEBUG_PRINTF("ODBC::PutDataLoop - Entry\n");
1237
-
1238
- while ((ret = SQLParamData(hSTMT, &token)) == SQL_NEED_DATA)
1239
- {
1240
- int paramIdx = (int)(intptr_t)token - 1;
1241
-
1242
- if (paramIdx < 0 || paramIdx >= count ||
1243
- params[paramIdx].chunks == NULL || params[paramIdx].chunkCount == 0)
1244
- {
1245
- return SQL_ERROR;
1246
- }
1247
-
1248
- for (int j = 0; j < params[paramIdx].chunkCount; j++)
1249
- {
1250
- ret = SQLPutData(hSTMT, params[paramIdx].chunks[j], params[paramIdx].chunkLens[j]);
1251
- if (!SQL_SUCCEEDED(ret)) return ret;
1252
- }
1253
- }
1254
-
1255
- DEBUG_PRINTF("ODBC::PutDataLoop - Exit, ret=%d\n", ret);
1256
- return ret;
1257
- }
1258
-
1259
- /*
1260
- * CallbackSQLError
1261
- */
1262
- Napi::Value ODBC::CallbackSQLError(Napi::Env env, SQLSMALLINT handleType,
1263
- SQLHANDLE handle, Napi::FunctionReference *cb)
1264
- {
1265
- return CallbackSQLError(env, handleType, handle, (char *)"[node-ibm_db] SQL_ERROR", cb);
1266
- }
1267
-
1268
- Napi::Value ODBC::CallbackSQLError(Napi::Env env, SQLSMALLINT handleType,
1269
- SQLHANDLE handle, char *message,
1270
- Napi::FunctionReference *cb)
1271
- {
1272
- Napi::Value objError = ODBC::GetSQLError(env, handleType, handle, message);
1273
- cb->Call({objError});
1274
- return env.Undefined();
1275
- }
1276
-
1277
- /*
1278
- * GetSQLError
1279
- */
1280
- Napi::Value ODBC::GetSQLError(Napi::Env env, SQLSMALLINT handleType, SQLHANDLE handle)
1281
- {
1282
- return GetSQLError(env, handleType, handle, (char *)"[ibm_db] SQL_ERROR");
1283
- }
1284
-
1285
- Napi::Value ODBC::GetSQLError(Napi::Env env, SQLSMALLINT handleType,
1286
- SQLHANDLE handle, char *message)
1287
- {
1288
- DEBUG_PRINTF("ODBC::GetSQLError : handleType=%i, handle=0x%llx\n", handleType, HandleToLogValue(handle));
1289
-
1290
- SQLINTEGER i = 0;
1291
- SQLINTEGER native;
1292
- SQLSMALLINT len;
1293
- SQLRETURN ret;
1294
- char errorSQLState[14];
1295
- char errorMessage[SQL_MAX_MESSAGE_LENGTH];
1296
- errorMessage[0] = '\0';
1297
- errorSQLState[0] = '\0';
1298
- native = 0;
1299
-
1300
- ret = SQLGetDiagRec(handleType, handle, 1,
1301
- (SQLTCHAR *)errorSQLState, &native,
1302
- (SQLTCHAR *)errorMessage, sizeof(errorMessage), &len);
1303
-
1304
- if (ret == -2)
1305
- {
1306
- strcpy(errorMessage, "CLI0600E Invalid connection handle or connection is closed. SQLSTATE=S1000");
1307
- strcpy(errorSQLState, "S1000");
1308
- native = -1;
1309
- }
1310
-
1311
- // Create a proper Error object so that (err instanceof Error) === true
1312
- #ifdef UNICODE
1313
- Napi::Error error = Napi::Error::New(env, Napi::String::New(env, (char16_t *)errorMessage));
1314
- #else
1315
- Napi::Error error = Napi::Error::New(env, Napi::String::New(env, errorMessage));
1316
- #endif
1317
- Napi::Object objError = error.Value();
1318
- objError.Set("error", Napi::String::New(env, message));
1319
- objError.Set("sqlcode", Napi::Number::New(env, (double)native));
1320
- #ifdef UNICODE
1321
- objError.Set("sqlstate", Napi::String::New(env, (char16_t *)errorSQLState));
1322
- #else
1323
- objError.Set("sqlstate", Napi::String::New(env, errorSQLState));
1324
- #endif
1325
-
1326
- return objError;
1327
- }
1328
-
1329
- /*
1330
- * GetAllRecordsSync
1331
- */
1332
- Napi::Array ODBC::GetAllRecordsSync(Napi::Env env, SQLHENV hENV, SQLHDBC hDBC,
1333
- SQLHSTMT hSTMT, uint16_t *buffer, size_t bufferLength)
1334
- {
1335
- DEBUG_PRINTF("ODBC::GetAllRecordsSync\n");
1336
-
1337
- int count = 0;
1338
- short colCount = 0;
1339
- Column *columns = GetColumns(hSTMT, &colCount);
1340
- Napi::Array rows = Napi::Array::New(env);
1341
-
1342
- while (true)
1343
- {
1344
- SQLRETURN ret = SQLFetch(hSTMT);
1345
-
1346
- if (ret == SQL_ERROR)
1347
- {
1348
- // error - break and return what we have
1349
- break;
1350
- }
1351
- if (ret == SQL_NO_DATA) break;
1352
-
1353
- rows.Set(count, ODBC::GetRecordTuple(env, hSTMT, columns, &colCount, buffer, bufferLength));
1354
- count++;
1355
- }
1356
- ODBC::FreeColumns(columns, &colCount);
1357
- return rows;
1358
- }
1359
-
1360
- #ifdef _AIX
1361
- // On AIX, GCC's _GLOBAL__FD_node calls __cxa_finalize(NULL) during exit()
1362
- // which crashes with SIGILL (jump to 0x0) because libdb2's __cxa_atexit
1363
- // handlers become invalid. We install a SIGILL handler at module load time
1364
- // so it is in place before any exit path runs module finalization.
1365
- // Note: atexit() runs AFTER __modfini64 on AIX, and env cleanup hooks
1366
- // are skipped by process.exit(), so neither approach works alone.
1367
- // Installing at load time covers all exit paths. (#439, #1045)
1368
- static void AIX_SigillHandler(int sig)
1369
- {
1370
- _exit(0);
1371
- }
1372
- #endif
1373
-
1374
- // Module initialization
1375
- Napi::Object InitAll(Napi::Env env, Napi::Object exports)
1376
- {
1377
- ODBC::Init(env, exports);
1378
- ODBCResult::Init(env, exports);
1379
- ODBCConnection::Init(env, exports);
1380
- ODBCStatement::Init(env, exports);
1381
-
1382
- #ifdef _AIX
1383
- // Install SIGILL handler early — must be in place before exit() triggers
1384
- // __modfini64 → _GLOBAL__FD_node → __cxa_finalize(NULL) crash.
1385
- struct sigaction sa;
1386
- memset(&sa, 0, sizeof(sa));
1387
- sa.sa_handler = AIX_SigillHandler;
1388
- sigemptyset(&sa.sa_mask);
1389
- sa.sa_flags = 0;
1390
- sigaction(SIGILL, &sa, NULL);
1391
- #endif
1392
-
1393
- return exports;
1394
- }
1395
-
1396
- NODE_API_MODULE(odbc_bindings, InitAll)
1
+ /*
2
+ Copyright (c) 2013, Dan VerWeire <dverweire@gmail.com>
3
+ Copyright (c) 2010, Lee Smith<notwink@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
+ #ifdef __MVS__
18
+ #define _AE_BIMODAL
19
+ #include <_Nascii.h>
20
+ #endif
21
+
22
+ // On AIX, GCC auto-generates _GLOBAL__FI_*/_GLOBAL__FD_* init/fini functions
23
+ // in every shared library via crtcxa.o. The fini function calls __cxa_finalize
24
+ // which crashes with SIGILL (jump to 0x0) because atexit handlers registered
25
+ // by libdb2.a become invalid during process exit. (#439, #1045)
26
+ // We override the init/fini with this no-op to prevent the crash.
27
+ #ifdef _AIX
28
+ extern "C" void __ibmdb_aix_noop(void) {}
29
+ #endif
30
+
31
+ #include <string.h>
32
+ #include <stdint.h>
33
+ #include <type_traits>
34
+ #include <time.h>
35
+ #include <cassert>
36
+ #include <cstdlib>
37
+ #include <uv.h>
38
+ #ifdef _AIX
39
+ #include <signal.h>
40
+ #include <unistd.h>
41
+ #include <exception>
42
+ #include <cstdio>
43
+ #include <typeinfo>
44
+ #include <cxxabi.h>
45
+ #endif
46
+
47
+ #include "odbc.h"
48
+ #include "odbc_connection.h"
49
+ #include "odbc_result.h"
50
+ #include "odbc_statement.h"
51
+
52
+ #ifdef _WIN32
53
+ #include "strptime.h"
54
+ #endif
55
+
56
+ #define FILE_PARAM 3
57
+ #define DATA_AT_EXEC_PARAM 5
58
+
59
+ namespace {
60
+ template <typename HandleType>
61
+ unsigned long long HandleToLogValueImpl(HandleType handle, std::true_type)
62
+ {
63
+ return static_cast<unsigned long long>(reinterpret_cast<uintptr_t>(handle));
64
+ }
65
+
66
+ template <typename HandleType>
67
+ unsigned long long HandleToLogValueImpl(HandleType handle, std::false_type)
68
+ {
69
+ return static_cast<unsigned long long>(handle);
70
+ }
71
+
72
+ template <typename HandleType>
73
+ unsigned long long HandleToLogValue(HandleType handle)
74
+ {
75
+ return HandleToLogValueImpl(handle, typename std::is_pointer<HandleType>::type());
76
+ }
77
+ }
78
+
79
+ uv_mutex_t ODBC::g_odbcMutex;
80
+ uv_async_t ODBC::g_async;
81
+
82
+ Napi::FunctionReference ODBC::constructor;
83
+
84
+ Napi::Object ODBC::Init(Napi::Env env, Napi::Object exports)
85
+ {
86
+ DEBUG_PRINTF("ODBC::Init\n");
87
+
88
+ Napi::Function func = DefineClass(env, "ODBC", {
89
+ InstanceMethod("createConnection", &ODBC::CreateConnection, NAPI_METHOD_ATTR),
90
+ InstanceMethod("createConnectionSync", &ODBC::CreateConnectionSync, NAPI_METHOD_ATTR),
91
+ StaticValue("SQL_CLOSE", Napi::Number::New(env, SQL_CLOSE), napi_enumerable),
92
+ StaticValue("SQL_DROP", Napi::Number::New(env, SQL_DROP), napi_enumerable),
93
+ StaticValue("SQL_UNBIND", Napi::Number::New(env, SQL_UNBIND), napi_enumerable),
94
+ StaticValue("SQL_RESET_PARAMS", Napi::Number::New(env, SQL_RESET_PARAMS), napi_enumerable),
95
+ StaticValue("SQL_DESTROY", Napi::Number::New(env, SQL_DESTROY), napi_enumerable),
96
+ StaticValue("FETCH_ARRAY", Napi::Number::New(env, FETCH_ARRAY), napi_enumerable),
97
+ StaticValue("FETCH_OBJECT", Napi::Number::New(env, FETCH_OBJECT), napi_enumerable),
98
+ });
99
+
100
+ constructor = Napi::Persistent(func);
101
+ constructor.SuppressDestruct();
102
+ exports.Set("ODBC", func);
103
+
104
+ // Initialize the cross platform mutex provided by libuv
105
+ uv_mutex_init(&ODBC::g_odbcMutex);
106
+
107
+ return exports;
108
+ }
109
+
110
+ ODBC::~ODBC()
111
+ {
112
+ DEBUG_PRINTF("ODBC::~ODBC\n");
113
+ this->Free();
114
+ }
115
+
116
+ void ODBC::Free()
117
+ {
118
+ DEBUG_PRINTF("ODBC::Free: m_hEnv = 0x%llx\n", HandleToLogValue(m_hEnv));
119
+ if (m_hEnv)
120
+ {
121
+ SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv);
122
+ m_hEnv = (SQLHENV)NULL;
123
+ }
124
+ }
125
+
126
+ ODBC::ODBC(const Napi::CallbackInfo &info) : Napi::ObjectWrap<ODBC>(info)
127
+ {
128
+ Napi::Env env = info.Env();
129
+ DEBUG_PRINTF("ODBC::New - Entry\n");
130
+
131
+ m_hEnv = (SQLHENV)NULL;
132
+
133
+ #ifdef __MVS__
134
+ int ori_mode = __ae_thread_swapmode(__AE_EBCDIC_MODE);
135
+ #endif
136
+ int ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_hEnv);
137
+ #ifdef __MVS__
138
+ __ae_thread_swapmode(ori_mode);
139
+ #endif
140
+ if (!SQL_SUCCEEDED(ret))
141
+ {
142
+ DEBUG_PRINTF("ODBC::New - ERROR ALLOCATING ENV HANDLE!!\n");
143
+ Napi::Value objError = ODBC::GetSQLError(env, SQL_HANDLE_ENV, m_hEnv);
144
+ napi_throw(env, objError);
145
+ return;
146
+ }
147
+
148
+ ret = SQLSetEnvAttr(m_hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_UINTEGER);
149
+
150
+ if (!SQL_SUCCEEDED(ret))
151
+ {
152
+ DEBUG_PRINTF("ODBC::New - ERROR SETTING ODBC VERSION!!\n");
153
+ Napi::Value objError = ODBC::GetSQLError(env, SQL_HANDLE_ENV, m_hEnv);
154
+ SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv);
155
+ m_hEnv = (SQLHENV)NULL;
156
+ napi_throw(env, objError);
157
+ return;
158
+ }
159
+
160
+ DEBUG_PRINTF("ODBC::New - Exit\n");
161
+ }
162
+
163
+ /*
164
+ * CreateConnection
165
+ */
166
+ Napi::Value ODBC::CreateConnection(const Napi::CallbackInfo &info)
167
+ {
168
+ DEBUG_PRINTF("ODBC::CreateConnection - Entry\n");
169
+ Napi::Env env = info.Env();
170
+
171
+ REQ_FUN_ARG(0, cb);
172
+
173
+ uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
174
+ MEMCHECK(work_req);
175
+
176
+ create_connection_work_data *data =
177
+ (create_connection_work_data *)(calloc(1, sizeof(create_connection_work_data)));
178
+ if (!data) free(work_req);
179
+ MEMCHECK(data);
180
+
181
+ data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
182
+ data->dbo = this;
183
+ data->env = env;
184
+
185
+ work_req->data = data;
186
+
187
+ uv_queue_work(uv_default_loop(), work_req, UV_CreateConnection, (uv_after_work_cb)UV_AfterCreateConnection);
188
+
189
+ this->Ref();
190
+
191
+ DEBUG_PRINTF("ODBC::CreateConnection - Exit\n");
192
+ return env.Undefined();
193
+ }
194
+
195
+ void ODBC::UV_CreateConnection(uv_work_t *req)
196
+ {
197
+ DEBUG_PRINTF("ODBC::UV_CreateConnection - Entry\n");
198
+ create_connection_work_data *data = (create_connection_work_data *)(req->data);
199
+ data->result = SQLAllocHandle(SQL_HANDLE_DBC, data->dbo->m_hEnv, &data->hDBC);
200
+ DEBUG_PRINTF("ODBC::UV_CreateConnection - Exit: hDBC = 0x%llx\n", HandleToLogValue(data->hDBC));
201
+ }
202
+
203
+ void ODBC::UV_AfterCreateConnection(uv_work_t *req, int status)
204
+ {
205
+ DEBUG_PRINTF("ODBC::UV_AfterCreateConnection - Entry\n");
206
+ create_connection_work_data *data = (create_connection_work_data *)(req->data);
207
+ Napi::Env env(data->env);
208
+ Napi::HandleScope scope(env);
209
+
210
+ if (!SQL_SUCCEEDED(data->result))
211
+ {
212
+ Napi::Value error = ODBC::GetSQLError(env, SQL_HANDLE_ENV, data->dbo->m_hEnv);
213
+ data->cb->Call({error});
214
+ }
215
+ else
216
+ {
217
+ Napi::Value args[2] = {
218
+ Napi::External<void>::New(env, (void *)(intptr_t)data->dbo->m_hEnv),
219
+ Napi::External<void>::New(env, (void *)(intptr_t)data->hDBC)
220
+ };
221
+ Napi::Object js_result = ODBCConnection::constructor.New({args[0], args[1]});
222
+ data->cb->Call({env.Null(), js_result});
223
+ }
224
+ PropagateCallbackException(env);
225
+
226
+ data->dbo->Unref();
227
+ delete data->cb;
228
+ free(data);
229
+ free(req);
230
+ DEBUG_PRINTF("ODBC::UV_AfterCreateConnection - Exit\n");
231
+ }
232
+
233
+ /*
234
+ * CreateConnectionSync
235
+ */
236
+ Napi::Value ODBC::CreateConnectionSync(const Napi::CallbackInfo &info)
237
+ {
238
+ DEBUG_PRINTF("ODBC::CreateConnectionSync - Entry\n");
239
+ Napi::Env env = info.Env();
240
+
241
+ SQLHDBC hDBC;
242
+ SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_DBC, m_hEnv, &hDBC);
243
+
244
+ if (!SQL_SUCCEEDED(ret))
245
+ {
246
+ // TODO: do something!
247
+ }
248
+
249
+ Napi::Value params[2] = {
250
+ Napi::External<void>::New(env, (void *)(intptr_t)m_hEnv),
251
+ Napi::External<void>::New(env, (void *)(intptr_t)hDBC)
252
+ };
253
+ Napi::Object js_result = ODBCConnection::constructor.New({params[0], params[1]});
254
+
255
+ DEBUG_PRINTF("ODBC::CreateConnectionSync - Exit: hDBC = 0x%llx\n", HandleToLogValue(hDBC));
256
+ return js_result;
257
+ }
258
+
259
+ /*
260
+ * GetColumns
261
+ */
262
+ Column *ODBC::GetColumns(SQLHSTMT hStmt, short *colCount)
263
+ {
264
+ SQLRETURN ret;
265
+ SQLSMALLINT buflen;
266
+ SQLSMALLINT typebuflen;
267
+ DEBUG_PRINTF("ODBC::GetColumns - Entry\n");
268
+
269
+ *colCount = 0;
270
+ ret = SQLNumResultCols(hStmt, colCount);
271
+
272
+ if (!SQL_SUCCEEDED(ret))
273
+ {
274
+ return new Column[0];
275
+ }
276
+
277
+ Column *columns = new Column[*colCount];
278
+ char colname[MAX_FIELD_SIZE];
279
+
280
+ for (int i = 0; i < *colCount; i++)
281
+ {
282
+ columns[i].index = i + 1;
283
+ columns[i].getData = false;
284
+ columns[i].isFileBound = false;
285
+ colname[0] = '\0';
286
+ buflen = 0;
287
+
288
+ ret = SQLColAttribute(hStmt, columns[i].index,
289
+ #ifdef STRICT_COLUMN_NAMES
290
+ SQL_DESC_NAME,
291
+ #else
292
+ SQL_DESC_LABEL,
293
+ #endif
294
+ colname, (SQLSMALLINT)MAX_FIELD_SIZE, &buflen, NULL);
295
+
296
+ columns[i].name_len = buflen;
297
+ if (buflen > 0)
298
+ {
299
+ columns[i].name = new unsigned char[buflen + 2];
300
+ memcpy(columns[i].name, colname, buflen);
301
+ columns[i].name[buflen] = '\0';
302
+ columns[i].name[buflen + 1] = '\0';
303
+ }
304
+ DEBUG_PRINTF("ODBC::GetColumns index = %i, buflen=%i\n", columns[i].index, buflen);
305
+
306
+ ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_DISPLAY_SIZE,
307
+ NULL, 0, NULL, &columns[i].max_display_len);
308
+ ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_PRECISION,
309
+ NULL, 0, NULL, &columns[i].precision);
310
+ ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_SCALE,
311
+ NULL, 0, NULL, &columns[i].scale);
312
+ ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_LENGTH,
313
+ NULL, 0, NULL, &columns[i].field_len);
314
+ ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_CONCISE_TYPE,
315
+ NULL, 0, NULL, &columns[i].type);
316
+
317
+ columns[i].type_name = new unsigned char[(MAX_FIELD_SIZE)];
318
+ columns[i].type_name[0] = '\0';
319
+
320
+ ret = SQLColAttribute(hStmt, columns[i].index, SQL_DESC_TYPE_NAME,
321
+ columns[i].type_name, (SQLSMALLINT)(MAX_FIELD_SIZE),
322
+ &typebuflen, NULL);
323
+ }
324
+ DEBUG_PRINTF("ODBC::GetColumns - Exit\n");
325
+ return columns;
326
+ }
327
+
328
+ /*
329
+ * FreeColumns
330
+ */
331
+ void ODBC::FreeColumns(Column *&columns, short *colCount)
332
+ {
333
+ DEBUG_PRINTF("ODBC::FreeColumns - Entry\n");
334
+ for (int i = 0; i < *colCount; i++)
335
+ {
336
+ delete[] columns[i].name;
337
+ delete[] columns[i].type_name;
338
+ }
339
+ if (columns != NULL)
340
+ {
341
+ delete[] columns;
342
+ columns = NULL;
343
+ }
344
+ *colCount = 0;
345
+ DEBUG_PRINTF("ODBC::FreeColumns - Exit\n");
346
+ }
347
+
348
+ /*
349
+ * GetColumnValue
350
+ */
351
+ Napi::Value ODBC::GetColumnValue(Napi::Env env, SQLHSTMT hStmt, Column column,
352
+ uint16_t *buffer, size_t bufferLength)
353
+ {
354
+ if (column.isFileBound) {
355
+ return env.Null();
356
+ }
357
+
358
+ SQLLEN len = 0;
359
+ SQLLEN maxBuffSize = 0;
360
+ unsigned short terCharLen = 1;
361
+ SQLRETURN ret = SQL_SUCCESS;
362
+ SQLSMALLINT ctype = SQL_C_TCHAR;
363
+ char *errmsg = (char *)"[node-ibm_db] Error in ODBC::GetColumnValue";
364
+
365
+ #ifdef UNICODE
366
+ terCharLen = 2;
367
+ #endif
368
+
369
+ DEBUG_PRINTF("Column Type : %i\t%i\t%i\t%i\n", column.type, SQL_DATETIME,
370
+ SQL_TIMESTAMP, SQL_TYPE_TIME);
371
+
372
+ switch (column.type)
373
+ {
374
+ case SQL_BLOB:
375
+ case SQL_BINARY:
376
+ case SQL_VARBINARY:
377
+ case SQL_LONGVARBINARY:
378
+ terCharLen = 0;
379
+ ctype = SQL_C_BINARY;
380
+ break;
381
+ }
382
+
383
+ switch (column.type)
384
+ {
385
+ case SQL_INTEGER:
386
+ case SQL_SMALLINT:
387
+ case SQL_TINYINT:
388
+ {
389
+ SQLINTEGER value;
390
+ ret = SQLGetData(hStmt, column.index, SQL_C_SLONG, &value, sizeof(value), &len);
391
+
392
+ if (len == SQL_NULL_DATA) return env.Null();
393
+ return Napi::Number::New(env, (double)value);
394
+ }
395
+ break;
396
+
397
+ case SQL_NUMERIC:
398
+ case SQL_DECIMAL:
399
+ case SQL_DECFLOAT:
400
+ case SQL_FLOAT:
401
+ case SQL_REAL:
402
+ case SQL_DOUBLE:
403
+ {
404
+ double value;
405
+ ret = SQLGetData(hStmt, column.index, SQL_C_DOUBLE, &value, sizeof(value), &len);
406
+
407
+ if (len == SQL_NULL_DATA) return env.Null();
408
+ return Napi::Number::New(env, value);
409
+ }
410
+ break;
411
+
412
+ case SQL_DATETIME:
413
+ case SQL_TIMESTAMP:
414
+ {
415
+ SQL_TIMESTAMP_STRUCT odbcTime;
416
+ #ifdef _WIN32
417
+ struct tm timeInfo = {};
418
+ ret = SQLGetData(hStmt, column.index, SQL_C_CHAR, &odbcTime, sizeof(odbcTime), &len);
419
+ #else
420
+ #if defined(_AIX) || defined(__MVS__)
421
+ struct tm timeInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0};
422
+ #else
423
+ struct tm timeInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
424
+ #endif
425
+ ret = SQLGetData(hStmt, column.index, SQL_C_TYPE_TIMESTAMP, &odbcTime, sizeof(odbcTime), &len);
426
+ #endif
427
+
428
+ if (len == SQL_NULL_DATA) return env.Null();
429
+
430
+ timeInfo.tm_year = odbcTime.year - 1900;
431
+ timeInfo.tm_mon = odbcTime.month - 1;
432
+ timeInfo.tm_mday = odbcTime.day;
433
+ timeInfo.tm_hour = odbcTime.hour;
434
+ timeInfo.tm_min = odbcTime.minute;
435
+ timeInfo.tm_sec = odbcTime.second;
436
+ timeInfo.tm_isdst = -1;
437
+
438
+ #ifdef TIMEGM
439
+ return Napi::Date::New(env, (double(timegm(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000));
440
+ #else
441
+ return Napi::Date::New(env, (double(mktime(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000));
442
+ #endif
443
+ }
444
+ break;
445
+
446
+ case SQL_BIT:
447
+ {
448
+ char bit[4] = {'\0'};
449
+ ret = SQLGetData(hStmt, column.index, SQL_C_CHAR, &bit, 4, &len);
450
+ if (len == SQL_NULL_DATA) return env.Null();
451
+ return Napi::Boolean::New(env, (*bit == '0') ? false : true);
452
+ }
453
+ break;
454
+
455
+ case SQL_TYPE_TIME:
456
+ case SQL_BIGINT:
457
+ if (column.type == SQL_BIGINT) ctype = SQL_C_CHAR;
458
+ /* fall through */
459
+ case SQL_DBCLOB:
460
+ if (column.type == SQL_DBCLOB) ctype = SQL_C_CHAR;
461
+ /* fall through */
462
+ default:
463
+ {
464
+ uint16_t *tmp_out_ptr = NULL;
465
+ uint32_t bufferSize = 0;
466
+ uint32_t newbufflen = 0;
467
+ unsigned short secondGetData = 0;
468
+ len = 0;
469
+ bufferSize = bufferLength + terCharLen;
470
+
471
+ if (buffer == NULL)
472
+ {
473
+ buffer = (uint16_t *)malloc(bufferSize);
474
+ if (buffer == NULL)
475
+ {
476
+ errmsg = (char *)"Failed to allocate memory buffer for column data.";
477
+ napi_throw(env, ODBC::GetSQLError(env, SQL_HANDLE_STMT, hStmt, errmsg));
478
+ return env.Undefined();
479
+ }
480
+ }
481
+ maxBuffSize = bufferSize;
482
+ memset(buffer, '\0', bufferSize);
483
+ ret = SQLGetData(hStmt, column.index, ctype, (char *)buffer, maxBuffSize, &len);
484
+
485
+ if (column.getData) { secondGetData = 1; }
486
+ else if (ret == SQL_SUCCESS_WITH_INFO && len > 0)
487
+ {
488
+ if (len + terCharLen > 0x3fffffff)
489
+ newbufflen = 0x3fffffff;
490
+ else
491
+ newbufflen = len + terCharLen;
492
+
493
+ tmp_out_ptr = (uint16_t *)malloc(newbufflen);
494
+ if (tmp_out_ptr == NULL)
495
+ {
496
+ ret = (SQLRETURN)-3;
497
+ newbufflen = bufferSize;
498
+ }
499
+ else
500
+ {
501
+ memcpy(tmp_out_ptr, (char *)buffer, bufferLength);
502
+ free((uint16_t *)buffer);
503
+ buffer = tmp_out_ptr;
504
+ maxBuffSize = newbufflen - bufferLength;
505
+ len = 0;
506
+ ret = SQLGetData(hStmt, column.index, ctype, (char *)buffer + bufferLength, maxBuffSize, &len);
507
+ ret = SQL_SUCCESS;
508
+ len = newbufflen;
509
+ secondGetData = 1;
510
+ }
511
+ }
512
+ if (len >= 0) newbufflen = len;
513
+
514
+ if (len == SQL_NULL_DATA)
515
+ {
516
+ FREE(buffer);
517
+ return env.Null();
518
+ }
519
+ else if (SQL_SUCCEEDED(ret) || secondGetData)
520
+ {
521
+ if (ctype == SQL_C_BINARY)
522
+ {
523
+ // Return binary data as buffer - transfer ownership to Node
524
+ Napi::Buffer<char> buf = Napi::Buffer<char>::Copy(env, (char *)buffer, newbufflen);
525
+ FREE(buffer);
526
+ return buf;
527
+ }
528
+ else
529
+ {
530
+ #ifdef UNICODE
531
+ Napi::Value str = Napi::String::New(env, (char16_t *)buffer);
532
+ #else
533
+ Napi::Value str = Napi::String::New(env, (char *)buffer);
534
+ #endif
535
+ FREE(buffer);
536
+ return str;
537
+ }
538
+ }
539
+ else
540
+ {
541
+ FREE(buffer);
542
+ if (ret == SQL_INVALID_HANDLE)
543
+ {
544
+ unsigned long long hstmt_value = HandleToLogValue(hStmt);
545
+ #ifdef __MVS__
546
+ __fprintf_a(stdout, "Invalid Handle: SQLGetData return code = %i, stmt handle = 0x%llx"
547
+ ", columnType = %i, index = %i\n",
548
+ ret, hstmt_value, (int)column.type, column.index);
549
+ #else
550
+ fprintf(stdout, "Invalid Handle: SQLGetData return code = %i, stmt handle = 0x%llx"
551
+ ", columnType = %i, index = %i\n",
552
+ ret, hstmt_value, (int)column.type, column.index);
553
+ #endif
554
+ assert(ret != SQL_INVALID_HANDLE);
555
+ }
556
+ napi_throw(env, ODBC::GetSQLError(env, SQL_HANDLE_STMT, hStmt, errmsg));
557
+ return env.Undefined();
558
+ }
559
+ }
560
+ }
561
+ }
562
+
563
+ /*
564
+ * GetOutputParameter
565
+ */
566
+ Napi::Value ODBC::GetOutputParameter(Napi::Env env, Parameter *prm)
567
+ {
568
+ bool sqlTypeBinary = false;
569
+
570
+ switch (prm->type)
571
+ {
572
+ case SQL_BLOB:
573
+ case SQL_BINARY:
574
+ case SQL_VARBINARY:
575
+ case SQL_LONGVARBINARY:
576
+ sqlTypeBinary = true;
577
+ break;
578
+ }
579
+
580
+ if (((int)prm->length == SQL_NULL_DATA) || !prm->buffer)
581
+ {
582
+ return env.Null();
583
+ }
584
+
585
+ switch (prm->type)
586
+ {
587
+ case SQL_INTEGER:
588
+ case SQL_SMALLINT:
589
+ case SQL_TINYINT:
590
+ case SQL_BOOLEAN:
591
+ case SQL_NUMERIC:
592
+ case SQL_BIGINT:
593
+ if (prm->length == sizeof(int))
594
+ return Napi::Number::New(env, *(int *)prm->buffer);
595
+ else if (prm->length == sizeof(short))
596
+ return Napi::Number::New(env, *(short *)prm->buffer);
597
+ else if (prm->length == sizeof(long))
598
+ return Napi::Number::New(env, (double)*(long *)prm->buffer);
599
+ else if (prm->length == sizeof(long long))
600
+ return Napi::Number::New(env, (double)*(long long *)prm->buffer);
601
+ else
602
+ return Napi::Number::New(env, *(char *)prm->buffer);
603
+ break;
604
+
605
+ case SQL_DECIMAL:
606
+ case SQL_FLOAT:
607
+ case SQL_DECFLOAT:
608
+ case SQL_REAL:
609
+ case SQL_DOUBLE:
610
+ return Napi::Number::New(env, *(double *)prm->buffer);
611
+ break;
612
+
613
+ case SQL_BIT:
614
+ return Napi::Boolean::New(env, (*((char *)prm->buffer) == '0') ? false : true);
615
+ break;
616
+
617
+ default:
618
+ if (prm->isBuffer)
619
+ {
620
+ return Napi::Buffer<char>::Copy(env, (char *)prm->buffer, prm->length);
621
+ }
622
+ else if (sqlTypeBinary || prm->c_type == SQL_C_BINARY)
623
+ {
624
+ prm->isBuffer = true;
625
+ return Napi::Buffer<char>::Copy(env, (char *)prm->buffer, prm->length);
626
+ }
627
+ else
628
+ {
629
+ #ifdef UNICODE
630
+ return Napi::String::New(env, (char16_t *)prm->buffer);
631
+ #else
632
+ return Napi::String::New(env, (char *)prm->buffer);
633
+ #endif
634
+ }
635
+ }
636
+ }
637
+
638
+ /*
639
+ * GetRecordTuple
640
+ */
641
+ Napi::Object ODBC::GetRecordTuple(Napi::Env env, SQLHSTMT hStmt, Column *columns,
642
+ short *colCount, uint16_t *buffer, size_t bufferLength)
643
+ {
644
+ Napi::Object tuple = Napi::Object::New(env);
645
+
646
+ for (int i = 0; i < *colCount; i++)
647
+ {
648
+ #ifdef UNICODE
649
+ tuple.Set(Napi::String::New(env, (char16_t *)columns[i].name),
650
+ GetColumnValue(env, hStmt, columns[i], buffer, bufferLength));
651
+ #else
652
+ tuple.Set(Napi::String::New(env, (const char *)columns[i].name),
653
+ GetColumnValue(env, hStmt, columns[i], buffer, bufferLength));
654
+ #endif
655
+ }
656
+
657
+ return tuple;
658
+ }
659
+
660
+ /*
661
+ * GetRecordArray
662
+ */
663
+ Napi::Value ODBC::GetRecordArray(Napi::Env env, SQLHSTMT hStmt, Column *columns,
664
+ short *colCount, uint16_t *buffer, size_t bufferLength)
665
+ {
666
+ Napi::Array array = Napi::Array::New(env);
667
+
668
+ for (int i = 0; i < *colCount; i++)
669
+ {
670
+ array.Set(i, GetColumnValue(env, hStmt, columns[i], buffer, bufferLength));
671
+ }
672
+
673
+ return array;
674
+ }
675
+
676
+ /*
677
+ * GetParametersFromArray
678
+ */
679
+ Parameter *ODBC::GetParametersFromArray(Napi::Env env, Napi::Array values, int *paramCount)
680
+ {
681
+ DEBUG_PRINTF("ODBC::GetParametersFromArray\n");
682
+ *paramCount = values.Length();
683
+
684
+ Parameter *params = (Parameter *)malloc(*paramCount * sizeof(Parameter));
685
+ if (*paramCount != 0 && !params)
686
+ {
687
+ Napi::Error::New(env, "Could not allocate enough memory for params").ThrowAsJavaScriptException();
688
+ return params;
689
+ }
690
+ memset(params, '\0', *paramCount * sizeof(Parameter));
691
+
692
+ for (int i = 0; i < *paramCount; i++)
693
+ {
694
+ Napi::Value value = values.Get(i);
695
+ params[i].paramtype = SQL_PARAM_INPUT;
696
+ params[i].size = 0;
697
+ params[i].length = SQL_NULL_DATA;
698
+ params[i].buffer_length = 0;
699
+ params[i].decimals = 0;
700
+ params[i].isBuffer = false;
701
+
702
+ if (value.IsArray())
703
+ {
704
+ Napi::Array paramArray = value.As<Napi::Array>();
705
+ int arrlen = paramArray.Length();
706
+ if (arrlen < 4)
707
+ {
708
+ Napi::Error::New(env, "Wrong param format!").ThrowAsJavaScriptException();
709
+ return params;
710
+ }
711
+
712
+ Napi::Value val = paramArray.Get((uint32_t)0);
713
+ if (val.IsNumber()) params[i].paramtype = val.As<Napi::Number>().Int32Value();
714
+
715
+ val = paramArray.Get((uint32_t)1);
716
+ if (val.IsNumber()) params[i].c_type = val.As<Napi::Number>().Int32Value();
717
+ else params[i].c_type = SQL_C_CHAR;
718
+
719
+ val = paramArray.Get((uint32_t)2);
720
+ if (val.IsNumber()) params[i].type = val.As<Napi::Number>().Int32Value();
721
+ else params[i].type = SQL_CHAR;
722
+
723
+ if (arrlen == 5)
724
+ {
725
+ val = paramArray.Get((uint32_t)4);
726
+ if (val.IsNumber()) params[i].buffer_length = val.As<Napi::Number>().Int32Value();
727
+ }
728
+
729
+ val = paramArray.Get((uint32_t)3);
730
+ if (params[i].paramtype == DATA_AT_EXEC_PARAM && val.IsArray())
731
+ {
732
+ GetDataAtExecParam(env, val, &params[i], i + 1);
733
+ }
734
+ else if (val.IsNull() || val.IsUndefined())
735
+ {
736
+ GetNullParam(&params[i], i + 1);
737
+ }
738
+ else if (val.IsNumber() && !val.IsNull() && params[i].type != 3)
739
+ {
740
+ // Check if integer
741
+ double dval = val.As<Napi::Number>().DoubleValue();
742
+ int32_t ival = val.As<Napi::Number>().Int32Value();
743
+ if (dval == (double)ival && params[i].type != 3)
744
+ GetInt32Param(env, val, &params[i], i + 1);
745
+ else
746
+ GetNumberParam(env, val, &params[i], i + 1);
747
+ }
748
+ else if (val.IsBoolean())
749
+ {
750
+ GetBoolParam(env, val, &params[i], i + 1);
751
+ }
752
+ else if (val.IsArray())
753
+ {
754
+ GetArrayParam(env, val, &params[i], i + 1);
755
+ }
756
+ else if (val.IsBuffer())
757
+ {
758
+ GetBufferParam(val, &params[i], i + 1);
759
+ }
760
+ else
761
+ {
762
+ GetStringParam(env, val, &params[i], i + 1);
763
+ }
764
+ }
765
+ else if (value.IsBuffer())
766
+ {
767
+ GetBufferParam(value, &params[i], i + 1);
768
+ }
769
+ else if (value.IsString())
770
+ {
771
+ GetStringParam(env, value, &params[i], i + 1);
772
+ }
773
+ else if (value.IsNull() || value.IsUndefined())
774
+ {
775
+ GetNullParam(&params[i], i + 1);
776
+ }
777
+ else if (value.IsNumber())
778
+ {
779
+ double dval = value.As<Napi::Number>().DoubleValue();
780
+ int32_t ival = value.As<Napi::Number>().Int32Value();
781
+ if (dval == (double)ival)
782
+ GetInt32Param(env, value, &params[i], i + 1);
783
+ else
784
+ GetNumberParam(env, value, &params[i], i + 1);
785
+ }
786
+ else if (value.IsBoolean())
787
+ {
788
+ GetBoolParam(env, value, &params[i], i + 1);
789
+ }
790
+
791
+ if (env.IsExceptionPending())
792
+ {
793
+ return params;
794
+ }
795
+ }
796
+ return params;
797
+ }
798
+
799
+ void ODBC::GetStringParam(Napi::Env env, Napi::Value value, Parameter *param, int num)
800
+ {
801
+ int length = 0;
802
+ int bufflen = 0;
803
+ int terCharLen = 1;
804
+ bool isBinary = (param->c_type == SQL_C_BINARY);
805
+
806
+ Napi::String stringValue = value.ToString();
807
+ if (stringValue.IsEmpty())
808
+ {
809
+ return;
810
+ }
811
+
812
+ // Determine string length using encoding that matches NAN behavior:
813
+ // SQL_C_BINARY -> Latin-1 (WriteOneByte), UNICODE -> UTF-16 (Write), else -> UTF-8
814
+ if (isBinary) {
815
+ size_t latin1_len = 0;
816
+ napi_get_value_string_latin1(env, value, NULL, 0, &latin1_len);
817
+ length = (int)latin1_len;
818
+ }
819
+ #ifdef UNICODE
820
+ else {
821
+ size_t utf16_len = 0;
822
+ napi_get_value_string_utf16(env, value, NULL, 0, &utf16_len);
823
+ length = (int)utf16_len;
824
+ }
825
+ #else
826
+ else {
827
+ std::string utf8str = stringValue.Utf8Value();
828
+ length = (int)utf8str.length();
829
+ }
830
+ #endif
831
+
832
+ bufflen = length;
833
+ param->length = length;
834
+ if (!param->c_type || (param->c_type == SQL_CHAR))
835
+ param->c_type = SQL_C_TCHAR;
836
+ #ifdef UNICODE
837
+ param->length = SQL_NTS;
838
+ if (!param->type || (param->type == SQL_CHAR))
839
+ param->type = (length >= 8000) ? SQL_WLONGVARCHAR : SQL_WVARCHAR;
840
+ if (!isBinary)
841
+ bufflen = (length + 1) * sizeof(uint16_t);
842
+ terCharLen = 2;
843
+ #else
844
+ if (!param->type || (param->type == SQL_CHAR))
845
+ param->type = (length >= 8000) ? SQL_LONGVARCHAR : SQL_VARCHAR;
846
+ if (!isBinary)
847
+ {
848
+ std::string utf8str = stringValue.Utf8Value();
849
+ bufflen = (int)utf8str.length() + 1;
850
+ param->length = bufflen - 1;
851
+ }
852
+ #endif
853
+ if (isBinary) { bufflen = length + 1; }
854
+ if (bufflen <= param->buffer_length &&
855
+ ((param->paramtype % 2 == 0) || (param->arraySize > 0)))
856
+ {
857
+ bufflen = param->buffer_length + terCharLen;
858
+ }
859
+ param->buffer_length = bufflen;
860
+ param->size = bufflen;
861
+
862
+ param->buffer = malloc(param->buffer_length);
863
+ if (!param->buffer) return;
864
+ memset(param->buffer, 0, param->buffer_length);
865
+
866
+ // Copy string data using correct encoding (matching NAN behavior)
867
+ if (param->paramtype == FILE_PARAM) {
868
+ // FILE_PARAM: use UTF-8 (matches NAN's WriteUtf8)
869
+ std::string utf8str = stringValue.Utf8Value();
870
+ memcpy(param->buffer, utf8str.c_str(), utf8str.length());
871
+ } else if (isBinary) {
872
+ // SQL_C_BINARY: use Latin-1 (matches NAN's WriteOneByte)
873
+ size_t copied = 0;
874
+ napi_get_value_string_latin1(env, value, (char *)param->buffer, param->buffer_length, &copied);
875
+ } else {
876
+ #ifdef UNICODE
877
+ // UNICODE text: use UTF-16 (matches NAN's String::Write)
878
+ size_t copied = 0;
879
+ napi_get_value_string_utf16(env, value, (char16_t *)param->buffer, length + 1, &copied);
880
+ #else
881
+ // Non-UNICODE text: use UTF-8 (matches NAN's WriteUtf8)
882
+ std::string utf8str = stringValue.Utf8Value();
883
+ memcpy(param->buffer, utf8str.c_str(), utf8str.length());
884
+ #endif
885
+ }
886
+
887
+ #ifdef UNICODE
888
+ ((char *)param->buffer)[param->buffer_length - 2] = '\0';
889
+ #endif
890
+ ((char *)param->buffer)[param->buffer_length - 1] = '\0';
891
+
892
+ if (param->paramtype == FILE_PARAM)
893
+ {
894
+ param->decimals = param->buffer_length;
895
+ param->fileOption = SQL_FILE_READ;
896
+ param->fileIndicator = 0;
897
+ }
898
+ }
899
+
900
+ void ODBC::GetBufferParam(Napi::Value value, Parameter *param, int num)
901
+ {
902
+ Napi::Buffer<char> buffer = value.As<Napi::Buffer<char>>();
903
+ param->buffer = (char *)buffer.Data();
904
+ param->length = buffer.Length();
905
+ int bufflen = param->length;
906
+ param->isBuffer = true;
907
+
908
+ if (bufflen <= param->buffer_length &&
909
+ ((param->paramtype % 2 == 0) || (param->arraySize > 0)))
910
+ {
911
+ bufflen = param->buffer_length;
912
+ }
913
+ param->buffer_length = bufflen;
914
+ param->size = bufflen;
915
+
916
+ if (!param->c_type || (param->c_type == SQL_CHAR))
917
+ param->c_type = SQL_C_TCHAR;
918
+ #ifdef UNICODE
919
+ param->length = SQL_NTS;
920
+ if (!param->type || (param->type == SQL_CHAR))
921
+ param->type = (param->length >= 8000) ? SQL_WLONGVARCHAR : SQL_WVARCHAR;
922
+ if (param->c_type != SQL_C_BINARY) param->size /= 2;
923
+ #else
924
+ if (!param->type || (param->type == SQL_CHAR))
925
+ param->type = (param->length >= 8000) ? SQL_LONGVARCHAR : SQL_VARCHAR;
926
+ #endif
927
+
928
+ if (param->paramtype == FILE_PARAM)
929
+ {
930
+ param->decimals = param->buffer_length;
931
+ param->fileOption = SQL_FILE_READ;
932
+ param->fileIndicator = 0;
933
+ }
934
+ }
935
+
936
+ void ODBC::GetDataAtExecParam(Napi::Env env, Napi::Value value, Parameter *param, int num)
937
+ {
938
+ Napi::Array chunkArray = value.As<Napi::Array>();
939
+ int numChunks = chunkArray.Length();
940
+
941
+ if (numChunks == 0)
942
+ {
943
+ Napi::Error::New(env, "DATA_AT_EXEC: Data array must not be empty").ThrowAsJavaScriptException();
944
+ return;
945
+ }
946
+
947
+ param->chunks = (void **)calloc(numChunks, sizeof(void *));
948
+ param->chunkLens = (SQLLEN *)calloc(numChunks, sizeof(SQLLEN));
949
+ if (!param->chunks || !param->chunkLens)
950
+ {
951
+ Napi::Error::New(env, "Failed to allocate chunk arrays for DATA_AT_EXEC").ThrowAsJavaScriptException();
952
+ return;
953
+ }
954
+ param->chunkCount = numChunks;
955
+
956
+ SQLLEN totalSize = 0;
957
+ for (int j = 0; j < numChunks; j++)
958
+ {
959
+ Napi::Value chunk = chunkArray.Get(j);
960
+ if (!chunk.IsBuffer())
961
+ {
962
+ Napi::Error::New(env, "DATA_AT_EXEC: each element in Data array must be a Buffer").ThrowAsJavaScriptException();
963
+ return;
964
+ }
965
+ Napi::Buffer<char> buf = chunk.As<Napi::Buffer<char>>();
966
+ param->chunks[j] = buf.Data();
967
+ param->chunkLens[j] = (SQLLEN)buf.Length();
968
+ totalSize += param->chunkLens[j];
969
+ }
970
+
971
+ if (param->buffer_length > 0)
972
+ param->size = param->buffer_length;
973
+ else
974
+ param->size = totalSize;
975
+
976
+ param->buffer = NULL;
977
+ param->buffer_length = 0;
978
+ param->length = SQL_LEN_DATA_AT_EXEC((SQLLEN)param->size);
979
+ param->isBuffer = true;
980
+ }
981
+
982
+ void ODBC::GetNullParam(Parameter *param, int num)
983
+ {
984
+ param->c_type = param->c_type ? param->c_type : SQL_C_DEFAULT;
985
+ param->type = param->type ? param->type : SQL_VARCHAR;
986
+ param->length = SQL_NULL_DATA;
987
+
988
+ if (param->paramtype % 2 == 0)
989
+ {
990
+ if (param->type == SQL_BIGINT || param->type == SQL_INTEGER ||
991
+ param->type == SQL_BOOLEAN || param->type == SQL_SMALLINT ||
992
+ param->type == SQL_TINYINT)
993
+ {
994
+ param->buffer = new int64_t(0);
995
+ }
996
+ else if (param->type == SQL_DECIMAL || param->type == SQL_DOUBLE ||
997
+ param->type == SQL_REAL || param->type == SQL_FLOAT ||
998
+ param->type == SQL_NUMERIC || param->type == SQL_DECFLOAT)
999
+ {
1000
+ param->buffer = new double(0);
1001
+ }
1002
+ else if (param->buffer_length > 0)
1003
+ {
1004
+ param->buffer = malloc(param->buffer_length);
1005
+ }
1006
+ else
1007
+ param->buffer = malloc(8);
1008
+ }
1009
+ }
1010
+
1011
+ void ODBC::GetInt32Param(Napi::Env env, Napi::Value value, Parameter *param, int num)
1012
+ {
1013
+ int64_t *number = new int64_t(value.As<Napi::Number>().Int32Value());
1014
+ param->c_type = SQL_C_SBIGINT;
1015
+ if (!param->type || (param->type == 1)) param->type = SQL_BIGINT;
1016
+ param->buffer = number;
1017
+ param->length = sizeof(number);
1018
+ }
1019
+
1020
+ void ODBC::GetNumberParam(Napi::Env env, Napi::Value value, Parameter *param, int num)
1021
+ {
1022
+ double *number = new double(value.As<Napi::Number>().DoubleValue());
1023
+ std::string string = value.ToString().Utf8Value();
1024
+ int length = (int)string.length();
1025
+ char *ptr = strchr((char *)string.c_str(), '.');
1026
+
1027
+ if (!param->c_type || (param->c_type == SQL_C_CHAR)) param->c_type = SQL_C_DOUBLE;
1028
+ if (!param->type || (param->type == SQL_CHAR)) param->type = SQL_DECIMAL;
1029
+ param->buffer = number;
1030
+ param->buffer_length = sizeof(double);
1031
+ param->length = param->buffer_length;
1032
+ param->size = sizeof(double);
1033
+ if (ptr)
1034
+ {
1035
+ param->decimals = length - (ptr - string.c_str()) - 1;
1036
+ if (param->type == SQL_DECIMAL) param->size = length - 1;
1037
+ }
1038
+ else
1039
+ {
1040
+ param->decimals = 0;
1041
+ if (param->type == SQL_DECIMAL) param->size = length;
1042
+ }
1043
+ }
1044
+
1045
+ void ODBC::GetBoolParam(Napi::Env env, Napi::Value value, Parameter *param, int num)
1046
+ {
1047
+ bool boolean = value.As<Napi::Boolean>().Value();
1048
+ int64_t *number = new int64_t(boolean ? 1 : 0);
1049
+ param->c_type = SQL_C_SBIGINT;
1050
+ if (!param->type || (param->type == 1)) param->type = SQL_BIGINT;
1051
+ param->buffer = number;
1052
+ param->length = sizeof(number);
1053
+ }
1054
+
1055
+ void ODBC::GetArrayParam(Napi::Env env, Napi::Value value, Parameter *param, int num)
1056
+ {
1057
+ Napi::Array valueArray = value.As<Napi::Array>();
1058
+ int arrlen = valueArray.Length();
1059
+
1060
+ param->arraySize = arrlen;
1061
+ if (arrlen <= 0)
1062
+ {
1063
+ param->buffer = new int[0];
1064
+ return;
1065
+ }
1066
+
1067
+ param->strLenArray = new SQLINTEGER[arrlen];
1068
+
1069
+ for (int j = 0; j < arrlen; j++)
1070
+ {
1071
+ Napi::Value val = valueArray.Get(j);
1072
+ if (val.IsNull() || val.IsUndefined())
1073
+ {
1074
+ param->strLenArray[j] = SQL_NULL_DATA;
1075
+ if (j == arrlen - 1)
1076
+ {
1077
+ GetNullParam(param, num);
1078
+ param->buffer = new int[arrlen];
1079
+ }
1080
+ continue;
1081
+ }
1082
+ else if (val.IsNumber())
1083
+ {
1084
+ double dval = val.As<Napi::Number>().DoubleValue();
1085
+ int32_t ival = val.As<Napi::Number>().Int32Value();
1086
+ if (dval == (double)ival && param->type != 3)
1087
+ {
1088
+ int64_t *number = new int64_t[arrlen];
1089
+ for (int i = j; i < arrlen; i++)
1090
+ {
1091
+ if (i != j)
1092
+ {
1093
+ val = valueArray.Get(i);
1094
+ if (val.IsNull() || val.IsUndefined()) { param->strLenArray[i] = SQL_NULL_DATA; continue; }
1095
+ }
1096
+ number[i] = val.As<Napi::Number>().Int64Value();
1097
+ param->strLenArray[i] = sizeof(int64_t);
1098
+ }
1099
+ param->c_type = SQL_C_SBIGINT;
1100
+ if (!param->type || (param->type == 1)) param->type = SQL_BIGINT;
1101
+ param->buffer = number;
1102
+ }
1103
+ else
1104
+ {
1105
+ double *number = new double[arrlen];
1106
+ int decimals = 0;
1107
+ for (int i = j; i < arrlen; i++)
1108
+ {
1109
+ if (i != j)
1110
+ {
1111
+ val = valueArray.Get(i);
1112
+ if (val.IsNull() || val.IsUndefined()) { param->strLenArray[i] = SQL_NULL_DATA; continue; }
1113
+ }
1114
+ GetNumberParam(env, val, param, num);
1115
+ number[i] = *(double *)param->buffer;
1116
+ param->strLenArray[i] = param->length;
1117
+ delete (double *)param->buffer;
1118
+ param->buffer = NULL;
1119
+ decimals = (decimals < param->decimals) ? param->decimals : decimals;
1120
+ }
1121
+ param->decimals = decimals;
1122
+ param->buffer = number;
1123
+ }
1124
+ }
1125
+ else if (val.IsBoolean())
1126
+ {
1127
+ int64_t *boolean = new int64_t[arrlen];
1128
+ for (int i = j; i < arrlen; i++)
1129
+ {
1130
+ if (i != j)
1131
+ {
1132
+ val = valueArray.Get(i);
1133
+ if (val.IsNull() || val.IsUndefined()) { param->strLenArray[i] = SQL_NULL_DATA; continue; }
1134
+ }
1135
+ GetBoolParam(env, val, param, num);
1136
+ boolean[i] = *(int64_t *)(param->buffer);
1137
+ param->strLenArray[i] = (SQLINTEGER)param->length;
1138
+ delete (int64_t *)param->buffer;
1139
+ param->buffer = NULL;
1140
+ }
1141
+ param->buffer = boolean;
1142
+ }
1143
+ else
1144
+ {
1145
+ SQLPOINTER *buff = (SQLPOINTER *)NULL;
1146
+ SQLULEN bufflen = 0;
1147
+ SQLULEN cbValueMax = 0;
1148
+
1149
+ SQLPOINTER *elemBufs = new SQLPOINTER[arrlen]();
1150
+ SQLULEN *elemLens = new SQLULEN[arrlen]();
1151
+ SQLLEN *elemDataLens = new SQLLEN[arrlen]();
1152
+ bool *elemIsBuf = new bool[arrlen]();
1153
+
1154
+ for (int i = j; i < arrlen; i++)
1155
+ {
1156
+ if (i != j)
1157
+ {
1158
+ val = valueArray.Get(i);
1159
+ if (val.IsNull() || val.IsUndefined()) { param->strLenArray[i] = SQL_NULL_DATA; continue; }
1160
+ }
1161
+ if (val.IsBuffer())
1162
+ {
1163
+ GetBufferParam(val, param, num);
1164
+ }
1165
+ else
1166
+ {
1167
+ GetStringParam(env, val, param, num);
1168
+ if (env.IsExceptionPending()) break;
1169
+ #ifdef UNICODE
1170
+ param->buffer_length -= 2;
1171
+ #else
1172
+ param->buffer_length -= 1;
1173
+ #endif
1174
+ }
1175
+ elemBufs[i] = param->buffer;
1176
+ elemLens[i] = param->buffer_length;
1177
+ elemDataLens[i] = param->length;
1178
+ elemIsBuf[i] = param->isBuffer;
1179
+ param->buffer = NULL;
1180
+
1181
+ if ((SQLULEN)param->buffer_length > cbValueMax)
1182
+ cbValueMax = (SQLULEN)param->buffer_length;
1183
+ }
1184
+
1185
+ bufflen = (SQLULEN)arrlen * cbValueMax;
1186
+ buff = (SQLPOINTER *)malloc(bufflen);
1187
+ memset(buff, 0, bufflen);
1188
+
1189
+ for (int i = j; i < arrlen; i++)
1190
+ {
1191
+ if (elemBufs[i] == NULL) continue;
1192
+ SQLULEN copyLen = cbValueMax;
1193
+ if (copyLen > (SQLULEN)elemDataLens[i]) copyLen = (SQLULEN)elemDataLens[i];
1194
+ memcpy((char *)buff + (SQLULEN)i * cbValueMax, elemBufs[i], copyLen);
1195
+ param->strLenArray[i] = (SQLINTEGER)copyLen;
1196
+ if (!elemIsBuf[i]) FREE(elemBufs[i]);
1197
+ }
1198
+
1199
+ delete[] elemBufs;
1200
+ delete[] elemLens;
1201
+ delete[] elemDataLens;
1202
+ delete[] elemIsBuf;
1203
+
1204
+ param->buffer = buff;
1205
+ param->buffer_length = cbValueMax;
1206
+ param->size = cbValueMax;
1207
+ }
1208
+ break;
1209
+ }
1210
+ }
1211
+
1212
+ SQLRETURN ODBC::BindParameters(SQLHSTMT hSTMT, Parameter params[], int count)
1213
+ {
1214
+ SQLRETURN ret = SQL_SUCCESS;
1215
+ Parameter prm;
1216
+
1217
+ for (int i = 0; i < count; i++)
1218
+ {
1219
+ prm = params[i];
1220
+
1221
+ if (prm.paramtype == FILE_PARAM)
1222
+ {
1223
+ ret = SQLBindFileToParam(hSTMT, i + 1, prm.type, (SQLCHAR *)prm.buffer,
1224
+ NULL, &params[i].fileOption, prm.decimals, &params[i].fileIndicator);
1225
+ }
1226
+ else if (prm.paramtype == DATA_AT_EXEC_PARAM)
1227
+ {
1228
+ ret = SQLBindParameter(hSTMT, i + 1, SQL_PARAM_INPUT, prm.c_type, prm.type,
1229
+ prm.size, 0, (SQLPOINTER)(intptr_t)(i + 1), 0, &params[i].length);
1230
+ }
1231
+ else if (prm.arraySize > 0)
1232
+ {
1233
+ ret = SQLBindParameter(hSTMT, i + 1, prm.paramtype, prm.c_type, prm.type,
1234
+ prm.size, prm.decimals, prm.buffer, prm.buffer_length, prm.strLenArray);
1235
+ }
1236
+ else
1237
+ {
1238
+ ret = SQLBindParameter(hSTMT, i + 1, prm.paramtype, prm.c_type, prm.type,
1239
+ prm.size, prm.decimals, prm.buffer, prm.buffer_length, &params[i].length);
1240
+ }
1241
+
1242
+ if (ret == SQL_ERROR) break;
1243
+ }
1244
+ return ret;
1245
+ }
1246
+
1247
+ SQLRETURN ODBC::PutDataLoop(SQLHSTMT hSTMT, Parameter params[], int count)
1248
+ {
1249
+ SQLRETURN ret;
1250
+ SQLPOINTER token;
1251
+
1252
+ DEBUG_PRINTF("ODBC::PutDataLoop - Entry\n");
1253
+
1254
+ while ((ret = SQLParamData(hSTMT, &token)) == SQL_NEED_DATA)
1255
+ {
1256
+ int paramIdx = (int)(intptr_t)token - 1;
1257
+
1258
+ if (paramIdx < 0 || paramIdx >= count ||
1259
+ params[paramIdx].chunks == NULL || params[paramIdx].chunkCount == 0)
1260
+ {
1261
+ return SQL_ERROR;
1262
+ }
1263
+
1264
+ for (int j = 0; j < params[paramIdx].chunkCount; j++)
1265
+ {
1266
+ ret = SQLPutData(hSTMT, params[paramIdx].chunks[j], params[paramIdx].chunkLens[j]);
1267
+ if (!SQL_SUCCEEDED(ret)) return ret;
1268
+ }
1269
+ }
1270
+
1271
+ DEBUG_PRINTF("ODBC::PutDataLoop - Exit, ret=%d\n", ret);
1272
+ return ret;
1273
+ }
1274
+
1275
+ /*
1276
+ * CallbackSQLError
1277
+ */
1278
+ Napi::Value ODBC::CallbackSQLError(Napi::Env env, SQLSMALLINT handleType,
1279
+ SQLHANDLE handle, Napi::FunctionReference *cb)
1280
+ {
1281
+ return CallbackSQLError(env, handleType, handle, (char *)"[node-ibm_db] SQL_ERROR", cb);
1282
+ }
1283
+
1284
+ Napi::Value ODBC::CallbackSQLError(Napi::Env env, SQLSMALLINT handleType,
1285
+ SQLHANDLE handle, char *message,
1286
+ Napi::FunctionReference *cb)
1287
+ {
1288
+ Napi::Value objError = ODBC::GetSQLError(env, handleType, handle, message);
1289
+ cb->Call({objError});
1290
+ return env.Undefined();
1291
+ }
1292
+
1293
+ /*
1294
+ * GetSQLError
1295
+ */
1296
+ Napi::Value ODBC::GetSQLError(Napi::Env env, SQLSMALLINT handleType, SQLHANDLE handle)
1297
+ {
1298
+ return GetSQLError(env, handleType, handle, (char *)"[ibm_db] SQL_ERROR");
1299
+ }
1300
+
1301
+ Napi::Value ODBC::GetSQLError(Napi::Env env, SQLSMALLINT handleType,
1302
+ SQLHANDLE handle, char *message)
1303
+ {
1304
+ DEBUG_PRINTF("ODBC::GetSQLError : handleType=%i, handle=0x%llx\n", handleType, HandleToLogValue(handle));
1305
+
1306
+ SQLINTEGER i = 0;
1307
+ SQLINTEGER native;
1308
+ SQLSMALLINT len;
1309
+ SQLRETURN ret;
1310
+ char errorSQLState[14];
1311
+ char errorMessage[SQL_MAX_MESSAGE_LENGTH];
1312
+ errorMessage[0] = '\0';
1313
+ errorSQLState[0] = '\0';
1314
+ native = 0;
1315
+
1316
+ ret = SQLGetDiagRec(handleType, handle, 1,
1317
+ (SQLTCHAR *)errorSQLState, &native,
1318
+ (SQLTCHAR *)errorMessage, sizeof(errorMessage), &len);
1319
+
1320
+ if (ret == -2)
1321
+ {
1322
+ strcpy(errorMessage, "CLI0600E Invalid connection handle or connection is closed. SQLSTATE=S1000");
1323
+ strcpy(errorSQLState, "S1000");
1324
+ native = -1;
1325
+ }
1326
+
1327
+ // Create a proper Error object so that (err instanceof Error) === true
1328
+ #ifdef UNICODE
1329
+ Napi::Error error = Napi::Error::New(env, Napi::String::New(env, (char16_t *)errorMessage));
1330
+ #else
1331
+ Napi::Error error = Napi::Error::New(env, Napi::String::New(env, errorMessage));
1332
+ #endif
1333
+ Napi::Object objError = error.Value();
1334
+ objError.Set("error", Napi::String::New(env, message));
1335
+ objError.Set("sqlcode", Napi::Number::New(env, (double)native));
1336
+ #ifdef UNICODE
1337
+ objError.Set("sqlstate", Napi::String::New(env, (char16_t *)errorSQLState));
1338
+ #else
1339
+ objError.Set("sqlstate", Napi::String::New(env, errorSQLState));
1340
+ #endif
1341
+
1342
+ return objError;
1343
+ }
1344
+
1345
+ /*
1346
+ * GetAllRecordsSync
1347
+ */
1348
+ Napi::Array ODBC::GetAllRecordsSync(Napi::Env env, SQLHENV hENV, SQLHDBC hDBC,
1349
+ SQLHSTMT hSTMT, uint16_t *buffer, size_t bufferLength)
1350
+ {
1351
+ DEBUG_PRINTF("ODBC::GetAllRecordsSync\n");
1352
+
1353
+ int count = 0;
1354
+ short colCount = 0;
1355
+ Column *columns = GetColumns(hSTMT, &colCount);
1356
+ Napi::Array rows = Napi::Array::New(env);
1357
+
1358
+ while (true)
1359
+ {
1360
+ SQLRETURN ret = SQLFetch(hSTMT);
1361
+
1362
+ if (ret == SQL_ERROR)
1363
+ {
1364
+ // error - break and return what we have
1365
+ break;
1366
+ }
1367
+ if (ret == SQL_NO_DATA) break;
1368
+
1369
+ rows.Set(count, ODBC::GetRecordTuple(env, hSTMT, columns, &colCount, buffer, bufferLength));
1370
+ count++;
1371
+ }
1372
+ ODBC::FreeColumns(columns, &colCount);
1373
+ return rows;
1374
+ }
1375
+
1376
+ #ifdef _AIX
1377
+ // On AIX, GCC's _GLOBAL__FD_node calls __cxa_finalize(NULL) during exit()
1378
+ // which crashes with SIGILL (jump to 0x0) because libdb2's __cxa_atexit
1379
+ // handlers become invalid. We install a SIGILL handler at module load time
1380
+ // so it is in place before any exit path runs module finalization.
1381
+ // Note: atexit() runs AFTER __modfini64 on AIX, and env cleanup hooks
1382
+ // are skipped by process.exit(), so neither approach works alone.
1383
+ // Installing at load time covers all exit paths. (#439, #1045)
1384
+ static void AIX_SigillHandler(int sig)
1385
+ {
1386
+ _exit(0);
1387
+ }
1388
+
1389
+ // GSKit v9.x throws C++ exceptions (gsk09000006::GSKException) from internal
1390
+ // threads during SSL handshake failures. Since those threads have no exception
1391
+ // handler, std::terminate() is called, leading to abort() and coredump.
1392
+ // We install a custom terminate handler to convert this to a graceful exit
1393
+ // with an error message instead of a coredump.
1394
+ static std::terminate_handler g_oldTerminateHandler = nullptr;
1395
+
1396
+ // Helper to demangle C++ type names
1397
+ static const char* DemangleTypeName(const char* mangledName)
1398
+ {
1399
+ int status = 0;
1400
+ char* demangled = abi::__cxa_demangle(mangledName, nullptr, nullptr, &status);
1401
+ if (status == 0 && demangled) {
1402
+ return demangled; // Caller should free this
1403
+ }
1404
+ return mangledName;
1405
+ }
1406
+
1407
+ static void AIX_TerminateHandler()
1408
+ {
1409
+ // Try to get exception info
1410
+ std::exception_ptr eptr = std::current_exception();
1411
+ if (eptr) {
1412
+ try {
1413
+ std::rethrow_exception(eptr);
1414
+ } catch (const std::exception& e) {
1415
+ fprintf(stderr, "[ibm_db] GSKit SSL exception: %s\n", e.what());
1416
+ } catch (...) {
1417
+ // Try to get the exception type using GCC's internal ABI
1418
+ std::type_info* ti = abi::__cxa_current_exception_type();
1419
+ if (ti) {
1420
+ const char* typeName = DemangleTypeName(ti->name());
1421
+ fprintf(stderr, "\n[ibm_db] ERROR: GSKit v9.x SSL connection failed.\n");
1422
+ fprintf(stderr, "Exception type: %s\n\n", typeName);
1423
+ fprintf(stderr, "This is a known incompatibility between GSKit v9.x (compiled with IBM XLC)\n");
1424
+ fprintf(stderr, "and GCC-compiled applications like Node.js on AIX. GSKit v9 throws C++\n");
1425
+ fprintf(stderr, "exceptions from internal threads using XLC's exception handling ABI,\n");
1426
+ fprintf(stderr, "which is incompatible with GCC's libstdc++ runtime.\n\n");
1427
+ fprintf(stderr, "SOLUTION: Use a Db2 CLI driver (clidriver) that includes GSKit v8.x instead\n");
1428
+ fprintf(stderr, "of GSKit v9.x. GSKit v8.x does not have this incompatibility.\n\n");
1429
+ fprintf(stderr, "Db2 client from version 12.1.5.0 onwards comes with GSKit v9.x library.\n");
1430
+ fprintf(stderr, "Use an older Db2 CLI driver (clidriver) to avoid this issue.\n\n");
1431
+ fprintf(stderr, "To check your GSKit version:\n");
1432
+ fprintf(stderr, " ls $IBM_DB_HOME/lib/icc/libgsk*.so\n");
1433
+ fprintf(stderr, " - libgsk8*.so = GSKit v8 (compatible)\n");
1434
+ fprintf(stderr, " - libgsk9*.so = GSKit v9 (incompatible with GCC-compiled Node.js)\n\n");
1435
+ } else {
1436
+ fprintf(stderr, "[ibm_db] GSKit SSL exception (unknown type)\n");
1437
+ }
1438
+ }
1439
+ } else {
1440
+ fprintf(stderr, "[ibm_db] Terminate called without active exception\n");
1441
+ }
1442
+
1443
+ // Exit without coredump - use _exit to avoid running atexit handlers
1444
+ // which could cause additional crashes
1445
+ _exit(1);
1446
+ }
1447
+
1448
+ // SIGABRT handler as a fallback - catches abort() calls from GSKit threads
1449
+ static void AIX_SigabrtHandler(int sig)
1450
+ {
1451
+ fprintf(stderr, "\n[ibm_db] ERROR: SIGABRT received - GSKit v9.x SSL connection failed.\n\n");
1452
+ fprintf(stderr, "This is a known incompatibility between GSKit v9.x and GCC-compiled\n");
1453
+ fprintf(stderr, "Node.js on AIX. Use a clidriver older than v12.1.5.0 instead.\n\n");
1454
+ fprintf(stderr, "Check: ls $IBM_DB_HOME/lib/icc/libgsk*.so\n");
1455
+ fprintf(stderr, " - libgsk8*.so = GSKit v8 (compatible)\n");
1456
+ fprintf(stderr, " - libgsk9*.so = GSKit v9 (incompatible)\n\n");
1457
+ _exit(1);
1458
+ }
1459
+ #endif
1460
+
1461
+ // Module initialization
1462
+ Napi::Object InitAll(Napi::Env env, Napi::Object exports)
1463
+ {
1464
+ ODBC::Init(env, exports);
1465
+ ODBCResult::Init(env, exports);
1466
+ ODBCConnection::Init(env, exports);
1467
+ ODBCStatement::Init(env, exports);
1468
+
1469
+ #ifdef _AIX
1470
+ // Install custom terminate handler to catch GSKit v9.x exceptions thrown
1471
+ // from internal threads. This converts coredumps to graceful error exits.
1472
+ g_oldTerminateHandler = std::set_terminate(AIX_TerminateHandler);
1473
+
1474
+ // Install SIGABRT handler as fallback for abort() calls
1475
+ struct sigaction sa_abrt;
1476
+ memset(&sa_abrt, 0, sizeof(sa_abrt));
1477
+ sa_abrt.sa_handler = AIX_SigabrtHandler;
1478
+ sigemptyset(&sa_abrt.sa_mask);
1479
+ sa_abrt.sa_flags = 0;
1480
+ sigaction(SIGABRT, &sa_abrt, NULL);
1481
+
1482
+ // Install SIGILL handler early — must be in place before exit() triggers
1483
+ // __modfini64 → _GLOBAL__FD_node → __cxa_finalize(NULL) crash.
1484
+ struct sigaction sa;
1485
+ memset(&sa, 0, sizeof(sa));
1486
+ sa.sa_handler = AIX_SigillHandler;
1487
+ sigemptyset(&sa.sa_mask);
1488
+ sa.sa_flags = 0;
1489
+ sigaction(SIGILL, &sa, NULL);
1490
+ #endif
1491
+
1492
+ return exports;
1493
+ }
1494
+
1495
+ NODE_API_MODULE(odbc_bindings, InitAll)