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

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 (282) 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/adm-zip/adm-zip.js +32 -6
  6. package/node_modules/adm-zip/headers/entryHeader.js +13 -1
  7. package/node_modules/adm-zip/methods/inflater.js +33 -3
  8. package/node_modules/adm-zip/package.json +2 -2
  9. package/node_modules/adm-zip/util/errors.js +3 -0
  10. package/node_modules/adm-zip/util/utils.js +102 -14
  11. package/node_modules/adm-zip/zipEntry.js +43 -18
  12. package/node_modules/adm-zip/zipFile.js +10 -0
  13. package/node_modules/axios/CHANGELOG.md +62 -0
  14. package/node_modules/axios/README.md +236 -73
  15. package/node_modules/axios/dist/axios.js +1308 -357
  16. package/node_modules/axios/dist/axios.min.js +3 -3
  17. package/node_modules/axios/dist/axios.min.js.map +1 -1
  18. package/node_modules/axios/dist/browser/axios.cjs +1443 -403
  19. package/node_modules/axios/dist/esm/axios.js +1443 -403
  20. package/node_modules/axios/dist/esm/axios.min.js +2 -2
  21. package/node_modules/axios/dist/esm/axios.min.js.map +1 -1
  22. package/node_modules/axios/dist/node/axios.cjs +1483 -348
  23. package/node_modules/axios/index.d.cts +24 -1
  24. package/node_modules/axios/index.d.ts +22 -1
  25. package/node_modules/axios/lib/adapters/fetch.js +136 -30
  26. package/node_modules/axios/lib/adapters/http.js +277 -74
  27. package/node_modules/axios/lib/adapters/xhr.js +172 -16
  28. package/node_modules/axios/lib/core/Axios.js +87 -27
  29. package/node_modules/axios/lib/core/AxiosError.js +59 -0
  30. package/node_modules/axios/lib/core/AxiosHeaders.js +17 -4
  31. package/node_modules/axios/lib/core/InterceptorManager.js +118 -8
  32. package/node_modules/axios/lib/core/assertParamsAcyclic.js +49 -0
  33. package/node_modules/axios/lib/core/buildFullPath.js +1 -13
  34. package/node_modules/axios/lib/core/createMultipartBody.js +37 -0
  35. package/node_modules/axios/lib/core/dispatchRequest.js +28 -2
  36. package/node_modules/axios/lib/core/mergeConfig.js +183 -120
  37. package/node_modules/axios/lib/core/methodList.js +17 -0
  38. package/node_modules/axios/lib/defaults/index.js +2 -1
  39. package/node_modules/axios/lib/defaults/transitional.js +1 -0
  40. package/node_modules/axios/lib/env/data.js +1 -1
  41. package/node_modules/axios/lib/helpers/AxiosTransformStream.js +11 -2
  42. package/node_modules/axios/lib/helpers/Http2Sessions.js +4 -1
  43. package/node_modules/axios/lib/helpers/HttpStatusCode.js +11 -1
  44. package/node_modules/axios/lib/helpers/composeSignals.js +6 -3
  45. package/node_modules/axios/lib/helpers/cookies.js +30 -6
  46. package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +34 -7
  47. package/node_modules/axios/lib/helpers/formDataToBlob.js +113 -0
  48. package/node_modules/axios/lib/helpers/formDataToJSON.js +25 -1
  49. package/node_modules/axios/lib/helpers/fromDataURI.js +11 -4
  50. package/node_modules/axios/lib/helpers/isURLSameOrigin.js +4 -9
  51. package/node_modules/axios/lib/helpers/normalizeURLForProtocolCheck.js +24 -0
  52. package/node_modules/axios/lib/helpers/parseHeaders.js +2 -1
  53. package/node_modules/axios/lib/helpers/parseProtocol.js +1 -1
  54. package/node_modules/axios/lib/helpers/progressEventReducer.js +1 -1
  55. package/node_modules/axios/lib/helpers/resolveConfig.js +20 -5
  56. package/node_modules/axios/lib/helpers/shouldBypassProxy.js +197 -6
  57. package/node_modules/axios/lib/helpers/throttle.js +3 -2
  58. package/node_modules/axios/lib/helpers/toFormData.js +14 -21
  59. package/node_modules/axios/lib/utils.js +192 -20
  60. package/node_modules/axios/package.json +1 -2
  61. package/node_modules/follow-redirects/README.md +27 -1
  62. package/node_modules/follow-redirects/index.js +11 -6
  63. package/node_modules/follow-redirects/package.json +1 -1
  64. package/node_modules/fs-extra/README.md +0 -1
  65. package/node_modules/fs-extra/lib/empty/index.js +4 -2
  66. package/node_modules/fs-extra/lib/util/stat.js +12 -2
  67. package/node_modules/fs-extra/package.json +1 -1
  68. package/node_modules/ibm_db/APIDocumentation.md +2883 -2024
  69. package/node_modules/ibm_db/LICENSE +23 -23
  70. package/node_modules/ibm_db/README.md +735 -735
  71. package/node_modules/ibm_db/binding.gyp +99 -98
  72. package/node_modules/ibm_db/installer/driverInstall.js +984 -958
  73. package/node_modules/ibm_db/installer/ifx.sh +38 -38
  74. package/node_modules/ibm_db/installer/setenv.sh +23 -23
  75. package/node_modules/ibm_db/installer/testODBCConnection.bat +51 -51
  76. package/node_modules/ibm_db/installer/testODBCConnection.sh +61 -61
  77. package/node_modules/ibm_db/lib/climacros.js +431 -431
  78. package/node_modules/ibm_db/lib/odbc.js +4293 -3487
  79. package/node_modules/ibm_db/lib/simple-queue.js +41 -41
  80. package/node_modules/ibm_db/package.json +70 -70
  81. package/node_modules/ibm_db/src/odbc.cpp +1495 -1396
  82. package/node_modules/ibm_db/src/odbc.h +477 -473
  83. package/node_modules/ibm_db/src/odbc_connection.cpp +1762 -1612
  84. package/node_modules/ibm_db/src/odbc_connection.h +228 -215
  85. package/node_modules/ibm_db/src/odbc_result.cpp +1547 -1547
  86. package/node_modules/ibm_db/src/odbc_result.h +152 -152
  87. package/node_modules/ibm_db/src/odbc_statement.cpp +2047 -861
  88. package/node_modules/ibm_db/src/odbc_statement.h +257 -135
  89. package/node_modules/ibm_db/src/strptime.c +389 -389
  90. package/node_modules/ibm_db/src/strptime.h +46 -46
  91. package/node_modules/ibm_db/test/README +23 -23
  92. package/node_modules/ibm_db/test/bench-insert.js +62 -62
  93. package/node_modules/ibm_db/test/bench-insertBatch10.js +74 -74
  94. package/node_modules/ibm_db/test/bench-insertBatch100.js +75 -75
  95. package/node_modules/ibm_db/test/bench-insertBatch1000.js +75 -75
  96. package/node_modules/ibm_db/test/bench-insertBatch10000.js +75 -75
  97. package/node_modules/ibm_db/test/bench-prepare-bind-execute-closeSync.js +60 -60
  98. package/node_modules/ibm_db/test/bench-prepare-bind-executeNonQuery.js +53 -53
  99. package/node_modules/ibm_db/test/bench-prepare-bindSync-execute-closeSync.js +50 -50
  100. package/node_modules/ibm_db/test/bench-prepare-bindSync-executeNonQuery.js +48 -48
  101. package/node_modules/ibm_db/test/bench-prepare-execute-closeSync.js +51 -51
  102. package/node_modules/ibm_db/test/bench-prepare-executeNonQuery.js +47 -47
  103. package/node_modules/ibm_db/test/bench-prepare-not.js +43 -43
  104. package/node_modules/ibm_db/test/bench-query-fetch.js +59 -59
  105. package/node_modules/ibm_db/test/bench-query-fetchAll.js +49 -49
  106. package/node_modules/ibm_db/test/bench-query-fetchAllSync.js +43 -43
  107. package/node_modules/ibm_db/test/bench-query.js +40 -40
  108. package/node_modules/ibm_db/test/bench-querySync-fetchArray.js +28 -28
  109. package/node_modules/ibm_db/test/bench-querySync.js +29 -29
  110. package/node_modules/ibm_db/test/common.d.ts +10 -10
  111. package/node_modules/ibm_db/test/common.js +127 -127
  112. package/node_modules/ibm_db/test/config.json +7 -7
  113. package/node_modules/ibm_db/test/config.zos.json +4 -4
  114. package/node_modules/ibm_db/test/data/sample1.txt +9 -9
  115. package/node_modules/ibm_db/test/data/trc.fmt +30114 -30114
  116. package/node_modules/ibm_db/test/nodeEE-leak-huge-query.js +152 -152
  117. package/node_modules/ibm_db/test/nodeEE-leak-multiple-query-multiple-connections.js +148 -148
  118. package/node_modules/ibm_db/test/nodeEE-leak-multiple-query-pool-connections.js +154 -154
  119. package/node_modules/ibm_db/test/nodeEE-leak-multiple-query.js +133 -133
  120. package/node_modules/ibm_db/test/nodeEE-stress-async-waterfall-multiple-connections.js +174 -174
  121. package/node_modules/ibm_db/test/nodeEE-stress-async-waterfall.js +169 -169
  122. package/node_modules/ibm_db/test/nodeEE-stress-mixed-query-multiple-connections.js +159 -159
  123. package/node_modules/ibm_db/test/nodeEE-stress-mixed-query.js +148 -148
  124. package/node_modules/ibm_db/test/odbc-bench.c +123 -123
  125. package/node_modules/ibm_db/test/perfTest.js +53 -53
  126. package/node_modules/ibm_db/test/quick-example.sh +30 -30
  127. package/node_modules/ibm_db/test/quick-example.ts +80 -80
  128. package/node_modules/ibm_db/test/run-bench.js +96 -96
  129. package/node_modules/ibm_db/test/run-nodeEE.js +132 -132
  130. package/node_modules/ibm_db/test/run-tests.js +132 -138
  131. package/node_modules/ibm_db/test/sql-cli.js +28 -28
  132. package/node_modules/ibm_db/test/test-all-data-types.js +109 -109
  133. package/node_modules/ibm_db/test/test-array-insert.js +178 -178
  134. package/node_modules/ibm_db/test/test-async-await.js +105 -105
  135. package/node_modules/ibm_db/test/test-attributes.js +34 -34
  136. package/node_modules/ibm_db/test/test-bad-pool-connection-prepare-execute.js +57 -57
  137. package/node_modules/ibm_db/test/test-basic-test.js +138 -138
  138. package/node_modules/ibm_db/test/test-binary-array-insert.js +73 -73
  139. package/node_modules/ibm_db/test/test-blob-from-file.js +90 -90
  140. package/node_modules/ibm_db/test/test-blob-insert.js +144 -144
  141. package/node_modules/ibm_db/test/test-blob-stream-insert.js +321 -299
  142. package/node_modules/ibm_db/test/test-blob-to-file.js +370 -370
  143. package/node_modules/ibm_db/test/test-block-fetch.js +364 -364
  144. package/node_modules/ibm_db/test/test-blocking-issue210.js +123 -123
  145. package/node_modules/ibm_db/test/test-call-async.js +64 -64
  146. package/node_modules/ibm_db/test/test-call-stmt.js +53 -53
  147. package/node_modules/ibm_db/test/test-cancel.js +703 -0
  148. package/node_modules/ibm_db/test/test-chinese-char.js +59 -59
  149. package/node_modules/ibm_db/test/test-connection.js +306 -0
  150. package/node_modules/ibm_db/test/test-date.js +32 -32
  151. package/node_modules/ibm_db/test/test-dbclob.js +93 -93
  152. package/node_modules/ibm_db/test/test-decimals.js +135 -135
  153. package/node_modules/ibm_db/test/test-describe-metadata.js +148 -0
  154. package/node_modules/ibm_db/test/test-executeFile.js +167 -167
  155. package/node_modules/ibm_db/test/test-executeFileSync.js +85 -85
  156. package/node_modules/ibm_db/test/test-fetch-apis.js +146 -146
  157. package/node_modules/ibm_db/test/test-fetchN.js +130 -130
  158. package/node_modules/ibm_db/test/test-getinfo.js +197 -197
  159. package/node_modules/ibm_db/test/test-ibm-db-issue14.js +58 -58
  160. package/node_modules/ibm_db/test/test-ibm-db-issue17.js +54 -54
  161. package/node_modules/ibm_db/test/test-ibm-db-issue18.js +54 -54
  162. package/node_modules/ibm_db/test/test-issue-1059-fetchAll-undefined.js +67 -67
  163. package/node_modules/ibm_db/test/test-issue-1080-array-blob-insert.js +40 -0
  164. package/node_modules/ibm_db/test/test-issue-54.js +39 -39
  165. package/node_modules/ibm_db/test/test-issue-get-column-value-2.js +44 -44
  166. package/node_modules/ibm_db/test/test-issue211.js +80 -80
  167. package/node_modules/ibm_db/test/test-issue253.js +39 -39
  168. package/node_modules/ibm_db/test/test-max-pool-size.js +96 -96
  169. package/node_modules/ibm_db/test/test-memory-leaks-new-objects.js +32 -32
  170. package/node_modules/ibm_db/test/test-multi-open-query-close.js +75 -75
  171. package/node_modules/ibm_db/test/test-param-put-data.js +713 -0
  172. package/node_modules/ibm_db/test/test-pool-idle-connection.js +72 -72
  173. package/node_modules/ibm_db/test/test-pool-operations.js +325 -0
  174. package/node_modules/ibm_db/test/test-pool-stale-connections.js +111 -111
  175. package/node_modules/ibm_db/test/test-pool-uncommited.js +33 -33
  176. package/node_modules/ibm_db/test/test-prepare-execute.js +339 -0
  177. package/node_modules/ibm_db/test/test-prepareSync-multiple-execution.js +93 -93
  178. package/node_modules/ibm_db/test/test-primary-foreign-keys.js +246 -0
  179. package/node_modules/ibm_db/test/test-procedures.js +243 -0
  180. package/node_modules/ibm_db/test/test-promise-all-data-types.js +42 -42
  181. package/node_modules/ibm_db/test/test-query-operations.js +405 -0
  182. package/node_modules/ibm_db/test/test-quick-example.js +25 -25
  183. package/node_modules/ibm_db/test/test-row-array-insert.js +167 -167
  184. package/node_modules/ibm_db/test/test-sp-array.js +54 -54
  185. package/node_modules/ibm_db/test/test-sp-dbc-6000.js +88 -88
  186. package/node_modules/ibm_db/test/test-sp-memleak.js +136 -136
  187. package/node_modules/ibm_db/test/test-sp-resultset-execute.js +269 -269
  188. package/node_modules/ibm_db/test/test-sp-resultset.js +228 -228
  189. package/node_modules/ibm_db/test/test-transactions.js +261 -0
  190. package/node_modules/ibm_db/test/test-typescript.js +48 -48
  191. package/node_modules/ibm_db/test/test-xml-datatype.js +70 -70
  192. package/node_modules/ibm_db/test/testWebApp.js +56 -56
  193. package/node_modules/ibm_db/test/trace +51 -51
  194. package/node_modules/ibm_db/test/typescript-apis-test.js +978 -978
  195. package/node_modules/ibm_db/test/webapp.html +17 -17
  196. package/node_modules/ibm_db/test/webapp.js +79 -79
  197. package/node_modules/ibm_db/typescript/README.md +28 -28
  198. package/node_modules/ibm_db/typescript/dist/Database.d.ts +26 -0
  199. package/node_modules/ibm_db/typescript/dist/ODBCStatement.d.ts +93 -0
  200. package/node_modules/string_decoder/node_modules/safe-buffer/LICENSE +21 -0
  201. package/node_modules/string_decoder/node_modules/safe-buffer/README.md +584 -0
  202. package/node_modules/string_decoder/node_modules/safe-buffer/index.d.ts +187 -0
  203. package/node_modules/string_decoder/node_modules/safe-buffer/index.js +62 -0
  204. package/node_modules/string_decoder/node_modules/safe-buffer/package.json +37 -0
  205. package/node_modules/which-typed-array/CHANGELOG.md +14 -0
  206. package/node_modules/which-typed-array/README.md +0 -2
  207. package/node_modules/which-typed-array/index.d.ts +288 -11
  208. package/node_modules/which-typed-array/index.js +6 -7
  209. package/node_modules/which-typed-array/package.json +12 -11
  210. package/package.json +10 -11
  211. package/node_modules/ibm_db/test/disabled/test-binding-statement-rebinding.js +0 -59
  212. package/node_modules/ibm_db/test/disabled/test-open-dont-close.js +0 -12
  213. package/node_modules/ibm_db/test/disabled/test-param-select-with-booleans-only.js +0 -21
  214. package/node_modules/ibm_db/test/disabled/test-param-select-with-decimals-only.js +0 -20
  215. package/node_modules/ibm_db/test/disabled/test-param-select-with-null.js +0 -21
  216. package/node_modules/ibm_db/test/disabled/test-param-select-with-nulls-mixed.js +0 -23
  217. package/node_modules/ibm_db/test/disabled/test-param-select-with-numbers-mixed.js +0 -22
  218. package/node_modules/ibm_db/test/disabled/test-param-select-with-numbers-only.js +0 -24
  219. package/node_modules/ibm_db/test/disabled/test-param-select-with-strings-only.js +0 -23
  220. package/node_modules/ibm_db/test/disabled/test-param-select-with-unicode.js +0 -15
  221. package/node_modules/ibm_db/test/disabled/test-prepare-bind-execute-closeSync.js +0 -62
  222. package/node_modules/ibm_db/test/disabled/test-prepare-bind-execute-error.js +0 -49
  223. package/node_modules/ibm_db/test/disabled/test-prepare-bind-execute-long-string.js +0 -63
  224. package/node_modules/ibm_db/test/disabled/test-prepare-bind-executeNonQuery.js +0 -63
  225. package/node_modules/ibm_db/test/disabled/test-prepare-bindSync-execute-closeSync.js +0 -46
  226. package/node_modules/ibm_db/test/disabled/test-prepare-bindSync-executeNonQuery.js +0 -50
  227. package/node_modules/ibm_db/test/disabled/test-prepare.js +0 -34
  228. package/node_modules/ibm_db/test/disabled/test-prepareSync.js +0 -29
  229. package/node_modules/ibm_db/test/disabled/test-query-select-fetchAll.js +0 -23
  230. package/node_modules/ibm_db/test/disabled/test-query-select-fetchAllSync.js +0 -23
  231. package/node_modules/ibm_db/test/disabled/test-query-select-unicode.js +0 -15
  232. package/node_modules/ibm_db/test/disabled/test-querySync-select-unicode.js +0 -20
  233. package/node_modules/ibm_db/test/test-affected-rows.js +0 -55
  234. package/node_modules/ibm_db/test/test-bad-connection-string.js +0 -30
  235. package/node_modules/ibm_db/test/test-binding-connection-timeOut.js +0 -31
  236. package/node_modules/ibm_db/test/test-binding-statement-executeSync.js +0 -84
  237. package/node_modules/ibm_db/test/test-binding-transaction-commit.js +0 -79
  238. package/node_modules/ibm_db/test/test-binding-transaction-commitSync.js +0 -53
  239. package/node_modules/ibm_db/test/test-closed.js +0 -14
  240. package/node_modules/ibm_db/test/test-connection-object.js +0 -16
  241. package/node_modules/ibm_db/test/test-describe-column.js +0 -92
  242. package/node_modules/ibm_db/test/test-describe-database.js +0 -18
  243. package/node_modules/ibm_db/test/test-describe-table.js +0 -31
  244. package/node_modules/ibm_db/test/test-domains-open.js +0 -19
  245. package/node_modules/ibm_db/test/test-global-open-close.js +0 -11
  246. package/node_modules/ibm_db/test/test-instantiate-one-and-end.js +0 -9
  247. package/node_modules/ibm_db/test/test-multi-open-close.js +0 -45
  248. package/node_modules/ibm_db/test/test-multi-openSync-closeSync.js +0 -52
  249. package/node_modules/ibm_db/test/test-open-close.js +0 -29
  250. package/node_modules/ibm_db/test/test-open-connectTimeout.js +0 -33
  251. package/node_modules/ibm_db/test/test-openSync.js +0 -31
  252. package/node_modules/ibm_db/test/test-pool-close.js +0 -120
  253. package/node_modules/ibm_db/test/test-pool-connect.js +0 -36
  254. package/node_modules/ibm_db/test/test-pool-rollbackTransaction.js +0 -35
  255. package/node_modules/ibm_db/test/test-prepareSync-bad-sql.js +0 -34
  256. package/node_modules/ibm_db/test/test-promise-open-close.js +0 -33
  257. package/node_modules/ibm_db/test/test-query-create-table-fetchSync.js +0 -23
  258. package/node_modules/ibm_db/test/test-query-create-table.js +0 -12
  259. package/node_modules/ibm_db/test/test-query-drop-table.js +0 -13
  260. package/node_modules/ibm_db/test/test-query-insert.js +0 -38
  261. package/node_modules/ibm_db/test/test-query-select-fetch.js +0 -21
  262. package/node_modules/ibm_db/test/test-query-select-fetchMode-array.js +0 -17
  263. package/node_modules/ibm_db/test/test-query-select.js +0 -64
  264. package/node_modules/ibm_db/test/test-queryResult.js +0 -35
  265. package/node_modules/ibm_db/test/test-queryStream.js +0 -43
  266. package/node_modules/ibm_db/test/test-querySync-select-with-exception.js +0 -25
  267. package/node_modules/ibm_db/test/test-querySync-select.js +0 -28
  268. package/node_modules/ibm_db/test/test-require-and-end.js +0 -8
  269. package/node_modules/ibm_db/test/test-statement-close.js +0 -118
  270. package/node_modules/ibm_db/test/test-transaction-commit-sync.js +0 -54
  271. package/node_modules/ibm_db/test/test-transaction-commit.js +0 -79
  272. package/node_modules/which-typed-array/eslint.config.mjs +0 -12
  273. package/node_modules/which-typed-array/test/index.js +0 -105
  274. /package/node_modules/{tar-fs/node_modules/chownr → chownr}/LICENSE +0 -0
  275. /package/node_modules/{tar-fs/node_modules/chownr → chownr}/README.md +0 -0
  276. /package/node_modules/{tar-fs/node_modules/chownr → chownr}/chownr.js +0 -0
  277. /package/node_modules/{tar-fs/node_modules/chownr → chownr}/package.json +0 -0
  278. /package/node_modules/{tar-stream/node_modules/string_decoder → string_decoder}/.travis.yml +0 -0
  279. /package/node_modules/{tar-stream/node_modules/string_decoder → string_decoder}/LICENSE +0 -0
  280. /package/node_modules/{tar-stream/node_modules/string_decoder → string_decoder}/README.md +0 -0
  281. /package/node_modules/{tar-stream/node_modules/string_decoder → string_decoder}/lib/string_decoder.js +0 -0
  282. /package/node_modules/{tar-stream/node_modules/string_decoder → string_decoder}/package.json +0 -0
@@ -1,1547 +1,1547 @@
1
- /*
2
- Copyright (c) 2013, Dan VerWeire <dverweire@gmail.com>
3
-
4
- Permission to use, copy, modify, and/or distribute this software for any
5
- purpose with or without fee is hereby granted, provided that the above
6
- copyright notice and this permission notice appear in all copies.
7
-
8
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
- */
16
-
17
- #include <string.h>
18
- #include <time.h>
19
- #include <uv.h>
20
-
21
- #include "odbc.h"
22
- #include "odbc_connection.h"
23
- #include "odbc_result.h"
24
- #include "odbc_statement.h"
25
-
26
- Napi::FunctionReference ODBCResult::constructor;
27
-
28
- Napi::Object ODBCResult::Init(Napi::Env env, Napi::Object exports)
29
- {
30
- DEBUG_PRINTF("ODBCResult::Init\n");
31
-
32
- Napi::Function func = DefineClass(env, "ODBCResult", {
33
- InstanceMethod("fetchAll", &ODBCResult::FetchAll, NAPI_METHOD_ATTR),
34
- InstanceMethod("fetch", &ODBCResult::Fetch, NAPI_METHOD_ATTR),
35
- InstanceMethod("getData", &ODBCResult::GetData, NAPI_METHOD_ATTR),
36
- InstanceMethod("moreResultsSync", &ODBCResult::MoreResultsSync, NAPI_METHOD_ATTR),
37
- InstanceMethod("close", &ODBCResult::Close, NAPI_METHOD_ATTR),
38
- InstanceMethod("closeSync", &ODBCResult::CloseSync, NAPI_METHOD_ATTR),
39
- InstanceMethod("fetchSync", &ODBCResult::FetchSync, NAPI_METHOD_ATTR),
40
- InstanceMethod("fetchAllSync", &ODBCResult::FetchAllSync, NAPI_METHOD_ATTR),
41
- InstanceMethod("fetchN", &ODBCResult::FetchN, NAPI_METHOD_ATTR),
42
- InstanceMethod("fetchNSync", &ODBCResult::FetchNSync, NAPI_METHOD_ATTR),
43
- InstanceMethod("getDataSync", &ODBCResult::GetDataSync, NAPI_METHOD_ATTR),
44
- InstanceMethod("getColumnNamesSync", &ODBCResult::GetColumnNamesSync, NAPI_METHOD_ATTR),
45
- InstanceMethod("getColumnMetadataSync", &ODBCResult::GetColumnMetadataSync, NAPI_METHOD_ATTR),
46
- InstanceMethod("getSQLErrorSync", &ODBCResult::GetSQLErrorSync, NAPI_METHOD_ATTR),
47
- InstanceMethod("getAffectedRowsSync", &ODBCResult::GetAffectedRowsSync, NAPI_METHOD_ATTR),
48
- InstanceMethod("bindFileToColSync", &ODBCResult::BindFileToColSync, NAPI_METHOD_ATTR),
49
- InstanceAccessor("fetchMode", &ODBCResult::FetchModeGetter, &ODBCResult::FetchModeSetter, napi_enumerable),
50
- });
51
-
52
- constructor = Napi::Persistent(func);
53
- constructor.SuppressDestruct();
54
- exports.Set("ODBCResult", func);
55
- return exports;
56
- }
57
-
58
- ODBCResult::~ODBCResult()
59
- {
60
- DEBUG_PRINTF("ODBCResult::~ODBCResult m_hSTMT=%X m_canFreeHandle=%d\n", m_hSTMT, m_canFreeHandle);
61
- this->Free();
62
- }
63
-
64
- void ODBCResult::Free()
65
- {
66
- FreeBlockFetchBuffers();
67
-
68
- if (m_fileColBindings) { free(m_fileColBindings); m_fileColBindings = NULL; m_fileColCount = 0; }
69
- if (m_hSTMT && m_canFreeHandle) {
70
- SQLFreeHandle(SQL_HANDLE_STMT, m_hSTMT);
71
- m_hSTMT = (SQLHSTMT)NULL; m_canFreeHandle = 0;
72
- }
73
- if (buffer != NULL) { free((uint16_t *)buffer); buffer = NULL; }
74
- bufferLength = 0;
75
- }
76
-
77
- ODBCResult::ODBCResult(const Napi::CallbackInfo &info) : Napi::ObjectWrap<ODBCResult>(info)
78
- {
79
- DEBUG_PRINTF("ODBCResult::New\n");
80
- Napi::Env env = info.Env();
81
-
82
- if (info.Length() < 4 || !info[0].IsExternal() || !info[1].IsExternal() || !info[2].IsExternal() || !info[3].IsExternal())
83
- {
84
- Napi::TypeError::New(env, "ODBCResult::New requires 4 external arguments").ThrowAsJavaScriptException();
85
- return;
86
- }
87
-
88
- m_hENV = (SQLHENV)((intptr_t)info[0].As<Napi::External<void>>().Data());
89
- m_hDBC = (SQLHDBC)((intptr_t)info[1].As<Napi::External<void>>().Data());
90
- m_hSTMT = (SQLHSTMT)((intptr_t)info[2].As<Napi::External<void>>().Data());
91
- bool *canFreeHandle = static_cast<bool *>(info[3].As<Napi::External<void>>().Data());
92
- m_canFreeHandle = *canFreeHandle;
93
- delete canFreeHandle;
94
-
95
- bufferLength = MAX_VALUE_SIZE;
96
- buffer = NULL;
97
- colCount = 0;
98
- m_fetchMode = FETCH_OBJECT;
99
-
100
- m_rowArraySize = 0;
101
- m_rowsFetched = 0;
102
- m_currentRowInBlock = 0;
103
- m_rowStatusArray = NULL;
104
- m_boundCols = NULL;
105
- m_blockFetchInitialized = false;
106
- m_blockExhausted = false;
107
- m_fileColBindings = NULL;
108
- m_fileColCount = 0;
109
- }
110
-
111
- Napi::Value ODBCResult::FetchModeGetter(const Napi::CallbackInfo &info)
112
- {
113
- return Napi::Number::New(info.Env(), m_fetchMode);
114
- }
115
-
116
- void ODBCResult::FetchModeSetter(const Napi::CallbackInfo &info, const Napi::Value &value)
117
- {
118
- if (value.IsNumber()) m_fetchMode = value.As<Napi::Number>().Int32Value();
119
- }
120
-
121
- /*
122
- * Fetch
123
- */
124
- Napi::Value ODBCResult::Fetch(const Napi::CallbackInfo &info)
125
- {
126
- DEBUG_PRINTF("ODBCResult::Fetch\n");
127
- Napi::Env env = info.Env();
128
-
129
- uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
130
- MEMCHECK(work_req);
131
- fetch_work_data *data = (fetch_work_data *)calloc(1, sizeof(fetch_work_data));
132
- if (!data) free(work_req);
133
- MEMCHECK(data);
134
-
135
- Napi::Function cb;
136
- data->fetchMode = m_fetchMode;
137
-
138
- if (info.Length() == 1 && info[0].IsFunction())
139
- {
140
- cb = info[0].As<Napi::Function>();
141
- }
142
- else if (info.Length() == 2 && info[0].IsObject() && info[1].IsFunction())
143
- {
144
- cb = info[1].As<Napi::Function>();
145
- Napi::Object obj = info[0].As<Napi::Object>();
146
- if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
147
- data->fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
148
- }
149
- else
150
- {
151
- free(data); free(work_req);
152
- Napi::TypeError::New(env, "ODBCResult::Fetch(): 1 or 2 arguments are required. The last argument must be a callback function.").ThrowAsJavaScriptException();
153
- return env.Undefined();
154
- }
155
-
156
- data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
157
- data->objResult = this;
158
- data->useBlockFetch = false;
159
- data->blockRowIndex = 0;
160
- data->env = env;
161
- work_req->data = data;
162
-
163
- uv_queue_work(uv_default_loop(), work_req, UV_Fetch, (uv_after_work_cb)UV_AfterFetch);
164
- this->Ref();
165
- return env.Undefined();
166
- }
167
-
168
- void ODBCResult::UV_Fetch(uv_work_t *work_req)
169
- {
170
- DEBUG_PRINTF("ODBCResult::UV_Fetch\n");
171
- fetch_work_data *data = (fetch_work_data *)(work_req->data);
172
-
173
- bool useBlockFetch = data->objResult->InitBlockFetch();
174
- data->useBlockFetch = useBlockFetch;
175
-
176
- if (useBlockFetch)
177
- {
178
- ODBCResult *self = data->objResult;
179
- if (self->m_blockExhausted)
180
- {
181
- data->result = SQL_NO_DATA;
182
- }
183
- else
184
- {
185
- self->m_rowsFetched = 0;
186
- self->m_currentRowInBlock = 0;
187
- data->result = SQLFetch(self->m_hSTMT);
188
- if (data->result == SQL_NO_DATA) self->m_blockExhausted = true;
189
- }
190
- }
191
- else
192
- {
193
- data->result = SQLFetch(data->objResult->m_hSTMT);
194
- }
195
- }
196
-
197
- void ODBCResult::UV_AfterFetch(uv_work_t *work_req, int status)
198
- {
199
- DEBUG_PRINTF("ODBCResult::UV_AfterFetch\n");
200
- fetch_work_data *data = (fetch_work_data *)(work_req->data);
201
- Napi::Env env(data->env);
202
- Napi::HandleScope scope(env);
203
-
204
- SQLRETURN ret = data->result;
205
- Napi::Value objError;
206
- bool moreWork = true;
207
- bool error = false;
208
-
209
- if (data->objResult->colCount == 0)
210
- data->objResult->columns = ODBC::GetColumns(data->objResult->m_hSTMT, &data->objResult->colCount);
211
-
212
- if (data->objResult->colCount == 0 || data->fetchMode == FETCH_NODATA)
213
- moreWork = false;
214
- else if (ret == SQL_ERROR)
215
- {
216
- moreWork = false; error = true;
217
- objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, data->objResult->m_hSTMT, (char *)"Error in ODBCResult::UV_AfterFetch");
218
- }
219
- else if (ret == SQL_NO_DATA)
220
- moreWork = false;
221
-
222
- if (moreWork)
223
- {
224
- Napi::Value rowData;
225
- if (data->useBlockFetch)
226
- {
227
- ODBCResult *self = data->objResult->self();
228
- Napi::Array blockArray = Napi::Array::New(env);
229
- int validCount = 0;
230
- for (SQLULEN r = 0; r < self->m_rowsFetched; r++)
231
- {
232
- SQLUSMALLINT st = self->m_rowStatusArray[r];
233
- if (st == SQL_ROW_SUCCESS || st == SQL_ROW_SUCCESS_WITH_INFO)
234
- {
235
- if (data->fetchMode == FETCH_ARRAY)
236
- {
237
- Napi::Array array = Napi::Array::New(env);
238
- for (int c = 0; c < self->colCount; c++)
239
- array.Set((uint32_t)c, self->GetBoundColumnValue(env, c, r));
240
- blockArray.Set((uint32_t)validCount++, array);
241
- }
242
- else
243
- {
244
- Napi::Object tuple = Napi::Object::New(env);
245
- for (int c = 0; c < self->colCount; c++)
246
- {
247
- #ifdef UNICODE
248
- tuple.Set(Napi::String::New(env, (const char16_t *)self->columns[c].name), self->GetBoundColumnValue(env, c, r));
249
- #else
250
- tuple.Set(Napi::String::New(env, (const char *)self->columns[c].name), self->GetBoundColumnValue(env, c, r));
251
- #endif
252
- }
253
- blockArray.Set((uint32_t)validCount++, tuple);
254
- }
255
- }
256
- }
257
- rowData = blockArray;
258
- }
259
- else if (data->fetchMode == FETCH_ARRAY)
260
- {
261
- rowData = ODBC::GetRecordArray(env, data->objResult->m_hSTMT, data->objResult->columns,
262
- &data->objResult->colCount, data->objResult->buffer, data->objResult->bufferLength);
263
- }
264
- else
265
- {
266
- rowData = ODBC::GetRecordTuple(env, data->objResult->m_hSTMT, data->objResult->columns,
267
- &data->objResult->colCount, data->objResult->buffer, data->objResult->bufferLength);
268
- }
269
- data->objResult->OverrideFileColumns(env, rowData, data->fetchMode);
270
- data->cb->Call({env.Null(), rowData});
271
- }
272
- else
273
- {
274
- if (data->fetchMode != FETCH_NODATA)
275
- {
276
- ODBC::FreeColumns(data->objResult->columns, &data->objResult->colCount);
277
- }
278
- FREE(data->objResult->buffer);
279
-
280
- if (error)
281
- data->cb->Call({objError, env.Null()});
282
- else
283
- data->cb->Call({env.Null(), env.Null()});
284
- }
285
- PropagateCallbackException(env);
286
-
287
- data->objResult->Unref();
288
- delete data->cb;
289
- free(data);
290
- free(work_req);
291
- }
292
-
293
- /*
294
- * FetchSync
295
- */
296
- Napi::Value ODBCResult::FetchSync(const Napi::CallbackInfo &info)
297
- {
298
- DEBUG_PRINTF("ODBCResult::FetchSync - Entry\n");
299
- Napi::Env env = info.Env();
300
-
301
- Napi::Value objError;
302
- bool moreWork = true;
303
- bool error = false;
304
- int fetchMode = m_fetchMode;
305
-
306
- if (info.Length() == 1 && info[0].IsObject())
307
- {
308
- Napi::Object obj = info[0].As<Napi::Object>();
309
- if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
310
- fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
311
- }
312
-
313
- if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
314
-
315
- bool useBlockFetch = InitBlockFetch();
316
- SQLRETURN ret;
317
-
318
- if (useBlockFetch)
319
- {
320
- if (m_blockExhausted) ret = SQL_NO_DATA;
321
- else { m_rowsFetched = 0; m_currentRowInBlock = 0; ret = SQLFetch(m_hSTMT); }
322
- }
323
- else
324
- ret = SQLFetch(m_hSTMT);
325
-
326
- if (ret == SQL_ERROR)
327
- {
328
- moreWork = false; error = true;
329
- objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"Error in ODBCResult::FetchSync");
330
- }
331
- else if (ret == SQL_NO_DATA)
332
- {
333
- if (useBlockFetch) m_blockExhausted = true;
334
- moreWork = false;
335
- }
336
- else if (colCount == 0)
337
- moreWork = false;
338
-
339
- if (!error && fetchMode == FETCH_NODATA) return env.Null();
340
-
341
- if (moreWork)
342
- {
343
- Napi::Value data;
344
- if (useBlockFetch)
345
- {
346
- Napi::Array blockArray = Napi::Array::New(env);
347
- int validCount = 0;
348
- for (SQLULEN r = 0; r < m_rowsFetched; r++)
349
- {
350
- SQLUSMALLINT st = m_rowStatusArray[r];
351
- if (st == SQL_ROW_SUCCESS || st == SQL_ROW_SUCCESS_WITH_INFO)
352
- {
353
- if (fetchMode == FETCH_ARRAY)
354
- {
355
- Napi::Array array = Napi::Array::New(env);
356
- for (int c = 0; c < colCount; c++) array.Set((uint32_t)c, GetBoundColumnValue(env, c, r));
357
- blockArray.Set((uint32_t)validCount++, array);
358
- }
359
- else
360
- {
361
- Napi::Object tuple = Napi::Object::New(env);
362
- for (int c = 0; c < colCount; c++)
363
- {
364
- #ifdef UNICODE
365
- tuple.Set(Napi::String::New(env, (const char16_t *)columns[c].name), GetBoundColumnValue(env, c, r));
366
- #else
367
- tuple.Set(Napi::String::New(env, (const char *)columns[c].name), GetBoundColumnValue(env, c, r));
368
- #endif
369
- }
370
- blockArray.Set((uint32_t)validCount++, tuple);
371
- }
372
- }
373
- }
374
- data = blockArray;
375
- }
376
- else if (fetchMode == FETCH_ARRAY)
377
- data = ODBC::GetRecordArray(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
378
- else
379
- data = ODBC::GetRecordTuple(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
380
-
381
- OverrideFileColumns(env, data, fetchMode);
382
- return data;
383
- }
384
- else
385
- {
386
- ODBC::FreeColumns(columns, &colCount);
387
- FREE(buffer);
388
- if (error)
389
- {
390
- napi_throw(env, objError);
391
- return env.Null();
392
- }
393
- return env.Null();
394
- }
395
- }
396
-
397
- /*
398
- * FetchAll
399
- */
400
- Napi::Value ODBCResult::FetchAll(const Napi::CallbackInfo &info)
401
- {
402
- DEBUG_PRINTF("ODBCResult::FetchAll - Entry\n");
403
- Napi::Env env = info.Env();
404
-
405
- uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
406
- MEMCHECK(work_req);
407
- fetch_work_data *data = (fetch_work_data *)calloc(1, sizeof(fetch_work_data));
408
- if (!data) free(work_req);
409
- MEMCHECK(data);
410
-
411
- Napi::Function cb;
412
- data->fetchMode = m_fetchMode;
413
-
414
- if (info.Length() == 1 && info[0].IsFunction())
415
- {
416
- cb = info[0].As<Napi::Function>();
417
- }
418
- else if (info.Length() == 2 && info[0].IsObject() && info[1].IsFunction())
419
- {
420
- cb = info[1].As<Napi::Function>();
421
- Napi::Object obj = info[0].As<Napi::Object>();
422
- if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
423
- data->fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
424
- }
425
- else
426
- {
427
- free(data); free(work_req);
428
- Napi::TypeError::New(env, "ODBCResult::FetchAll(): 1 or 2 arguments are required. The last argument must be a callback function.").ThrowAsJavaScriptException();
429
- return env.Undefined();
430
- }
431
-
432
- data->rows = new Napi::Reference<Napi::Array>(Napi::Persistent(Napi::Array::New(env)));
433
- data->errorCount = 0;
434
- data->count = 0;
435
- data->objError = new Napi::Reference<Napi::Object>(Napi::Persistent(Napi::Object::New(env)));
436
-
437
- data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
438
- data->objResult = this;
439
- data->useBlockFetch = false;
440
- data->blockRowIndex = 0;
441
- data->env = env;
442
- work_req->data = data;
443
-
444
- uv_queue_work(uv_default_loop(), work_req, UV_FetchAll, (uv_after_work_cb)UV_AfterFetchAll);
445
- this->Ref();
446
- return env.Undefined();
447
- }
448
-
449
- void ODBCResult::UV_FetchAll(uv_work_t *work_req)
450
- {
451
- DEBUG_PRINTF("ODBCResult::UV_FetchAll - Entry\n");
452
- fetch_work_data *data = (fetch_work_data *)(work_req->data);
453
-
454
- bool useBlockFetch = data->objResult->InitBlockFetch();
455
- data->useBlockFetch = useBlockFetch;
456
-
457
- if (useBlockFetch)
458
- {
459
- SQLULEN rowIdx = 0;
460
- data->result = data->objResult->BlockFetchNextRow(&rowIdx);
461
- data->blockRowIndex = rowIdx;
462
- }
463
- else
464
- data->result = SQLFetch(data->objResult->m_hSTMT);
465
- }
466
-
467
- void ODBCResult::UV_AfterFetchAll(uv_work_t *work_req, int status)
468
- {
469
- DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll - Entry\n");
470
- fetch_work_data *data = (fetch_work_data *)(work_req->data);
471
- Napi::Env env(data->env);
472
- Napi::HandleScope scope(env);
473
-
474
- ODBCResult *self = data->objResult->self();
475
- bool doMoreWork = true;
476
-
477
- if (data->result == SQL_ERROR)
478
- {
479
- data->errorCount++;
480
- data->objError->Reset(ODBC::GetSQLError(env, SQL_HANDLE_STMT, self->m_hSTMT,
481
- (char *)"[node-odbc] Error in ODBCResult::UV_AfterFetchAll").As<Napi::Object>());
482
- }
483
-
484
- if (self->colCount == 0) self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount);
485
-
486
- bool noDataFetchQuery = false;
487
- if (self->colCount == 0) { noDataFetchQuery = true; doMoreWork = false; }
488
- else if (data->result == SQL_NO_DATA) doMoreWork = false;
489
- else if (data->result == SQL_ERROR) doMoreWork = false;
490
- else
491
- {
492
- Napi::Array rows = data->rows->Value();
493
-
494
- if (data->useBlockFetch)
495
- {
496
- SQLULEN rowIdx = data->blockRowIndex;
497
- if (data->fetchMode == FETCH_ARRAY)
498
- {
499
- Napi::Array array = Napi::Array::New(env);
500
- for (int c = 0; c < self->colCount; c++)
501
- array.Set((uint32_t)c, self->GetBoundColumnValue(env, c, rowIdx));
502
- rows.Set((uint32_t)data->count, array);
503
- }
504
- else
505
- {
506
- Napi::Object tuple = Napi::Object::New(env);
507
- for (int c = 0; c < self->colCount; c++)
508
- {
509
- #ifdef UNICODE
510
- tuple.Set(Napi::String::New(env, (const char16_t *)self->columns[c].name), self->GetBoundColumnValue(env, c, rowIdx));
511
- #else
512
- tuple.Set(Napi::String::New(env, (const char *)self->columns[c].name), self->GetBoundColumnValue(env, c, rowIdx));
513
- #endif
514
- }
515
- rows.Set((uint32_t)data->count, tuple);
516
- }
517
- }
518
- else if (data->fetchMode == FETCH_ARRAY)
519
- {
520
- Napi::Value rowVal = ODBC::GetRecordArray(env, self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength);
521
- self->OverrideFileColumns(env, rowVal, data->fetchMode);
522
- rows.Set((uint32_t)data->count, rowVal);
523
- }
524
- else
525
- {
526
- Napi::Value rowVal = ODBC::GetRecordTuple(env, self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength);
527
- self->OverrideFileColumns(env, rowVal, data->fetchMode);
528
- rows.Set((uint32_t)data->count, rowVal);
529
- }
530
- data->count++;
531
- }
532
-
533
- if (doMoreWork)
534
- {
535
- uv_queue_work(uv_default_loop(), work_req, UV_FetchAll, (uv_after_work_cb)UV_AfterFetchAll);
536
- }
537
- else
538
- {
539
- Napi::Value err;
540
- if (data->errorCount > 0 && !noDataFetchQuery)
541
- err = data->objError->Value();
542
- else
543
- err = env.Null();
544
-
545
- data->cb->Call({err, data->rows->Value(), Napi::Number::New(env, self->colCount)});
546
- PropagateCallbackException(env);
547
-
548
- ODBC::FreeColumns(self->columns, &self->colCount);
549
- FREE(self->buffer);
550
- delete data->cb;
551
- delete data->rows;
552
- delete data->objError;
553
- free(data);
554
- free(work_req);
555
- self->Unref();
556
- }
557
- }
558
-
559
- /*
560
- * FetchAllSync
561
- */
562
- Napi::Value ODBCResult::FetchAllSync(const Napi::CallbackInfo &info)
563
- {
564
- DEBUG_PRINTF("ODBCResult::FetchAllSync - Entry\n");
565
- Napi::Env env = info.Env();
566
-
567
- Napi::Value objError;
568
- SQLRETURN ret;
569
- int count = 0, errorCount = 0;
570
- int fetchMode = m_fetchMode;
571
-
572
- if (info.Length() == 1 && info[0].IsObject())
573
- {
574
- Napi::Object obj = info[0].As<Napi::Object>();
575
- if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
576
- fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
577
- }
578
-
579
- if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
580
-
581
- Napi::Array rows = Napi::Array::New(env);
582
-
583
- if (colCount > 0)
584
- {
585
- bool useBlockFetch = InitBlockFetch();
586
-
587
- if (useBlockFetch)
588
- {
589
- while (true)
590
- {
591
- m_rowsFetched = 0;
592
- ret = SQLFetch(m_hSTMT);
593
- if (ret == SQL_ERROR)
594
- {
595
- errorCount++;
596
- objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::FetchAllSync (block fetch)");
597
- break;
598
- }
599
- if (ret == SQL_NO_DATA) break;
600
-
601
- for (SQLULEN r = 0; r < m_rowsFetched; r++)
602
- {
603
- if (m_rowStatusArray[r] == SQL_ROW_ERROR || m_rowStatusArray[r] == SQL_ROW_NOROW) continue;
604
- if (fetchMode == FETCH_ARRAY)
605
- {
606
- Napi::Array array = Napi::Array::New(env);
607
- for (int c = 0; c < colCount; c++) array.Set((uint32_t)c, GetBoundColumnValue(env, c, r));
608
- rows.Set((uint32_t)count, array);
609
- }
610
- else
611
- {
612
- Napi::Object tuple = Napi::Object::New(env);
613
- for (int c = 0; c < colCount; c++)
614
- {
615
- #ifdef UNICODE
616
- tuple.Set(Napi::String::New(env, (const char16_t *)columns[c].name), GetBoundColumnValue(env, c, r));
617
- #else
618
- tuple.Set(Napi::String::New(env, (const char *)columns[c].name), GetBoundColumnValue(env, c, r));
619
- #endif
620
- }
621
- rows.Set((uint32_t)count, tuple);
622
- }
623
- count++;
624
- }
625
- }
626
- }
627
- else
628
- {
629
- while (true)
630
- {
631
- ret = SQLFetch(m_hSTMT);
632
- if (ret == SQL_ERROR)
633
- {
634
- errorCount++;
635
- objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT,
636
- (char *)"[node-odbc] Error in ODBCResult::FetchAllSync; probably your query did not have a result set.");
637
- break;
638
- }
639
- if (ret == SQL_NO_DATA) break;
640
-
641
- if (fetchMode == FETCH_ARRAY)
642
- {
643
- Napi::Value rowVal = ODBC::GetRecordArray(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
644
- OverrideFileColumns(env, rowVal, fetchMode);
645
- rows.Set((uint32_t)count, rowVal);
646
- }
647
- else
648
- {
649
- Napi::Value rowVal = ODBC::GetRecordTuple(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
650
- OverrideFileColumns(env, rowVal, fetchMode);
651
- rows.Set((uint32_t)count, rowVal);
652
- }
653
- count++;
654
- }
655
- }
656
- }
657
-
658
- ODBC::FreeColumns(columns, &colCount);
659
- FREE(buffer);
660
-
661
- if (errorCount > 0) napi_throw(env, objError);
662
- return rows;
663
- }
664
-
665
- /*
666
- * FetchN
667
- */
668
- Napi::Value ODBCResult::FetchN(const Napi::CallbackInfo &info)
669
- {
670
- DEBUG_PRINTF("ODBCResult::FetchN - Entry\n");
671
- Napi::Env env = info.Env();
672
-
673
- uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
674
- MEMCHECK(work_req);
675
- fetch_work_data *data = (fetch_work_data *)calloc(1, sizeof(fetch_work_data));
676
- if (!data) free(work_req);
677
- MEMCHECK(data);
678
-
679
- Napi::Function cb;
680
- data->fetchMode = m_fetchMode;
681
-
682
- if (info.Length() == 2 && info[0].IsNumber() && info[1].IsFunction())
683
- {
684
- data->maxCount = info[0].As<Napi::Number>().Int32Value();
685
- cb = info[1].As<Napi::Function>();
686
- }
687
- else if (info.Length() == 3 && info[0].IsNumber() && info[1].IsObject() && info[2].IsFunction())
688
- {
689
- data->maxCount = info[0].As<Napi::Number>().Int32Value();
690
- cb = info[2].As<Napi::Function>();
691
- Napi::Object obj = info[1].As<Napi::Object>();
692
- if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
693
- data->fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
694
- }
695
- else
696
- {
697
- free(data); free(work_req);
698
- Napi::TypeError::New(env, "ODBCResult::FetchN(): Arguments must be (count, [option], callback).").ThrowAsJavaScriptException();
699
- return env.Undefined();
700
- }
701
-
702
- if (data->maxCount <= 0)
703
- {
704
- free(data); free(work_req);
705
- Napi::RangeError::New(env, "ODBCResult::FetchN(): count must be a positive integer.").ThrowAsJavaScriptException();
706
- return env.Undefined();
707
- }
708
-
709
- data->rows = new Napi::Reference<Napi::Array>(Napi::Persistent(Napi::Array::New(env)));
710
- data->errorCount = 0;
711
- data->count = 0;
712
- data->objError = new Napi::Reference<Napi::Object>(Napi::Persistent(Napi::Object::New(env)));
713
-
714
- data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
715
- data->objResult = this;
716
- data->useBlockFetch = false;
717
- data->blockRowIndex = 0;
718
- data->env = env;
719
- work_req->data = data;
720
-
721
- uv_queue_work(uv_default_loop(), work_req, UV_FetchN, (uv_after_work_cb)UV_AfterFetchN);
722
- this->Ref();
723
- return env.Undefined();
724
- }
725
-
726
- void ODBCResult::UV_FetchN(uv_work_t *work_req)
727
- {
728
- DEBUG_PRINTF("ODBCResult::UV_FetchN\n");
729
- fetch_work_data *data = (fetch_work_data *)(work_req->data);
730
-
731
- bool useBlockFetch = data->objResult->InitBlockFetch();
732
- data->useBlockFetch = useBlockFetch;
733
-
734
- if (useBlockFetch)
735
- {
736
- SQLULEN rowIdx = 0;
737
- data->result = data->objResult->BlockFetchNextRow(&rowIdx);
738
- data->blockRowIndex = rowIdx;
739
- }
740
- else
741
- data->result = SQLFetch(data->objResult->m_hSTMT);
742
- }
743
-
744
- void ODBCResult::UV_AfterFetchN(uv_work_t *work_req, int status)
745
- {
746
- DEBUG_PRINTF("ODBCResult::UV_AfterFetchN - Entry\n");
747
- fetch_work_data *data = (fetch_work_data *)(work_req->data);
748
- Napi::Env env(data->env);
749
- Napi::HandleScope scope(env);
750
-
751
- ODBCResult *self = data->objResult->self();
752
- bool doMoreWork = true;
753
-
754
- if (data->result == SQL_ERROR)
755
- {
756
- data->errorCount++;
757
- data->objError->Reset(ODBC::GetSQLError(env, SQL_HANDLE_STMT, self->m_hSTMT,
758
- (char *)"[node-odbc] Error in ODBCResult::UV_AfterFetchN").As<Napi::Object>());
759
- }
760
-
761
- if (self->colCount == 0) self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount);
762
-
763
- if (self->colCount == 0) doMoreWork = false;
764
- else if (data->result == SQL_NO_DATA) doMoreWork = false;
765
- else if (data->result == SQL_ERROR) doMoreWork = false;
766
- else
767
- {
768
- Napi::Array rows = data->rows->Value();
769
-
770
- if (data->useBlockFetch)
771
- {
772
- SQLULEN rowIdx = data->blockRowIndex;
773
- if (data->fetchMode == FETCH_ARRAY)
774
- {
775
- Napi::Array array = Napi::Array::New(env);
776
- for (int c = 0; c < self->colCount; c++) array.Set((uint32_t)c, self->GetBoundColumnValue(env, c, rowIdx));
777
- rows.Set((uint32_t)data->count, array);
778
- }
779
- else
780
- {
781
- Napi::Object tuple = Napi::Object::New(env);
782
- for (int c = 0; c < self->colCount; c++)
783
- {
784
- #ifdef UNICODE
785
- tuple.Set(Napi::String::New(env, (const char16_t *)self->columns[c].name), self->GetBoundColumnValue(env, c, rowIdx));
786
- #else
787
- tuple.Set(Napi::String::New(env, (const char *)self->columns[c].name), self->GetBoundColumnValue(env, c, rowIdx));
788
- #endif
789
- }
790
- rows.Set((uint32_t)data->count, tuple);
791
- }
792
- }
793
- else if (data->fetchMode == FETCH_ARRAY)
794
- {
795
- Napi::Value rowVal = ODBC::GetRecordArray(env, self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength);
796
- self->OverrideFileColumns(env, rowVal, data->fetchMode);
797
- rows.Set((uint32_t)data->count, rowVal);
798
- }
799
- else
800
- {
801
- Napi::Value rowVal = ODBC::GetRecordTuple(env, self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength);
802
- self->OverrideFileColumns(env, rowVal, data->fetchMode);
803
- rows.Set((uint32_t)data->count, rowVal);
804
- }
805
- data->count++;
806
-
807
- if (data->count >= data->maxCount) doMoreWork = false;
808
- }
809
-
810
- if (doMoreWork)
811
- {
812
- uv_queue_work(uv_default_loop(), work_req, UV_FetchN, (uv_after_work_cb)UV_AfterFetchN);
813
- }
814
- else
815
- {
816
- Napi::Value err;
817
- if (data->errorCount > 0 && self->colCount > 0)
818
- err = data->objError->Value();
819
- else
820
- err = env.Null();
821
-
822
- data->cb->Call({err, data->rows->Value()});
823
- delete data->cb;
824
- delete data->rows;
825
- delete data->objError;
826
- free(data);
827
- free(work_req);
828
- self->Unref();
829
- }
830
- }
831
-
832
- /*
833
- * FetchNSync
834
- */
835
- Napi::Value ODBCResult::FetchNSync(const Napi::CallbackInfo &info)
836
- {
837
- DEBUG_PRINTF("ODBCResult::FetchNSync - Entry\n");
838
- Napi::Env env = info.Env();
839
-
840
- if (!info[0].IsNumber())
841
- {
842
- Napi::TypeError::New(env, "ODBCResult::FetchNSync(): First argument must be an integer count.").ThrowAsJavaScriptException();
843
- return env.Undefined();
844
- }
845
-
846
- int maxCount = info[0].As<Napi::Number>().Int32Value();
847
- if (maxCount <= 0)
848
- {
849
- Napi::RangeError::New(env, "ODBCResult::FetchNSync(): count must be a positive integer.").ThrowAsJavaScriptException();
850
- return env.Undefined();
851
- }
852
-
853
- Napi::Value objError;
854
- SQLRETURN ret;
855
- int count = 0, errorCount = 0;
856
- int fetchMode = m_fetchMode;
857
-
858
- if (info.Length() >= 2 && info[1].IsObject())
859
- {
860
- Napi::Object obj = info[1].As<Napi::Object>();
861
- if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
862
- fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
863
- }
864
-
865
- if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
866
-
867
- Napi::Array rows = Napi::Array::New(env);
868
-
869
- if (colCount > 0)
870
- {
871
- bool useBlockFetch = InitBlockFetch();
872
-
873
- if (useBlockFetch)
874
- {
875
- SQLULEN rowIdx;
876
- while (count < maxCount)
877
- {
878
- ret = BlockFetchNextRow(&rowIdx);
879
- if (ret == SQL_ERROR) { errorCount++; objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::FetchNSync (block fetch)"); break; }
880
- if (ret == SQL_NO_DATA) break;
881
-
882
- if (fetchMode == FETCH_ARRAY)
883
- {
884
- Napi::Array array = Napi::Array::New(env);
885
- for (int c = 0; c < colCount; c++) array.Set((uint32_t)c, GetBoundColumnValue(env, c, rowIdx));
886
- rows.Set((uint32_t)count, array);
887
- }
888
- else
889
- {
890
- Napi::Object tuple = Napi::Object::New(env);
891
- for (int c = 0; c < colCount; c++)
892
- {
893
- #ifdef UNICODE
894
- tuple.Set(Napi::String::New(env, (const char16_t *)columns[c].name), GetBoundColumnValue(env, c, rowIdx));
895
- #else
896
- tuple.Set(Napi::String::New(env, (const char *)columns[c].name), GetBoundColumnValue(env, c, rowIdx));
897
- #endif
898
- }
899
- rows.Set((uint32_t)count, tuple);
900
- }
901
- count++;
902
- }
903
- }
904
- else
905
- {
906
- while (count < maxCount)
907
- {
908
- ret = SQLFetch(m_hSTMT);
909
- if (ret == SQL_ERROR) { errorCount++; objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::FetchNSync"); break; }
910
- if (ret == SQL_NO_DATA) break;
911
-
912
- if (fetchMode == FETCH_ARRAY)
913
- {
914
- Napi::Value rowVal = ODBC::GetRecordArray(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
915
- OverrideFileColumns(env, rowVal, fetchMode);
916
- rows.Set((uint32_t)count, rowVal);
917
- }
918
- else
919
- {
920
- Napi::Value rowVal = ODBC::GetRecordTuple(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
921
- OverrideFileColumns(env, rowVal, fetchMode);
922
- rows.Set((uint32_t)count, rowVal);
923
- }
924
- count++;
925
- }
926
- }
927
- }
928
-
929
- if (errorCount > 0) napi_throw(env, objError);
930
- return rows;
931
- }
932
-
933
- /*
934
- * GetData
935
- */
936
- Napi::Value ODBCResult::GetData(const Napi::CallbackInfo &info)
937
- {
938
- DEBUG_PRINTF("ODBCResult::GetData\n");
939
- Napi::Env env = info.Env();
940
-
941
- REQ_INT_ARG(0, colNum);
942
- REQ_INT_ARG(1, dataSize);
943
-
944
- uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
945
- MEMCHECK(work_req);
946
- getdata_work_data *data = (getdata_work_data *)calloc(1, sizeof(getdata_work_data));
947
- if (!data) free(work_req);
948
- MEMCHECK(data);
949
-
950
- Napi::Function cb;
951
-
952
- if (info.Length() == 3 && info[2].IsFunction())
953
- {
954
- cb = info[2].As<Napi::Function>();
955
- data->colNum = (SQLUINTEGER)colNum;
956
- data->dataSize = (SQLUINTEGER)dataSize;
957
- }
958
- else if (info.Length() == 2 && info[1].IsFunction())
959
- {
960
- cb = info[1].As<Napi::Function>();
961
- data->colNum = (SQLUINTEGER)colNum;
962
- data->dataSize = 0;
963
- }
964
- else
965
- {
966
- free(data); free(work_req);
967
- Napi::TypeError::New(env, "ODBCResult::GetData(): 2 or 3 arguments are required.").ThrowAsJavaScriptException();
968
- return env.Undefined();
969
- }
970
-
971
- data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
972
- data->objResult = this;
973
- data->env = env;
974
- work_req->data = data;
975
-
976
- uv_queue_work(uv_default_loop(), work_req, UV_GetData, (uv_after_work_cb)UV_AfterGetData);
977
- this->Ref();
978
- return env.Undefined();
979
- }
980
-
981
- void ODBCResult::UV_GetData(uv_work_t *work_req)
982
- {
983
- // No-op; actual work is done in UV_AfterGetData since it accesses JS values
984
- }
985
-
986
- void ODBCResult::UV_AfterGetData(uv_work_t *work_req, int status)
987
- {
988
- DEBUG_PRINTF("ODBCResult::UV_AfterGetData - Entry\n");
989
- getdata_work_data *data = (getdata_work_data *)(work_req->data);
990
- Napi::Env env(data->env);
991
- Napi::HandleScope scope(env);
992
-
993
- ODBCResult *objResult = data->objResult;
994
- Napi::Value err = env.Null();
995
- Napi::Value val = env.Null();
996
-
997
- if (objResult->colCount > 0 && data->colNum <= (unsigned)objResult->colCount)
998
- {
999
- objResult->bufferLength = (size_t)data->dataSize;
1000
- objResult->columns[data->colNum - 1].getData = true;
1001
- val = ODBC::GetColumnValue(env, objResult->m_hSTMT, objResult->columns[data->colNum - 1], objResult->buffer, objResult->bufferLength);
1002
- }
1003
- else
1004
- {
1005
- ODBC::FreeColumns(objResult->columns, &objResult->colCount);
1006
- FREE(objResult->buffer);
1007
- }
1008
-
1009
- data->cb->Call({err, val});
1010
- PropagateCallbackException(env);
1011
-
1012
- delete data->cb;
1013
- data->objResult->Unref();
1014
- free(data);
1015
- free(work_req);
1016
- }
1017
-
1018
- /*
1019
- * GetDataSync
1020
- */
1021
- Napi::Value ODBCResult::GetDataSync(const Napi::CallbackInfo &info)
1022
- {
1023
- DEBUG_PRINTF("ODBCResult::GetDataSync - Entry\n");
1024
- Napi::Env env = info.Env();
1025
-
1026
- REQ_INT_ARG(0, colNum);
1027
- REQ_INT_ARG(1, dataSize);
1028
-
1029
- if (colCount > 0 && colNum <= colCount)
1030
- {
1031
- bufferLength = (size_t)dataSize;
1032
- columns[colNum - 1].getData = true;
1033
- return ODBC::GetColumnValue(env, m_hSTMT, columns[colNum - 1], buffer, bufferLength);
1034
- }
1035
- else
1036
- {
1037
- ODBC::FreeColumns(columns, &colCount);
1038
- FREE(buffer);
1039
- return env.Null();
1040
- }
1041
- }
1042
-
1043
- /*
1044
- * Close
1045
- */
1046
- Napi::Value ODBCResult::Close(const Napi::CallbackInfo &info)
1047
- {
1048
- DEBUG_PRINTF("ODBCResult::Close - Entry\n");
1049
- Napi::Env env = info.Env();
1050
- Napi::Function cb;
1051
- SQLUSMALLINT closeOption = SQL_DROP;
1052
-
1053
- if (info.Length() == 2)
1054
- {
1055
- if (!info[0].IsNumber()) { Napi::TypeError::New(env, "Argument 0 must be an Integer.").ThrowAsJavaScriptException(); return env.Undefined(); }
1056
- if (!info[1].IsFunction()) { Napi::TypeError::New(env, "Argument 1 must be a Function.").ThrowAsJavaScriptException(); return env.Undefined(); }
1057
- closeOption = (SQLUSMALLINT)info[0].As<Napi::Number>().Int32Value();
1058
- cb = info[1].As<Napi::Function>();
1059
- }
1060
- else if (info.Length() == 1)
1061
- {
1062
- if (!info[0].IsFunction()) { Napi::TypeError::New(env, "ODBCResult::Close(): Argument 0 must be a Function.").ThrowAsJavaScriptException(); return env.Undefined(); }
1063
- cb = info[0].As<Napi::Function>();
1064
- }
1065
-
1066
- uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
1067
- MEMCHECK(work_req);
1068
- close_result_work_data *data = (close_result_work_data *)(calloc(1, sizeof(close_result_work_data)));
1069
- if (!data) free(work_req);
1070
- MEMCHECK(data);
1071
-
1072
- data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
1073
- data->objResult = this;
1074
- data->closeOption = closeOption;
1075
- data->env = env;
1076
- work_req->data = data;
1077
-
1078
- uv_queue_work(uv_default_loop(), work_req, UV_Close, (uv_after_work_cb)UV_AfterClose);
1079
- this->Ref();
1080
- return env.Undefined();
1081
- }
1082
-
1083
- void ODBCResult::UV_Close(uv_work_t *req)
1084
- {
1085
- DEBUG_PRINTF("ODBCResult::UV_Close - Entry\n");
1086
- close_result_work_data *data = (close_result_work_data *)(req->data);
1087
- ODBCResult *objResult = data->objResult;
1088
- SQLHSTMT hSTMT = (SQLHSTMT)objResult->m_hSTMT;
1089
-
1090
- if (data->closeOption == SQL_DROP && objResult->m_canFreeHandle)
1091
- {
1092
- objResult->Free();
1093
- data->result = 0;
1094
- }
1095
- else if (data->closeOption == SQL_DROP && !objResult->m_canFreeHandle)
1096
- {
1097
- SQLFreeStmt(hSTMT, SQL_CLOSE);
1098
- data->result = 0;
1099
- }
1100
- else
1101
- {
1102
- data->result = SQLFreeStmt(hSTMT, data->closeOption);
1103
- }
1104
- DEBUG_PRINTF("ODBCResult::UV_Close - Exit\n");
1105
- }
1106
-
1107
- void ODBCResult::UV_AfterClose(uv_work_t *req, int status)
1108
- {
1109
- DEBUG_PRINTF("ODBCResult::UV_AfterClose - Entry\n");
1110
- close_result_work_data *data = (close_result_work_data *)(req->data);
1111
- Napi::Env env(data->env);
1112
- Napi::HandleScope scope(env);
1113
- ODBCResult *self = data->objResult->self();
1114
-
1115
- if (data->result != SQL_SUCCESS)
1116
- ODBC::CallbackSQLError(env, SQL_HANDLE_STMT, data->objResult->m_hSTMT, data->cb);
1117
- else
1118
- data->cb->Call({env.Null()});
1119
- PropagateCallbackException(env);
1120
-
1121
- self->Unref();
1122
- delete data->cb;
1123
- free(data);
1124
- free(req);
1125
- }
1126
-
1127
- /*
1128
- * CloseSync
1129
- */
1130
- Napi::Value ODBCResult::CloseSync(const Napi::CallbackInfo &info)
1131
- {
1132
- DEBUG_PRINTF("ODBCResult::CloseSync - Entry\n");
1133
- Napi::Env env = info.Env();
1134
- SQLUSMALLINT closeOption = SQL_DROP;
1135
-
1136
- if (info.Length() > 0 && info[0].IsNumber())
1137
- closeOption = (SQLUSMALLINT)info[0].As<Napi::Number>().Int32Value();
1138
-
1139
- if (closeOption == SQL_DROP && m_canFreeHandle)
1140
- this->Free();
1141
- else if (closeOption == SQL_DROP && !m_canFreeHandle)
1142
- SQLFreeStmt(m_hSTMT, SQL_CLOSE);
1143
- else
1144
- SQLFreeStmt(m_hSTMT, closeOption);
1145
-
1146
- return Napi::Boolean::New(env, true);
1147
- }
1148
-
1149
- /*
1150
- * MoreResultsSync
1151
- */
1152
- Napi::Value ODBCResult::MoreResultsSync(const Napi::CallbackInfo &info)
1153
- {
1154
- DEBUG_PRINTF("ODBCResult::MoreResultsSync\n");
1155
- Napi::Env env = info.Env();
1156
-
1157
- SQLRETURN ret = SQLMoreResults(m_hSTMT);
1158
-
1159
- if (ret == SQL_ERROR)
1160
- napi_throw(env, ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync"));
1161
-
1162
- return Napi::Boolean::New(env, SQL_SUCCEEDED(ret) || ret == SQL_ERROR);
1163
- }
1164
-
1165
- /*
1166
- * GetColumnNamesSync
1167
- */
1168
- Napi::Value ODBCResult::GetColumnNamesSync(const Napi::CallbackInfo &info)
1169
- {
1170
- DEBUG_PRINTF("ODBCResult::GetColumnNamesSync - Entry\n");
1171
- Napi::Env env = info.Env();
1172
-
1173
- if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
1174
-
1175
- Napi::Array cols = Napi::Array::New(env);
1176
- for (int i = 0; i < colCount; i++)
1177
- cols.Set((uint32_t)i, Napi::String::New(env, (const char *)columns[i].name));
1178
-
1179
- ODBC::FreeColumns(columns, &colCount);
1180
- return cols;
1181
- }
1182
-
1183
- /*
1184
- * GetColumnMetadataSync
1185
- */
1186
- Napi::Value ODBCResult::GetColumnMetadataSync(const Napi::CallbackInfo &info)
1187
- {
1188
- DEBUG_PRINTF("ODBCResult::GetColumnMetadataSync - Entry\n");
1189
- Napi::Env env = info.Env();
1190
-
1191
- if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
1192
-
1193
- Napi::Array result = Napi::Array::New(env);
1194
-
1195
- for (int i = 0; i < colCount; i++)
1196
- {
1197
- Napi::Object col = Napi::Object::New(env);
1198
- col.Set("index", Napi::Number::New(env, columns[i].index));
1199
- col.Set("SQL_DESC_NAME", Napi::String::New(env, (const char *)columns[i].name));
1200
- col.Set("SQL_DESC_TYPE_NAME", Napi::String::New(env, (const char *)columns[i].type_name));
1201
- col.Set("SQL_DESC_CONSIZE_TYPE", Napi::Number::New(env, (int32_t)columns[i].type));
1202
- col.Set("SQL_DESC_DISPLAY_SIZE", Napi::Number::New(env, columns[i].max_display_len));
1203
- col.Set("SQL_DESC_PRECISION", Napi::Number::New(env, columns[i].precision));
1204
- col.Set("SQL_DESC_SCALE", Napi::Number::New(env, columns[i].scale));
1205
- col.Set("SQL_DESC_LENGTH", Napi::Number::New(env, columns[i].field_len));
1206
- result.Set((uint32_t)i, col);
1207
- }
1208
-
1209
- ODBC::FreeColumns(columns, &colCount);
1210
- return result;
1211
- }
1212
-
1213
- /*
1214
- * GetSQLErrorSync
1215
- */
1216
- Napi::Value ODBCResult::GetSQLErrorSync(const Napi::CallbackInfo &info)
1217
- {
1218
- DEBUG_PRINTF("ODBCResult::GetSQLErrorSync\n");
1219
- Napi::Env env = info.Env();
1220
- return ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"");
1221
- }
1222
-
1223
- /*
1224
- * GetAffectedRowsSync
1225
- */
1226
- Napi::Value ODBCResult::GetAffectedRowsSync(const Napi::CallbackInfo &info)
1227
- {
1228
- DEBUG_PRINTF("ODBCResult::GetAffectedRowsSync\n");
1229
- Napi::Env env = info.Env();
1230
- SQLLEN rowCount = 0;
1231
- SQLRETURN ret = SQLRowCount(m_hSTMT, &rowCount);
1232
- if (!SQL_SUCCEEDED(ret)) rowCount = 0;
1233
- return Napi::Number::New(env, (double)rowCount);
1234
- }
1235
-
1236
- /*
1237
- * Block Fetch Support
1238
- */
1239
- #define BLOCK_FETCH_MAX_COL_SIZE 65536
1240
-
1241
- bool ODBCResult::InitBlockFetch()
1242
- {
1243
- if (m_blockFetchInitialized) return (m_rowArraySize > 1);
1244
- m_blockFetchInitialized = true;
1245
-
1246
- SQLULEN rowArraySize = 0;
1247
- SQLRETURN ret = SQLGetStmtAttr(m_hSTMT, SQL_ATTR_ROW_ARRAY_SIZE, &rowArraySize, SQL_IS_UINTEGER, NULL);
1248
- if (!SQL_SUCCEEDED(ret) || rowArraySize <= 1) { m_rowArraySize = 1; return false; }
1249
-
1250
- m_rowArraySize = rowArraySize;
1251
-
1252
- if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
1253
- if (colCount == 0) { m_rowArraySize = 1; return false; }
1254
-
1255
- m_rowStatusArray = (SQLUSMALLINT *)calloc(m_rowArraySize, sizeof(SQLUSMALLINT));
1256
- if (!m_rowStatusArray) { m_rowArraySize = 1; return false; }
1257
-
1258
- ret = SQLSetStmtAttr(m_hSTMT, SQL_ATTR_ROW_STATUS_PTR, m_rowStatusArray, 0);
1259
- if (!SQL_SUCCEEDED(ret)) { free(m_rowStatusArray); m_rowStatusArray = NULL; m_rowArraySize = 1; return false; }
1260
-
1261
- ret = SQLSetStmtAttr(m_hSTMT, SQL_ATTR_ROWS_FETCHED_PTR, &m_rowsFetched, 0);
1262
- if (!SQL_SUCCEEDED(ret)) { free(m_rowStatusArray); m_rowStatusArray = NULL; m_rowArraySize = 1; return false; }
1263
-
1264
- if (!BindColumnsForBlockFetch()) { FreeBlockFetchBuffers(); m_rowArraySize = 1; return false; }
1265
- return true;
1266
- }
1267
-
1268
- bool ODBCResult::BindColumnsForBlockFetch()
1269
- {
1270
- m_boundCols = (BoundColumn *)calloc(colCount, sizeof(BoundColumn));
1271
- if (!m_boundCols) return false;
1272
-
1273
- for (int i = 0; i < colCount; i++)
1274
- {
1275
- SQLSMALLINT cType;
1276
- SQLLEN elemSize;
1277
-
1278
- switch (columns[i].type)
1279
- {
1280
- case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT:
1281
- cType = SQL_C_SLONG; elemSize = sizeof(SQLINTEGER); break;
1282
- case SQL_BIGINT:
1283
- cType = SQL_C_CHAR; elemSize = 22; break;
1284
- case SQL_FLOAT: case SQL_REAL: case SQL_DOUBLE: case SQL_NUMERIC: case SQL_DECIMAL: case SQL_DECFLOAT:
1285
- cType = SQL_C_DOUBLE; elemSize = sizeof(double); break;
1286
- case SQL_BIT:
1287
- cType = SQL_C_CHAR; elemSize = 4; break;
1288
- case SQL_DATETIME: case SQL_TIMESTAMP: case SQL_TYPE_TIMESTAMP:
1289
- #ifdef _WIN32
1290
- cType = SQL_C_CHAR; elemSize = sizeof(SQL_TIMESTAMP_STRUCT); break;
1291
- #else
1292
- cType = SQL_C_TYPE_TIMESTAMP; elemSize = sizeof(SQL_TIMESTAMP_STRUCT); break;
1293
- #endif
1294
- case SQL_BLOB: case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY:
1295
- {
1296
- cType = SQL_C_BINARY;
1297
- SQLLEN colSize = columns[i].field_len;
1298
- if (colSize <= 0 || colSize > BLOCK_FETCH_MAX_COL_SIZE) colSize = BLOCK_FETCH_MAX_COL_SIZE;
1299
- elemSize = colSize;
1300
- break;
1301
- }
1302
- default:
1303
- {
1304
- cType = SQL_C_CHAR;
1305
- #ifdef UNICODE
1306
- cType = SQL_C_WCHAR;
1307
- #endif
1308
- SQLLEN colSize = columns[i].field_len;
1309
- if (colSize <= 0 || colSize > BLOCK_FETCH_MAX_COL_SIZE) colSize = BLOCK_FETCH_MAX_COL_SIZE;
1310
- #ifdef UNICODE
1311
- elemSize = (colSize + 1) * sizeof(uint16_t);
1312
- #else
1313
- elemSize = colSize + 1;
1314
- #endif
1315
- break;
1316
- }
1317
- }
1318
-
1319
- m_boundCols[i].cType = cType;
1320
- m_boundCols[i].elementSize = elemSize;
1321
- m_boundCols[i].data = calloc(m_rowArraySize, elemSize);
1322
- if (!m_boundCols[i].data) return false;
1323
- m_boundCols[i].indicators = (SQLLEN *)calloc(m_rowArraySize, sizeof(SQLLEN));
1324
- if (!m_boundCols[i].indicators) return false;
1325
-
1326
- SQLRETURN ret = SQLBindCol(m_hSTMT, (SQLUSMALLINT)(i + 1), cType, m_boundCols[i].data, elemSize, m_boundCols[i].indicators);
1327
- if (!SQL_SUCCEEDED(ret)) return false;
1328
- }
1329
- return true;
1330
- }
1331
-
1332
- void ODBCResult::FreeBlockFetchBuffers()
1333
- {
1334
- if (m_boundCols)
1335
- {
1336
- for (int i = 0; i < colCount; i++)
1337
- {
1338
- if (m_boundCols[i].data) { free(m_boundCols[i].data); m_boundCols[i].data = NULL; }
1339
- if (m_boundCols[i].indicators) { free(m_boundCols[i].indicators); m_boundCols[i].indicators = NULL; }
1340
- }
1341
- free(m_boundCols); m_boundCols = NULL;
1342
- }
1343
- if (m_rowStatusArray) { free(m_rowStatusArray); m_rowStatusArray = NULL; }
1344
- m_blockFetchInitialized = false;
1345
- m_rowArraySize = 0;
1346
- }
1347
-
1348
- Napi::Value ODBCResult::GetBoundColumnValue(Napi::Env env, int colIndex, SQLULEN rowIndex)
1349
- {
1350
- if (m_fileColBindings && colIndex < m_fileColCount && m_fileColBindings[colIndex].bound)
1351
- {
1352
- FileColumnBinding *fcb = &m_fileColBindings[colIndex];
1353
- if (fcb->indicator == SQL_NULL_DATA) return env.Null();
1354
- return Napi::String::New(env, fcb->fileName);
1355
- }
1356
-
1357
- BoundColumn *bc = &m_boundCols[colIndex];
1358
- SQLLEN indicator = bc->indicators[rowIndex];
1359
- if (indicator == SQL_NULL_DATA) return env.Null();
1360
-
1361
- char *rowData = (char *)bc->data + (rowIndex * bc->elementSize);
1362
-
1363
- switch (columns[colIndex].type)
1364
- {
1365
- case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT:
1366
- return Napi::Number::New(env, (int)(*(SQLINTEGER *)rowData));
1367
-
1368
- case SQL_BIGINT:
1369
- return Napi::String::New(env, (const char *)rowData);
1370
-
1371
- case SQL_FLOAT: case SQL_REAL: case SQL_DOUBLE: case SQL_NUMERIC: case SQL_DECIMAL: case SQL_DECFLOAT:
1372
- return Napi::Number::New(env, *(double *)rowData);
1373
-
1374
- case SQL_BIT:
1375
- return Napi::Boolean::New(env, (*rowData != '0'));
1376
-
1377
- case SQL_DATETIME: case SQL_TIMESTAMP: case SQL_TYPE_TIMESTAMP:
1378
- {
1379
- SQL_TIMESTAMP_STRUCT *ts = (SQL_TIMESTAMP_STRUCT *)rowData;
1380
- #ifdef _WIN32
1381
- struct tm timeInfo = {};
1382
- #elif defined(_AIX) || defined(__MVS__)
1383
- struct tm timeInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0};
1384
- #else
1385
- struct tm timeInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1386
- #endif
1387
- timeInfo.tm_year = ts->year - 1900;
1388
- timeInfo.tm_mon = ts->month - 1;
1389
- timeInfo.tm_mday = ts->day;
1390
- timeInfo.tm_hour = ts->hour;
1391
- timeInfo.tm_min = ts->minute;
1392
- timeInfo.tm_sec = ts->second;
1393
- timeInfo.tm_isdst = -1;
1394
-
1395
- #ifdef TIMEGM
1396
- double msec = (double(timegm(&timeInfo)) * 1000) + (ts->fraction / 1000000);
1397
- #else
1398
- double msec = (double(mktime(&timeInfo)) * 1000) + (ts->fraction / 1000000);
1399
- #endif
1400
- return Napi::Date::New(env, msec);
1401
- }
1402
-
1403
- case SQL_BLOB: case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY:
1404
- {
1405
- SQLLEN dataLen = indicator;
1406
- if (dataLen > bc->elementSize) dataLen = bc->elementSize;
1407
- return Napi::Buffer<char>::Copy(env, rowData, (size_t)dataLen);
1408
- }
1409
-
1410
- default:
1411
- {
1412
- #ifdef UNICODE
1413
- return Napi::String::New(env, (const char16_t *)rowData);
1414
- #else
1415
- return Napi::String::New(env, (const char *)rowData);
1416
- #endif
1417
- }
1418
- }
1419
- }
1420
-
1421
- SQLRETURN ODBCResult::BlockFetchNextRow(SQLULEN *outRowIndex)
1422
- {
1423
- if (m_blockExhausted) return SQL_NO_DATA;
1424
-
1425
- while (m_currentRowInBlock < m_rowsFetched)
1426
- {
1427
- SQLUSMALLINT st = m_rowStatusArray[m_currentRowInBlock];
1428
- if (st == SQL_ROW_SUCCESS || st == SQL_ROW_SUCCESS_WITH_INFO)
1429
- {
1430
- *outRowIndex = m_currentRowInBlock;
1431
- m_currentRowInBlock++;
1432
- return SQL_SUCCESS;
1433
- }
1434
- m_currentRowInBlock++;
1435
- }
1436
-
1437
- m_rowsFetched = 0;
1438
- m_currentRowInBlock = 0;
1439
- SQLRETURN ret = SQLFetch(m_hSTMT);
1440
-
1441
- if (ret == SQL_NO_DATA) { m_blockExhausted = true; return SQL_NO_DATA; }
1442
- if (ret == SQL_ERROR) return SQL_ERROR;
1443
-
1444
- while (m_currentRowInBlock < m_rowsFetched)
1445
- {
1446
- SQLUSMALLINT st = m_rowStatusArray[m_currentRowInBlock];
1447
- if (st == SQL_ROW_SUCCESS || st == SQL_ROW_SUCCESS_WITH_INFO)
1448
- {
1449
- *outRowIndex = m_currentRowInBlock;
1450
- m_currentRowInBlock++;
1451
- return SQL_SUCCESS;
1452
- }
1453
- m_currentRowInBlock++;
1454
- }
1455
-
1456
- return BlockFetchNextRow(outRowIndex);
1457
- }
1458
-
1459
- /*
1460
- * BindFileToColSync
1461
- */
1462
- Napi::Value ODBCResult::BindFileToColSync(const Napi::CallbackInfo &info)
1463
- {
1464
- DEBUG_PRINTF("ODBCResult::BindFileToColSync - Entry\n");
1465
- Napi::Env env = info.Env();
1466
-
1467
- if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsString())
1468
- {
1469
- Napi::TypeError::New(env, "bindFileToColSync(colNum, filePath[, fileOption]): colNum (integer) and filePath (string) are required.").ThrowAsJavaScriptException();
1470
- return env.Undefined();
1471
- }
1472
-
1473
- int colNum = info[0].As<Napi::Number>().Int32Value();
1474
- std::string filePath = info[1].As<Napi::String>().Utf8Value();
1475
- int filePathLen = (int)filePath.length();
1476
-
1477
- if (colNum < 1) { Napi::RangeError::New(env, "colNum must be >= 1").ThrowAsJavaScriptException(); return env.Undefined(); }
1478
- if (filePathLen <= 0 || filePathLen >= FILE_COL_MAX_PATH) { Napi::RangeError::New(env, "filePath is empty or exceeds max path length").ThrowAsJavaScriptException(); return env.Undefined(); }
1479
-
1480
- SQLUINTEGER fileOption = SQL_FILE_OVERWRITE;
1481
- if (info.Length() >= 3 && info[2].IsNumber())
1482
- fileOption = info[2].As<Napi::Number>().Uint32Value();
1483
-
1484
- if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
1485
- if (colNum > colCount) { Napi::RangeError::New(env, "colNum exceeds the number of columns in the result set").ThrowAsJavaScriptException(); return env.Undefined(); }
1486
-
1487
- if (!m_fileColBindings)
1488
- {
1489
- m_fileColBindings = (FileColumnBinding *)calloc(colCount, sizeof(FileColumnBinding));
1490
- if (!m_fileColBindings) { Napi::Error::New(env, "Failed to allocate memory for file column bindings").ThrowAsJavaScriptException(); return env.Undefined(); }
1491
- m_fileColCount = colCount;
1492
- }
1493
-
1494
- FileColumnBinding *fcb = &m_fileColBindings[colNum - 1];
1495
- memset(fcb, 0, sizeof(FileColumnBinding));
1496
- strncpy(fcb->fileName, filePath.c_str(), FILE_COL_MAX_PATH - 1);
1497
- fcb->fileName[FILE_COL_MAX_PATH - 1] = '\0';
1498
- fcb->fileNameLength = (SQLSMALLINT)filePathLen;
1499
- fcb->fileOption = fileOption;
1500
- fcb->stringLength = 0;
1501
- fcb->indicator = 0;
1502
-
1503
- SQLRETURN ret = SQLBindFileToCol(m_hSTMT, (SQLUSMALLINT)colNum,
1504
- (SQLCHAR *)fcb->fileName, &fcb->fileNameLength, &fcb->fileOption,
1505
- (SQLSMALLINT)FILE_COL_MAX_PATH, &fcb->stringLength, &fcb->indicator);
1506
-
1507
- if (!SQL_SUCCEEDED(ret))
1508
- {
1509
- napi_throw(env, ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"Error in ODBCResult::BindFileToColSync"));
1510
- return env.Undefined();
1511
- }
1512
-
1513
- fcb->bound = true;
1514
- columns[colNum - 1].isFileBound = true;
1515
- return Napi::Boolean::New(env, true);
1516
- }
1517
-
1518
- /*
1519
- * OverrideFileColumns
1520
- */
1521
- void ODBCResult::OverrideFileColumns(Napi::Env env, Napi::Value row, int fetchMode)
1522
- {
1523
- if (!m_fileColBindings || !row.IsObject() || row.IsNull()) return;
1524
-
1525
- Napi::Object obj = row.As<Napi::Object>();
1526
-
1527
- for (int i = 0; i < m_fileColCount && i < colCount; i++)
1528
- {
1529
- FileColumnBinding *fcb = &m_fileColBindings[i];
1530
- if (!fcb->bound) continue;
1531
-
1532
- Napi::Value val;
1533
- if (fcb->indicator == SQL_NULL_DATA) val = env.Null();
1534
- else val = Napi::String::New(env, fcb->fileName);
1535
-
1536
- if (fetchMode == FETCH_ARRAY)
1537
- obj.Set((uint32_t)i, val);
1538
- else
1539
- {
1540
- #ifdef UNICODE
1541
- obj.Set(Napi::String::New(env, (const char16_t *)columns[i].name), val);
1542
- #else
1543
- obj.Set(Napi::String::New(env, (const char *)columns[i].name), val);
1544
- #endif
1545
- }
1546
- }
1547
- }
1
+ /*
2
+ Copyright (c) 2013, Dan VerWeire <dverweire@gmail.com>
3
+
4
+ Permission to use, copy, modify, and/or distribute this software for any
5
+ purpose with or without fee is hereby granted, provided that the above
6
+ copyright notice and this permission notice appear in all copies.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
+ */
16
+
17
+ #include <string.h>
18
+ #include <time.h>
19
+ #include <uv.h>
20
+
21
+ #include "odbc.h"
22
+ #include "odbc_connection.h"
23
+ #include "odbc_result.h"
24
+ #include "odbc_statement.h"
25
+
26
+ Napi::FunctionReference ODBCResult::constructor;
27
+
28
+ Napi::Object ODBCResult::Init(Napi::Env env, Napi::Object exports)
29
+ {
30
+ DEBUG_PRINTF("ODBCResult::Init\n");
31
+
32
+ Napi::Function func = DefineClass(env, "ODBCResult", {
33
+ InstanceMethod("fetchAll", &ODBCResult::FetchAll, NAPI_METHOD_ATTR),
34
+ InstanceMethod("fetch", &ODBCResult::Fetch, NAPI_METHOD_ATTR),
35
+ InstanceMethod("getData", &ODBCResult::GetData, NAPI_METHOD_ATTR),
36
+ InstanceMethod("moreResultsSync", &ODBCResult::MoreResultsSync, NAPI_METHOD_ATTR),
37
+ InstanceMethod("close", &ODBCResult::Close, NAPI_METHOD_ATTR),
38
+ InstanceMethod("closeSync", &ODBCResult::CloseSync, NAPI_METHOD_ATTR),
39
+ InstanceMethod("fetchSync", &ODBCResult::FetchSync, NAPI_METHOD_ATTR),
40
+ InstanceMethod("fetchAllSync", &ODBCResult::FetchAllSync, NAPI_METHOD_ATTR),
41
+ InstanceMethod("fetchN", &ODBCResult::FetchN, NAPI_METHOD_ATTR),
42
+ InstanceMethod("fetchNSync", &ODBCResult::FetchNSync, NAPI_METHOD_ATTR),
43
+ InstanceMethod("getDataSync", &ODBCResult::GetDataSync, NAPI_METHOD_ATTR),
44
+ InstanceMethod("getColumnNamesSync", &ODBCResult::GetColumnNamesSync, NAPI_METHOD_ATTR),
45
+ InstanceMethod("getColumnMetadataSync", &ODBCResult::GetColumnMetadataSync, NAPI_METHOD_ATTR),
46
+ InstanceMethod("getSQLErrorSync", &ODBCResult::GetSQLErrorSync, NAPI_METHOD_ATTR),
47
+ InstanceMethod("getAffectedRowsSync", &ODBCResult::GetAffectedRowsSync, NAPI_METHOD_ATTR),
48
+ InstanceMethod("bindFileToColSync", &ODBCResult::BindFileToColSync, NAPI_METHOD_ATTR),
49
+ InstanceAccessor("fetchMode", &ODBCResult::FetchModeGetter, &ODBCResult::FetchModeSetter, napi_enumerable),
50
+ });
51
+
52
+ constructor = Napi::Persistent(func);
53
+ constructor.SuppressDestruct();
54
+ exports.Set("ODBCResult", func);
55
+ return exports;
56
+ }
57
+
58
+ ODBCResult::~ODBCResult()
59
+ {
60
+ DEBUG_PRINTF("ODBCResult::~ODBCResult m_hSTMT=%X m_canFreeHandle=%d\n", m_hSTMT, m_canFreeHandle);
61
+ this->Free();
62
+ }
63
+
64
+ void ODBCResult::Free()
65
+ {
66
+ FreeBlockFetchBuffers();
67
+
68
+ if (m_fileColBindings) { free(m_fileColBindings); m_fileColBindings = NULL; m_fileColCount = 0; }
69
+ if (m_hSTMT && m_canFreeHandle) {
70
+ SQLFreeHandle(SQL_HANDLE_STMT, m_hSTMT);
71
+ m_hSTMT = (SQLHSTMT)NULL; m_canFreeHandle = 0;
72
+ }
73
+ if (buffer != NULL) { free((uint16_t *)buffer); buffer = NULL; }
74
+ bufferLength = 0;
75
+ }
76
+
77
+ ODBCResult::ODBCResult(const Napi::CallbackInfo &info) : Napi::ObjectWrap<ODBCResult>(info)
78
+ {
79
+ DEBUG_PRINTF("ODBCResult::New\n");
80
+ Napi::Env env = info.Env();
81
+
82
+ if (info.Length() < 4 || !info[0].IsExternal() || !info[1].IsExternal() || !info[2].IsExternal() || !info[3].IsExternal())
83
+ {
84
+ Napi::TypeError::New(env, "ODBCResult::New requires 4 external arguments").ThrowAsJavaScriptException();
85
+ return;
86
+ }
87
+
88
+ m_hENV = (SQLHENV)((intptr_t)info[0].As<Napi::External<void>>().Data());
89
+ m_hDBC = (SQLHDBC)((intptr_t)info[1].As<Napi::External<void>>().Data());
90
+ m_hSTMT = (SQLHSTMT)((intptr_t)info[2].As<Napi::External<void>>().Data());
91
+ bool *canFreeHandle = static_cast<bool *>(info[3].As<Napi::External<void>>().Data());
92
+ m_canFreeHandle = *canFreeHandle;
93
+ delete canFreeHandle;
94
+
95
+ bufferLength = MAX_VALUE_SIZE;
96
+ buffer = NULL;
97
+ colCount = 0;
98
+ m_fetchMode = FETCH_OBJECT;
99
+
100
+ m_rowArraySize = 0;
101
+ m_rowsFetched = 0;
102
+ m_currentRowInBlock = 0;
103
+ m_rowStatusArray = NULL;
104
+ m_boundCols = NULL;
105
+ m_blockFetchInitialized = false;
106
+ m_blockExhausted = false;
107
+ m_fileColBindings = NULL;
108
+ m_fileColCount = 0;
109
+ }
110
+
111
+ Napi::Value ODBCResult::FetchModeGetter(const Napi::CallbackInfo &info)
112
+ {
113
+ return Napi::Number::New(info.Env(), m_fetchMode);
114
+ }
115
+
116
+ void ODBCResult::FetchModeSetter(const Napi::CallbackInfo &info, const Napi::Value &value)
117
+ {
118
+ if (value.IsNumber()) m_fetchMode = value.As<Napi::Number>().Int32Value();
119
+ }
120
+
121
+ /*
122
+ * Fetch
123
+ */
124
+ Napi::Value ODBCResult::Fetch(const Napi::CallbackInfo &info)
125
+ {
126
+ DEBUG_PRINTF("ODBCResult::Fetch\n");
127
+ Napi::Env env = info.Env();
128
+
129
+ uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
130
+ MEMCHECK(work_req);
131
+ fetch_work_data *data = (fetch_work_data *)calloc(1, sizeof(fetch_work_data));
132
+ if (!data) free(work_req);
133
+ MEMCHECK(data);
134
+
135
+ Napi::Function cb;
136
+ data->fetchMode = m_fetchMode;
137
+
138
+ if (info.Length() == 1 && info[0].IsFunction())
139
+ {
140
+ cb = info[0].As<Napi::Function>();
141
+ }
142
+ else if (info.Length() == 2 && info[0].IsObject() && info[1].IsFunction())
143
+ {
144
+ cb = info[1].As<Napi::Function>();
145
+ Napi::Object obj = info[0].As<Napi::Object>();
146
+ if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
147
+ data->fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
148
+ }
149
+ else
150
+ {
151
+ free(data); free(work_req);
152
+ Napi::TypeError::New(env, "ODBCResult::Fetch(): 1 or 2 arguments are required. The last argument must be a callback function.").ThrowAsJavaScriptException();
153
+ return env.Undefined();
154
+ }
155
+
156
+ data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
157
+ data->objResult = this;
158
+ data->useBlockFetch = false;
159
+ data->blockRowIndex = 0;
160
+ data->env = env;
161
+ work_req->data = data;
162
+
163
+ uv_queue_work(uv_default_loop(), work_req, UV_Fetch, (uv_after_work_cb)UV_AfterFetch);
164
+ this->Ref();
165
+ return env.Undefined();
166
+ }
167
+
168
+ void ODBCResult::UV_Fetch(uv_work_t *work_req)
169
+ {
170
+ DEBUG_PRINTF("ODBCResult::UV_Fetch\n");
171
+ fetch_work_data *data = (fetch_work_data *)(work_req->data);
172
+
173
+ bool useBlockFetch = data->objResult->InitBlockFetch();
174
+ data->useBlockFetch = useBlockFetch;
175
+
176
+ if (useBlockFetch)
177
+ {
178
+ ODBCResult *self = data->objResult;
179
+ if (self->m_blockExhausted)
180
+ {
181
+ data->result = SQL_NO_DATA;
182
+ }
183
+ else
184
+ {
185
+ self->m_rowsFetched = 0;
186
+ self->m_currentRowInBlock = 0;
187
+ data->result = SQLFetch(self->m_hSTMT);
188
+ if (data->result == SQL_NO_DATA) self->m_blockExhausted = true;
189
+ }
190
+ }
191
+ else
192
+ {
193
+ data->result = SQLFetch(data->objResult->m_hSTMT);
194
+ }
195
+ }
196
+
197
+ void ODBCResult::UV_AfterFetch(uv_work_t *work_req, int status)
198
+ {
199
+ DEBUG_PRINTF("ODBCResult::UV_AfterFetch\n");
200
+ fetch_work_data *data = (fetch_work_data *)(work_req->data);
201
+ Napi::Env env(data->env);
202
+ Napi::HandleScope scope(env);
203
+
204
+ SQLRETURN ret = data->result;
205
+ Napi::Value objError;
206
+ bool moreWork = true;
207
+ bool error = false;
208
+
209
+ if (data->objResult->colCount == 0)
210
+ data->objResult->columns = ODBC::GetColumns(data->objResult->m_hSTMT, &data->objResult->colCount);
211
+
212
+ if (data->objResult->colCount == 0 || data->fetchMode == FETCH_NODATA)
213
+ moreWork = false;
214
+ else if (ret == SQL_ERROR)
215
+ {
216
+ moreWork = false; error = true;
217
+ objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, data->objResult->m_hSTMT, (char *)"Error in ODBCResult::UV_AfterFetch");
218
+ }
219
+ else if (ret == SQL_NO_DATA)
220
+ moreWork = false;
221
+
222
+ if (moreWork)
223
+ {
224
+ Napi::Value rowData;
225
+ if (data->useBlockFetch)
226
+ {
227
+ ODBCResult *self = data->objResult->self();
228
+ Napi::Array blockArray = Napi::Array::New(env);
229
+ int validCount = 0;
230
+ for (SQLULEN r = 0; r < self->m_rowsFetched; r++)
231
+ {
232
+ SQLUSMALLINT st = self->m_rowStatusArray[r];
233
+ if (st == SQL_ROW_SUCCESS || st == SQL_ROW_SUCCESS_WITH_INFO)
234
+ {
235
+ if (data->fetchMode == FETCH_ARRAY)
236
+ {
237
+ Napi::Array array = Napi::Array::New(env);
238
+ for (int c = 0; c < self->colCount; c++)
239
+ array.Set((uint32_t)c, self->GetBoundColumnValue(env, c, r));
240
+ blockArray.Set((uint32_t)validCount++, array);
241
+ }
242
+ else
243
+ {
244
+ Napi::Object tuple = Napi::Object::New(env);
245
+ for (int c = 0; c < self->colCount; c++)
246
+ {
247
+ #ifdef UNICODE
248
+ tuple.Set(Napi::String::New(env, (const char16_t *)self->columns[c].name), self->GetBoundColumnValue(env, c, r));
249
+ #else
250
+ tuple.Set(Napi::String::New(env, (const char *)self->columns[c].name), self->GetBoundColumnValue(env, c, r));
251
+ #endif
252
+ }
253
+ blockArray.Set((uint32_t)validCount++, tuple);
254
+ }
255
+ }
256
+ }
257
+ rowData = blockArray;
258
+ }
259
+ else if (data->fetchMode == FETCH_ARRAY)
260
+ {
261
+ rowData = ODBC::GetRecordArray(env, data->objResult->m_hSTMT, data->objResult->columns,
262
+ &data->objResult->colCount, data->objResult->buffer, data->objResult->bufferLength);
263
+ }
264
+ else
265
+ {
266
+ rowData = ODBC::GetRecordTuple(env, data->objResult->m_hSTMT, data->objResult->columns,
267
+ &data->objResult->colCount, data->objResult->buffer, data->objResult->bufferLength);
268
+ }
269
+ data->objResult->OverrideFileColumns(env, rowData, data->fetchMode);
270
+ data->cb->Call({env.Null(), rowData});
271
+ }
272
+ else
273
+ {
274
+ if (data->fetchMode != FETCH_NODATA)
275
+ {
276
+ ODBC::FreeColumns(data->objResult->columns, &data->objResult->colCount);
277
+ }
278
+ FREE(data->objResult->buffer);
279
+
280
+ if (error)
281
+ data->cb->Call({objError, env.Null()});
282
+ else
283
+ data->cb->Call({env.Null(), env.Null()});
284
+ }
285
+ PropagateCallbackException(env);
286
+
287
+ data->objResult->Unref();
288
+ delete data->cb;
289
+ free(data);
290
+ free(work_req);
291
+ }
292
+
293
+ /*
294
+ * FetchSync
295
+ */
296
+ Napi::Value ODBCResult::FetchSync(const Napi::CallbackInfo &info)
297
+ {
298
+ DEBUG_PRINTF("ODBCResult::FetchSync - Entry\n");
299
+ Napi::Env env = info.Env();
300
+
301
+ Napi::Value objError;
302
+ bool moreWork = true;
303
+ bool error = false;
304
+ int fetchMode = m_fetchMode;
305
+
306
+ if (info.Length() == 1 && info[0].IsObject())
307
+ {
308
+ Napi::Object obj = info[0].As<Napi::Object>();
309
+ if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
310
+ fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
311
+ }
312
+
313
+ if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
314
+
315
+ bool useBlockFetch = InitBlockFetch();
316
+ SQLRETURN ret;
317
+
318
+ if (useBlockFetch)
319
+ {
320
+ if (m_blockExhausted) ret = SQL_NO_DATA;
321
+ else { m_rowsFetched = 0; m_currentRowInBlock = 0; ret = SQLFetch(m_hSTMT); }
322
+ }
323
+ else
324
+ ret = SQLFetch(m_hSTMT);
325
+
326
+ if (ret == SQL_ERROR)
327
+ {
328
+ moreWork = false; error = true;
329
+ objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"Error in ODBCResult::FetchSync");
330
+ }
331
+ else if (ret == SQL_NO_DATA)
332
+ {
333
+ if (useBlockFetch) m_blockExhausted = true;
334
+ moreWork = false;
335
+ }
336
+ else if (colCount == 0)
337
+ moreWork = false;
338
+
339
+ if (!error && fetchMode == FETCH_NODATA) return env.Null();
340
+
341
+ if (moreWork)
342
+ {
343
+ Napi::Value data;
344
+ if (useBlockFetch)
345
+ {
346
+ Napi::Array blockArray = Napi::Array::New(env);
347
+ int validCount = 0;
348
+ for (SQLULEN r = 0; r < m_rowsFetched; r++)
349
+ {
350
+ SQLUSMALLINT st = m_rowStatusArray[r];
351
+ if (st == SQL_ROW_SUCCESS || st == SQL_ROW_SUCCESS_WITH_INFO)
352
+ {
353
+ if (fetchMode == FETCH_ARRAY)
354
+ {
355
+ Napi::Array array = Napi::Array::New(env);
356
+ for (int c = 0; c < colCount; c++) array.Set((uint32_t)c, GetBoundColumnValue(env, c, r));
357
+ blockArray.Set((uint32_t)validCount++, array);
358
+ }
359
+ else
360
+ {
361
+ Napi::Object tuple = Napi::Object::New(env);
362
+ for (int c = 0; c < colCount; c++)
363
+ {
364
+ #ifdef UNICODE
365
+ tuple.Set(Napi::String::New(env, (const char16_t *)columns[c].name), GetBoundColumnValue(env, c, r));
366
+ #else
367
+ tuple.Set(Napi::String::New(env, (const char *)columns[c].name), GetBoundColumnValue(env, c, r));
368
+ #endif
369
+ }
370
+ blockArray.Set((uint32_t)validCount++, tuple);
371
+ }
372
+ }
373
+ }
374
+ data = blockArray;
375
+ }
376
+ else if (fetchMode == FETCH_ARRAY)
377
+ data = ODBC::GetRecordArray(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
378
+ else
379
+ data = ODBC::GetRecordTuple(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
380
+
381
+ OverrideFileColumns(env, data, fetchMode);
382
+ return data;
383
+ }
384
+ else
385
+ {
386
+ ODBC::FreeColumns(columns, &colCount);
387
+ FREE(buffer);
388
+ if (error)
389
+ {
390
+ napi_throw(env, objError);
391
+ return env.Null();
392
+ }
393
+ return env.Null();
394
+ }
395
+ }
396
+
397
+ /*
398
+ * FetchAll
399
+ */
400
+ Napi::Value ODBCResult::FetchAll(const Napi::CallbackInfo &info)
401
+ {
402
+ DEBUG_PRINTF("ODBCResult::FetchAll - Entry\n");
403
+ Napi::Env env = info.Env();
404
+
405
+ uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
406
+ MEMCHECK(work_req);
407
+ fetch_work_data *data = (fetch_work_data *)calloc(1, sizeof(fetch_work_data));
408
+ if (!data) free(work_req);
409
+ MEMCHECK(data);
410
+
411
+ Napi::Function cb;
412
+ data->fetchMode = m_fetchMode;
413
+
414
+ if (info.Length() == 1 && info[0].IsFunction())
415
+ {
416
+ cb = info[0].As<Napi::Function>();
417
+ }
418
+ else if (info.Length() == 2 && info[0].IsObject() && info[1].IsFunction())
419
+ {
420
+ cb = info[1].As<Napi::Function>();
421
+ Napi::Object obj = info[0].As<Napi::Object>();
422
+ if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
423
+ data->fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
424
+ }
425
+ else
426
+ {
427
+ free(data); free(work_req);
428
+ Napi::TypeError::New(env, "ODBCResult::FetchAll(): 1 or 2 arguments are required. The last argument must be a callback function.").ThrowAsJavaScriptException();
429
+ return env.Undefined();
430
+ }
431
+
432
+ data->rows = new Napi::Reference<Napi::Array>(Napi::Persistent(Napi::Array::New(env)));
433
+ data->errorCount = 0;
434
+ data->count = 0;
435
+ data->objError = new Napi::Reference<Napi::Object>(Napi::Persistent(Napi::Object::New(env)));
436
+
437
+ data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
438
+ data->objResult = this;
439
+ data->useBlockFetch = false;
440
+ data->blockRowIndex = 0;
441
+ data->env = env;
442
+ work_req->data = data;
443
+
444
+ uv_queue_work(uv_default_loop(), work_req, UV_FetchAll, (uv_after_work_cb)UV_AfterFetchAll);
445
+ this->Ref();
446
+ return env.Undefined();
447
+ }
448
+
449
+ void ODBCResult::UV_FetchAll(uv_work_t *work_req)
450
+ {
451
+ DEBUG_PRINTF("ODBCResult::UV_FetchAll - Entry\n");
452
+ fetch_work_data *data = (fetch_work_data *)(work_req->data);
453
+
454
+ bool useBlockFetch = data->objResult->InitBlockFetch();
455
+ data->useBlockFetch = useBlockFetch;
456
+
457
+ if (useBlockFetch)
458
+ {
459
+ SQLULEN rowIdx = 0;
460
+ data->result = data->objResult->BlockFetchNextRow(&rowIdx);
461
+ data->blockRowIndex = rowIdx;
462
+ }
463
+ else
464
+ data->result = SQLFetch(data->objResult->m_hSTMT);
465
+ }
466
+
467
+ void ODBCResult::UV_AfterFetchAll(uv_work_t *work_req, int status)
468
+ {
469
+ DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll - Entry\n");
470
+ fetch_work_data *data = (fetch_work_data *)(work_req->data);
471
+ Napi::Env env(data->env);
472
+ Napi::HandleScope scope(env);
473
+
474
+ ODBCResult *self = data->objResult->self();
475
+ bool doMoreWork = true;
476
+
477
+ if (data->result == SQL_ERROR)
478
+ {
479
+ data->errorCount++;
480
+ data->objError->Reset(ODBC::GetSQLError(env, SQL_HANDLE_STMT, self->m_hSTMT,
481
+ (char *)"[node-odbc] Error in ODBCResult::UV_AfterFetchAll").As<Napi::Object>());
482
+ }
483
+
484
+ if (self->colCount == 0) self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount);
485
+
486
+ bool noDataFetchQuery = false;
487
+ if (self->colCount == 0) { noDataFetchQuery = true; doMoreWork = false; }
488
+ else if (data->result == SQL_NO_DATA) doMoreWork = false;
489
+ else if (data->result == SQL_ERROR) doMoreWork = false;
490
+ else
491
+ {
492
+ Napi::Array rows = data->rows->Value();
493
+
494
+ if (data->useBlockFetch)
495
+ {
496
+ SQLULEN rowIdx = data->blockRowIndex;
497
+ if (data->fetchMode == FETCH_ARRAY)
498
+ {
499
+ Napi::Array array = Napi::Array::New(env);
500
+ for (int c = 0; c < self->colCount; c++)
501
+ array.Set((uint32_t)c, self->GetBoundColumnValue(env, c, rowIdx));
502
+ rows.Set((uint32_t)data->count, array);
503
+ }
504
+ else
505
+ {
506
+ Napi::Object tuple = Napi::Object::New(env);
507
+ for (int c = 0; c < self->colCount; c++)
508
+ {
509
+ #ifdef UNICODE
510
+ tuple.Set(Napi::String::New(env, (const char16_t *)self->columns[c].name), self->GetBoundColumnValue(env, c, rowIdx));
511
+ #else
512
+ tuple.Set(Napi::String::New(env, (const char *)self->columns[c].name), self->GetBoundColumnValue(env, c, rowIdx));
513
+ #endif
514
+ }
515
+ rows.Set((uint32_t)data->count, tuple);
516
+ }
517
+ }
518
+ else if (data->fetchMode == FETCH_ARRAY)
519
+ {
520
+ Napi::Value rowVal = ODBC::GetRecordArray(env, self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength);
521
+ self->OverrideFileColumns(env, rowVal, data->fetchMode);
522
+ rows.Set((uint32_t)data->count, rowVal);
523
+ }
524
+ else
525
+ {
526
+ Napi::Value rowVal = ODBC::GetRecordTuple(env, self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength);
527
+ self->OverrideFileColumns(env, rowVal, data->fetchMode);
528
+ rows.Set((uint32_t)data->count, rowVal);
529
+ }
530
+ data->count++;
531
+ }
532
+
533
+ if (doMoreWork)
534
+ {
535
+ uv_queue_work(uv_default_loop(), work_req, UV_FetchAll, (uv_after_work_cb)UV_AfterFetchAll);
536
+ }
537
+ else
538
+ {
539
+ Napi::Value err;
540
+ if (data->errorCount > 0 && !noDataFetchQuery)
541
+ err = data->objError->Value();
542
+ else
543
+ err = env.Null();
544
+
545
+ data->cb->Call({err, data->rows->Value(), Napi::Number::New(env, self->colCount)});
546
+ PropagateCallbackException(env);
547
+
548
+ ODBC::FreeColumns(self->columns, &self->colCount);
549
+ FREE(self->buffer);
550
+ delete data->cb;
551
+ delete data->rows;
552
+ delete data->objError;
553
+ free(data);
554
+ free(work_req);
555
+ self->Unref();
556
+ }
557
+ }
558
+
559
+ /*
560
+ * FetchAllSync
561
+ */
562
+ Napi::Value ODBCResult::FetchAllSync(const Napi::CallbackInfo &info)
563
+ {
564
+ DEBUG_PRINTF("ODBCResult::FetchAllSync - Entry\n");
565
+ Napi::Env env = info.Env();
566
+
567
+ Napi::Value objError;
568
+ SQLRETURN ret;
569
+ int count = 0, errorCount = 0;
570
+ int fetchMode = m_fetchMode;
571
+
572
+ if (info.Length() == 1 && info[0].IsObject())
573
+ {
574
+ Napi::Object obj = info[0].As<Napi::Object>();
575
+ if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
576
+ fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
577
+ }
578
+
579
+ if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
580
+
581
+ Napi::Array rows = Napi::Array::New(env);
582
+
583
+ if (colCount > 0)
584
+ {
585
+ bool useBlockFetch = InitBlockFetch();
586
+
587
+ if (useBlockFetch)
588
+ {
589
+ while (true)
590
+ {
591
+ m_rowsFetched = 0;
592
+ ret = SQLFetch(m_hSTMT);
593
+ if (ret == SQL_ERROR)
594
+ {
595
+ errorCount++;
596
+ objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::FetchAllSync (block fetch)");
597
+ break;
598
+ }
599
+ if (ret == SQL_NO_DATA) break;
600
+
601
+ for (SQLULEN r = 0; r < m_rowsFetched; r++)
602
+ {
603
+ if (m_rowStatusArray[r] == SQL_ROW_ERROR || m_rowStatusArray[r] == SQL_ROW_NOROW) continue;
604
+ if (fetchMode == FETCH_ARRAY)
605
+ {
606
+ Napi::Array array = Napi::Array::New(env);
607
+ for (int c = 0; c < colCount; c++) array.Set((uint32_t)c, GetBoundColumnValue(env, c, r));
608
+ rows.Set((uint32_t)count, array);
609
+ }
610
+ else
611
+ {
612
+ Napi::Object tuple = Napi::Object::New(env);
613
+ for (int c = 0; c < colCount; c++)
614
+ {
615
+ #ifdef UNICODE
616
+ tuple.Set(Napi::String::New(env, (const char16_t *)columns[c].name), GetBoundColumnValue(env, c, r));
617
+ #else
618
+ tuple.Set(Napi::String::New(env, (const char *)columns[c].name), GetBoundColumnValue(env, c, r));
619
+ #endif
620
+ }
621
+ rows.Set((uint32_t)count, tuple);
622
+ }
623
+ count++;
624
+ }
625
+ }
626
+ }
627
+ else
628
+ {
629
+ while (true)
630
+ {
631
+ ret = SQLFetch(m_hSTMT);
632
+ if (ret == SQL_ERROR)
633
+ {
634
+ errorCount++;
635
+ objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT,
636
+ (char *)"[node-odbc] Error in ODBCResult::FetchAllSync; probably your query did not have a result set.");
637
+ break;
638
+ }
639
+ if (ret == SQL_NO_DATA) break;
640
+
641
+ if (fetchMode == FETCH_ARRAY)
642
+ {
643
+ Napi::Value rowVal = ODBC::GetRecordArray(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
644
+ OverrideFileColumns(env, rowVal, fetchMode);
645
+ rows.Set((uint32_t)count, rowVal);
646
+ }
647
+ else
648
+ {
649
+ Napi::Value rowVal = ODBC::GetRecordTuple(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
650
+ OverrideFileColumns(env, rowVal, fetchMode);
651
+ rows.Set((uint32_t)count, rowVal);
652
+ }
653
+ count++;
654
+ }
655
+ }
656
+ }
657
+
658
+ ODBC::FreeColumns(columns, &colCount);
659
+ FREE(buffer);
660
+
661
+ if (errorCount > 0) napi_throw(env, objError);
662
+ return rows;
663
+ }
664
+
665
+ /*
666
+ * FetchN
667
+ */
668
+ Napi::Value ODBCResult::FetchN(const Napi::CallbackInfo &info)
669
+ {
670
+ DEBUG_PRINTF("ODBCResult::FetchN - Entry\n");
671
+ Napi::Env env = info.Env();
672
+
673
+ uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
674
+ MEMCHECK(work_req);
675
+ fetch_work_data *data = (fetch_work_data *)calloc(1, sizeof(fetch_work_data));
676
+ if (!data) free(work_req);
677
+ MEMCHECK(data);
678
+
679
+ Napi::Function cb;
680
+ data->fetchMode = m_fetchMode;
681
+
682
+ if (info.Length() == 2 && info[0].IsNumber() && info[1].IsFunction())
683
+ {
684
+ data->maxCount = info[0].As<Napi::Number>().Int32Value();
685
+ cb = info[1].As<Napi::Function>();
686
+ }
687
+ else if (info.Length() == 3 && info[0].IsNumber() && info[1].IsObject() && info[2].IsFunction())
688
+ {
689
+ data->maxCount = info[0].As<Napi::Number>().Int32Value();
690
+ cb = info[2].As<Napi::Function>();
691
+ Napi::Object obj = info[1].As<Napi::Object>();
692
+ if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
693
+ data->fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
694
+ }
695
+ else
696
+ {
697
+ free(data); free(work_req);
698
+ Napi::TypeError::New(env, "ODBCResult::FetchN(): Arguments must be (count, [option], callback).").ThrowAsJavaScriptException();
699
+ return env.Undefined();
700
+ }
701
+
702
+ if (data->maxCount <= 0)
703
+ {
704
+ free(data); free(work_req);
705
+ Napi::RangeError::New(env, "ODBCResult::FetchN(): count must be a positive integer.").ThrowAsJavaScriptException();
706
+ return env.Undefined();
707
+ }
708
+
709
+ data->rows = new Napi::Reference<Napi::Array>(Napi::Persistent(Napi::Array::New(env)));
710
+ data->errorCount = 0;
711
+ data->count = 0;
712
+ data->objError = new Napi::Reference<Napi::Object>(Napi::Persistent(Napi::Object::New(env)));
713
+
714
+ data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
715
+ data->objResult = this;
716
+ data->useBlockFetch = false;
717
+ data->blockRowIndex = 0;
718
+ data->env = env;
719
+ work_req->data = data;
720
+
721
+ uv_queue_work(uv_default_loop(), work_req, UV_FetchN, (uv_after_work_cb)UV_AfterFetchN);
722
+ this->Ref();
723
+ return env.Undefined();
724
+ }
725
+
726
+ void ODBCResult::UV_FetchN(uv_work_t *work_req)
727
+ {
728
+ DEBUG_PRINTF("ODBCResult::UV_FetchN\n");
729
+ fetch_work_data *data = (fetch_work_data *)(work_req->data);
730
+
731
+ bool useBlockFetch = data->objResult->InitBlockFetch();
732
+ data->useBlockFetch = useBlockFetch;
733
+
734
+ if (useBlockFetch)
735
+ {
736
+ SQLULEN rowIdx = 0;
737
+ data->result = data->objResult->BlockFetchNextRow(&rowIdx);
738
+ data->blockRowIndex = rowIdx;
739
+ }
740
+ else
741
+ data->result = SQLFetch(data->objResult->m_hSTMT);
742
+ }
743
+
744
+ void ODBCResult::UV_AfterFetchN(uv_work_t *work_req, int status)
745
+ {
746
+ DEBUG_PRINTF("ODBCResult::UV_AfterFetchN - Entry\n");
747
+ fetch_work_data *data = (fetch_work_data *)(work_req->data);
748
+ Napi::Env env(data->env);
749
+ Napi::HandleScope scope(env);
750
+
751
+ ODBCResult *self = data->objResult->self();
752
+ bool doMoreWork = true;
753
+
754
+ if (data->result == SQL_ERROR)
755
+ {
756
+ data->errorCount++;
757
+ data->objError->Reset(ODBC::GetSQLError(env, SQL_HANDLE_STMT, self->m_hSTMT,
758
+ (char *)"[node-odbc] Error in ODBCResult::UV_AfterFetchN").As<Napi::Object>());
759
+ }
760
+
761
+ if (self->colCount == 0) self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount);
762
+
763
+ if (self->colCount == 0) doMoreWork = false;
764
+ else if (data->result == SQL_NO_DATA) doMoreWork = false;
765
+ else if (data->result == SQL_ERROR) doMoreWork = false;
766
+ else
767
+ {
768
+ Napi::Array rows = data->rows->Value();
769
+
770
+ if (data->useBlockFetch)
771
+ {
772
+ SQLULEN rowIdx = data->blockRowIndex;
773
+ if (data->fetchMode == FETCH_ARRAY)
774
+ {
775
+ Napi::Array array = Napi::Array::New(env);
776
+ for (int c = 0; c < self->colCount; c++) array.Set((uint32_t)c, self->GetBoundColumnValue(env, c, rowIdx));
777
+ rows.Set((uint32_t)data->count, array);
778
+ }
779
+ else
780
+ {
781
+ Napi::Object tuple = Napi::Object::New(env);
782
+ for (int c = 0; c < self->colCount; c++)
783
+ {
784
+ #ifdef UNICODE
785
+ tuple.Set(Napi::String::New(env, (const char16_t *)self->columns[c].name), self->GetBoundColumnValue(env, c, rowIdx));
786
+ #else
787
+ tuple.Set(Napi::String::New(env, (const char *)self->columns[c].name), self->GetBoundColumnValue(env, c, rowIdx));
788
+ #endif
789
+ }
790
+ rows.Set((uint32_t)data->count, tuple);
791
+ }
792
+ }
793
+ else if (data->fetchMode == FETCH_ARRAY)
794
+ {
795
+ Napi::Value rowVal = ODBC::GetRecordArray(env, self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength);
796
+ self->OverrideFileColumns(env, rowVal, data->fetchMode);
797
+ rows.Set((uint32_t)data->count, rowVal);
798
+ }
799
+ else
800
+ {
801
+ Napi::Value rowVal = ODBC::GetRecordTuple(env, self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength);
802
+ self->OverrideFileColumns(env, rowVal, data->fetchMode);
803
+ rows.Set((uint32_t)data->count, rowVal);
804
+ }
805
+ data->count++;
806
+
807
+ if (data->count >= data->maxCount) doMoreWork = false;
808
+ }
809
+
810
+ if (doMoreWork)
811
+ {
812
+ uv_queue_work(uv_default_loop(), work_req, UV_FetchN, (uv_after_work_cb)UV_AfterFetchN);
813
+ }
814
+ else
815
+ {
816
+ Napi::Value err;
817
+ if (data->errorCount > 0 && self->colCount > 0)
818
+ err = data->objError->Value();
819
+ else
820
+ err = env.Null();
821
+
822
+ data->cb->Call({err, data->rows->Value()});
823
+ delete data->cb;
824
+ delete data->rows;
825
+ delete data->objError;
826
+ free(data);
827
+ free(work_req);
828
+ self->Unref();
829
+ }
830
+ }
831
+
832
+ /*
833
+ * FetchNSync
834
+ */
835
+ Napi::Value ODBCResult::FetchNSync(const Napi::CallbackInfo &info)
836
+ {
837
+ DEBUG_PRINTF("ODBCResult::FetchNSync - Entry\n");
838
+ Napi::Env env = info.Env();
839
+
840
+ if (!info[0].IsNumber())
841
+ {
842
+ Napi::TypeError::New(env, "ODBCResult::FetchNSync(): First argument must be an integer count.").ThrowAsJavaScriptException();
843
+ return env.Undefined();
844
+ }
845
+
846
+ int maxCount = info[0].As<Napi::Number>().Int32Value();
847
+ if (maxCount <= 0)
848
+ {
849
+ Napi::RangeError::New(env, "ODBCResult::FetchNSync(): count must be a positive integer.").ThrowAsJavaScriptException();
850
+ return env.Undefined();
851
+ }
852
+
853
+ Napi::Value objError;
854
+ SQLRETURN ret;
855
+ int count = 0, errorCount = 0;
856
+ int fetchMode = m_fetchMode;
857
+
858
+ if (info.Length() >= 2 && info[1].IsObject())
859
+ {
860
+ Napi::Object obj = info[1].As<Napi::Object>();
861
+ if (obj.Has("fetchMode") && obj.Get("fetchMode").IsNumber())
862
+ fetchMode = obj.Get("fetchMode").As<Napi::Number>().Int32Value();
863
+ }
864
+
865
+ if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
866
+
867
+ Napi::Array rows = Napi::Array::New(env);
868
+
869
+ if (colCount > 0)
870
+ {
871
+ bool useBlockFetch = InitBlockFetch();
872
+
873
+ if (useBlockFetch)
874
+ {
875
+ SQLULEN rowIdx;
876
+ while (count < maxCount)
877
+ {
878
+ ret = BlockFetchNextRow(&rowIdx);
879
+ if (ret == SQL_ERROR) { errorCount++; objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::FetchNSync (block fetch)"); break; }
880
+ if (ret == SQL_NO_DATA) break;
881
+
882
+ if (fetchMode == FETCH_ARRAY)
883
+ {
884
+ Napi::Array array = Napi::Array::New(env);
885
+ for (int c = 0; c < colCount; c++) array.Set((uint32_t)c, GetBoundColumnValue(env, c, rowIdx));
886
+ rows.Set((uint32_t)count, array);
887
+ }
888
+ else
889
+ {
890
+ Napi::Object tuple = Napi::Object::New(env);
891
+ for (int c = 0; c < colCount; c++)
892
+ {
893
+ #ifdef UNICODE
894
+ tuple.Set(Napi::String::New(env, (const char16_t *)columns[c].name), GetBoundColumnValue(env, c, rowIdx));
895
+ #else
896
+ tuple.Set(Napi::String::New(env, (const char *)columns[c].name), GetBoundColumnValue(env, c, rowIdx));
897
+ #endif
898
+ }
899
+ rows.Set((uint32_t)count, tuple);
900
+ }
901
+ count++;
902
+ }
903
+ }
904
+ else
905
+ {
906
+ while (count < maxCount)
907
+ {
908
+ ret = SQLFetch(m_hSTMT);
909
+ if (ret == SQL_ERROR) { errorCount++; objError = ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::FetchNSync"); break; }
910
+ if (ret == SQL_NO_DATA) break;
911
+
912
+ if (fetchMode == FETCH_ARRAY)
913
+ {
914
+ Napi::Value rowVal = ODBC::GetRecordArray(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
915
+ OverrideFileColumns(env, rowVal, fetchMode);
916
+ rows.Set((uint32_t)count, rowVal);
917
+ }
918
+ else
919
+ {
920
+ Napi::Value rowVal = ODBC::GetRecordTuple(env, m_hSTMT, columns, &colCount, buffer, bufferLength);
921
+ OverrideFileColumns(env, rowVal, fetchMode);
922
+ rows.Set((uint32_t)count, rowVal);
923
+ }
924
+ count++;
925
+ }
926
+ }
927
+ }
928
+
929
+ if (errorCount > 0) napi_throw(env, objError);
930
+ return rows;
931
+ }
932
+
933
+ /*
934
+ * GetData
935
+ */
936
+ Napi::Value ODBCResult::GetData(const Napi::CallbackInfo &info)
937
+ {
938
+ DEBUG_PRINTF("ODBCResult::GetData\n");
939
+ Napi::Env env = info.Env();
940
+
941
+ REQ_INT_ARG(0, colNum);
942
+ REQ_INT_ARG(1, dataSize);
943
+
944
+ uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
945
+ MEMCHECK(work_req);
946
+ getdata_work_data *data = (getdata_work_data *)calloc(1, sizeof(getdata_work_data));
947
+ if (!data) free(work_req);
948
+ MEMCHECK(data);
949
+
950
+ Napi::Function cb;
951
+
952
+ if (info.Length() == 3 && info[2].IsFunction())
953
+ {
954
+ cb = info[2].As<Napi::Function>();
955
+ data->colNum = (SQLUINTEGER)colNum;
956
+ data->dataSize = (SQLUINTEGER)dataSize;
957
+ }
958
+ else if (info.Length() == 2 && info[1].IsFunction())
959
+ {
960
+ cb = info[1].As<Napi::Function>();
961
+ data->colNum = (SQLUINTEGER)colNum;
962
+ data->dataSize = 0;
963
+ }
964
+ else
965
+ {
966
+ free(data); free(work_req);
967
+ Napi::TypeError::New(env, "ODBCResult::GetData(): 2 or 3 arguments are required.").ThrowAsJavaScriptException();
968
+ return env.Undefined();
969
+ }
970
+
971
+ data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
972
+ data->objResult = this;
973
+ data->env = env;
974
+ work_req->data = data;
975
+
976
+ uv_queue_work(uv_default_loop(), work_req, UV_GetData, (uv_after_work_cb)UV_AfterGetData);
977
+ this->Ref();
978
+ return env.Undefined();
979
+ }
980
+
981
+ void ODBCResult::UV_GetData(uv_work_t *work_req)
982
+ {
983
+ // No-op; actual work is done in UV_AfterGetData since it accesses JS values
984
+ }
985
+
986
+ void ODBCResult::UV_AfterGetData(uv_work_t *work_req, int status)
987
+ {
988
+ DEBUG_PRINTF("ODBCResult::UV_AfterGetData - Entry\n");
989
+ getdata_work_data *data = (getdata_work_data *)(work_req->data);
990
+ Napi::Env env(data->env);
991
+ Napi::HandleScope scope(env);
992
+
993
+ ODBCResult *objResult = data->objResult;
994
+ Napi::Value err = env.Null();
995
+ Napi::Value val = env.Null();
996
+
997
+ if (objResult->colCount > 0 && data->colNum <= (unsigned)objResult->colCount)
998
+ {
999
+ objResult->bufferLength = (size_t)data->dataSize;
1000
+ objResult->columns[data->colNum - 1].getData = true;
1001
+ val = ODBC::GetColumnValue(env, objResult->m_hSTMT, objResult->columns[data->colNum - 1], objResult->buffer, objResult->bufferLength);
1002
+ }
1003
+ else
1004
+ {
1005
+ ODBC::FreeColumns(objResult->columns, &objResult->colCount);
1006
+ FREE(objResult->buffer);
1007
+ }
1008
+
1009
+ data->cb->Call({err, val});
1010
+ PropagateCallbackException(env);
1011
+
1012
+ delete data->cb;
1013
+ data->objResult->Unref();
1014
+ free(data);
1015
+ free(work_req);
1016
+ }
1017
+
1018
+ /*
1019
+ * GetDataSync
1020
+ */
1021
+ Napi::Value ODBCResult::GetDataSync(const Napi::CallbackInfo &info)
1022
+ {
1023
+ DEBUG_PRINTF("ODBCResult::GetDataSync - Entry\n");
1024
+ Napi::Env env = info.Env();
1025
+
1026
+ REQ_INT_ARG(0, colNum);
1027
+ REQ_INT_ARG(1, dataSize);
1028
+
1029
+ if (colCount > 0 && colNum <= colCount)
1030
+ {
1031
+ bufferLength = (size_t)dataSize;
1032
+ columns[colNum - 1].getData = true;
1033
+ return ODBC::GetColumnValue(env, m_hSTMT, columns[colNum - 1], buffer, bufferLength);
1034
+ }
1035
+ else
1036
+ {
1037
+ ODBC::FreeColumns(columns, &colCount);
1038
+ FREE(buffer);
1039
+ return env.Null();
1040
+ }
1041
+ }
1042
+
1043
+ /*
1044
+ * Close
1045
+ */
1046
+ Napi::Value ODBCResult::Close(const Napi::CallbackInfo &info)
1047
+ {
1048
+ DEBUG_PRINTF("ODBCResult::Close - Entry\n");
1049
+ Napi::Env env = info.Env();
1050
+ Napi::Function cb;
1051
+ SQLUSMALLINT closeOption = SQL_DROP;
1052
+
1053
+ if (info.Length() == 2)
1054
+ {
1055
+ if (!info[0].IsNumber()) { Napi::TypeError::New(env, "Argument 0 must be an Integer.").ThrowAsJavaScriptException(); return env.Undefined(); }
1056
+ if (!info[1].IsFunction()) { Napi::TypeError::New(env, "Argument 1 must be a Function.").ThrowAsJavaScriptException(); return env.Undefined(); }
1057
+ closeOption = (SQLUSMALLINT)info[0].As<Napi::Number>().Int32Value();
1058
+ cb = info[1].As<Napi::Function>();
1059
+ }
1060
+ else if (info.Length() == 1)
1061
+ {
1062
+ if (!info[0].IsFunction()) { Napi::TypeError::New(env, "ODBCResult::Close(): Argument 0 must be a Function.").ThrowAsJavaScriptException(); return env.Undefined(); }
1063
+ cb = info[0].As<Napi::Function>();
1064
+ }
1065
+
1066
+ uv_work_t *work_req = (uv_work_t *)(calloc(1, sizeof(uv_work_t)));
1067
+ MEMCHECK(work_req);
1068
+ close_result_work_data *data = (close_result_work_data *)(calloc(1, sizeof(close_result_work_data)));
1069
+ if (!data) free(work_req);
1070
+ MEMCHECK(data);
1071
+
1072
+ data->cb = new Napi::FunctionReference(Napi::Persistent(cb));
1073
+ data->objResult = this;
1074
+ data->closeOption = closeOption;
1075
+ data->env = env;
1076
+ work_req->data = data;
1077
+
1078
+ uv_queue_work(uv_default_loop(), work_req, UV_Close, (uv_after_work_cb)UV_AfterClose);
1079
+ this->Ref();
1080
+ return env.Undefined();
1081
+ }
1082
+
1083
+ void ODBCResult::UV_Close(uv_work_t *req)
1084
+ {
1085
+ DEBUG_PRINTF("ODBCResult::UV_Close - Entry\n");
1086
+ close_result_work_data *data = (close_result_work_data *)(req->data);
1087
+ ODBCResult *objResult = data->objResult;
1088
+ SQLHSTMT hSTMT = (SQLHSTMT)objResult->m_hSTMT;
1089
+
1090
+ if (data->closeOption == SQL_DROP && objResult->m_canFreeHandle)
1091
+ {
1092
+ objResult->Free();
1093
+ data->result = 0;
1094
+ }
1095
+ else if (data->closeOption == SQL_DROP && !objResult->m_canFreeHandle)
1096
+ {
1097
+ SQLFreeStmt(hSTMT, SQL_CLOSE);
1098
+ data->result = 0;
1099
+ }
1100
+ else
1101
+ {
1102
+ data->result = SQLFreeStmt(hSTMT, data->closeOption);
1103
+ }
1104
+ DEBUG_PRINTF("ODBCResult::UV_Close - Exit\n");
1105
+ }
1106
+
1107
+ void ODBCResult::UV_AfterClose(uv_work_t *req, int status)
1108
+ {
1109
+ DEBUG_PRINTF("ODBCResult::UV_AfterClose - Entry\n");
1110
+ close_result_work_data *data = (close_result_work_data *)(req->data);
1111
+ Napi::Env env(data->env);
1112
+ Napi::HandleScope scope(env);
1113
+ ODBCResult *self = data->objResult->self();
1114
+
1115
+ if (data->result != SQL_SUCCESS)
1116
+ ODBC::CallbackSQLError(env, SQL_HANDLE_STMT, data->objResult->m_hSTMT, data->cb);
1117
+ else
1118
+ data->cb->Call({env.Null()});
1119
+ PropagateCallbackException(env);
1120
+
1121
+ self->Unref();
1122
+ delete data->cb;
1123
+ free(data);
1124
+ free(req);
1125
+ }
1126
+
1127
+ /*
1128
+ * CloseSync
1129
+ */
1130
+ Napi::Value ODBCResult::CloseSync(const Napi::CallbackInfo &info)
1131
+ {
1132
+ DEBUG_PRINTF("ODBCResult::CloseSync - Entry\n");
1133
+ Napi::Env env = info.Env();
1134
+ SQLUSMALLINT closeOption = SQL_DROP;
1135
+
1136
+ if (info.Length() > 0 && info[0].IsNumber())
1137
+ closeOption = (SQLUSMALLINT)info[0].As<Napi::Number>().Int32Value();
1138
+
1139
+ if (closeOption == SQL_DROP && m_canFreeHandle)
1140
+ this->Free();
1141
+ else if (closeOption == SQL_DROP && !m_canFreeHandle)
1142
+ SQLFreeStmt(m_hSTMT, SQL_CLOSE);
1143
+ else
1144
+ SQLFreeStmt(m_hSTMT, closeOption);
1145
+
1146
+ return Napi::Boolean::New(env, true);
1147
+ }
1148
+
1149
+ /*
1150
+ * MoreResultsSync
1151
+ */
1152
+ Napi::Value ODBCResult::MoreResultsSync(const Napi::CallbackInfo &info)
1153
+ {
1154
+ DEBUG_PRINTF("ODBCResult::MoreResultsSync\n");
1155
+ Napi::Env env = info.Env();
1156
+
1157
+ SQLRETURN ret = SQLMoreResults(m_hSTMT);
1158
+
1159
+ if (ret == SQL_ERROR)
1160
+ napi_throw(env, ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync"));
1161
+
1162
+ return Napi::Boolean::New(env, SQL_SUCCEEDED(ret) || ret == SQL_ERROR);
1163
+ }
1164
+
1165
+ /*
1166
+ * GetColumnNamesSync
1167
+ */
1168
+ Napi::Value ODBCResult::GetColumnNamesSync(const Napi::CallbackInfo &info)
1169
+ {
1170
+ DEBUG_PRINTF("ODBCResult::GetColumnNamesSync - Entry\n");
1171
+ Napi::Env env = info.Env();
1172
+
1173
+ if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
1174
+
1175
+ Napi::Array cols = Napi::Array::New(env);
1176
+ for (int i = 0; i < colCount; i++)
1177
+ cols.Set((uint32_t)i, Napi::String::New(env, (const char *)columns[i].name));
1178
+
1179
+ ODBC::FreeColumns(columns, &colCount);
1180
+ return cols;
1181
+ }
1182
+
1183
+ /*
1184
+ * GetColumnMetadataSync
1185
+ */
1186
+ Napi::Value ODBCResult::GetColumnMetadataSync(const Napi::CallbackInfo &info)
1187
+ {
1188
+ DEBUG_PRINTF("ODBCResult::GetColumnMetadataSync - Entry\n");
1189
+ Napi::Env env = info.Env();
1190
+
1191
+ if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
1192
+
1193
+ Napi::Array result = Napi::Array::New(env);
1194
+
1195
+ for (int i = 0; i < colCount; i++)
1196
+ {
1197
+ Napi::Object col = Napi::Object::New(env);
1198
+ col.Set("index", Napi::Number::New(env, columns[i].index));
1199
+ col.Set("SQL_DESC_NAME", Napi::String::New(env, (const char *)columns[i].name));
1200
+ col.Set("SQL_DESC_TYPE_NAME", Napi::String::New(env, (const char *)columns[i].type_name));
1201
+ col.Set("SQL_DESC_CONSIZE_TYPE", Napi::Number::New(env, (int32_t)columns[i].type));
1202
+ col.Set("SQL_DESC_DISPLAY_SIZE", Napi::Number::New(env, columns[i].max_display_len));
1203
+ col.Set("SQL_DESC_PRECISION", Napi::Number::New(env, columns[i].precision));
1204
+ col.Set("SQL_DESC_SCALE", Napi::Number::New(env, columns[i].scale));
1205
+ col.Set("SQL_DESC_LENGTH", Napi::Number::New(env, columns[i].field_len));
1206
+ result.Set((uint32_t)i, col);
1207
+ }
1208
+
1209
+ ODBC::FreeColumns(columns, &colCount);
1210
+ return result;
1211
+ }
1212
+
1213
+ /*
1214
+ * GetSQLErrorSync
1215
+ */
1216
+ Napi::Value ODBCResult::GetSQLErrorSync(const Napi::CallbackInfo &info)
1217
+ {
1218
+ DEBUG_PRINTF("ODBCResult::GetSQLErrorSync\n");
1219
+ Napi::Env env = info.Env();
1220
+ return ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"");
1221
+ }
1222
+
1223
+ /*
1224
+ * GetAffectedRowsSync
1225
+ */
1226
+ Napi::Value ODBCResult::GetAffectedRowsSync(const Napi::CallbackInfo &info)
1227
+ {
1228
+ DEBUG_PRINTF("ODBCResult::GetAffectedRowsSync\n");
1229
+ Napi::Env env = info.Env();
1230
+ SQLLEN rowCount = 0;
1231
+ SQLRETURN ret = SQLRowCount(m_hSTMT, &rowCount);
1232
+ if (!SQL_SUCCEEDED(ret)) rowCount = 0;
1233
+ return Napi::Number::New(env, (double)rowCount);
1234
+ }
1235
+
1236
+ /*
1237
+ * Block Fetch Support
1238
+ */
1239
+ #define BLOCK_FETCH_MAX_COL_SIZE 65536
1240
+
1241
+ bool ODBCResult::InitBlockFetch()
1242
+ {
1243
+ if (m_blockFetchInitialized) return (m_rowArraySize > 1);
1244
+ m_blockFetchInitialized = true;
1245
+
1246
+ SQLULEN rowArraySize = 0;
1247
+ SQLRETURN ret = SQLGetStmtAttr(m_hSTMT, SQL_ATTR_ROW_ARRAY_SIZE, &rowArraySize, SQL_IS_UINTEGER, NULL);
1248
+ if (!SQL_SUCCEEDED(ret) || rowArraySize <= 1) { m_rowArraySize = 1; return false; }
1249
+
1250
+ m_rowArraySize = rowArraySize;
1251
+
1252
+ if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
1253
+ if (colCount == 0) { m_rowArraySize = 1; return false; }
1254
+
1255
+ m_rowStatusArray = (SQLUSMALLINT *)calloc(m_rowArraySize, sizeof(SQLUSMALLINT));
1256
+ if (!m_rowStatusArray) { m_rowArraySize = 1; return false; }
1257
+
1258
+ ret = SQLSetStmtAttr(m_hSTMT, SQL_ATTR_ROW_STATUS_PTR, m_rowStatusArray, 0);
1259
+ if (!SQL_SUCCEEDED(ret)) { free(m_rowStatusArray); m_rowStatusArray = NULL; m_rowArraySize = 1; return false; }
1260
+
1261
+ ret = SQLSetStmtAttr(m_hSTMT, SQL_ATTR_ROWS_FETCHED_PTR, &m_rowsFetched, 0);
1262
+ if (!SQL_SUCCEEDED(ret)) { free(m_rowStatusArray); m_rowStatusArray = NULL; m_rowArraySize = 1; return false; }
1263
+
1264
+ if (!BindColumnsForBlockFetch()) { FreeBlockFetchBuffers(); m_rowArraySize = 1; return false; }
1265
+ return true;
1266
+ }
1267
+
1268
+ bool ODBCResult::BindColumnsForBlockFetch()
1269
+ {
1270
+ m_boundCols = (BoundColumn *)calloc(colCount, sizeof(BoundColumn));
1271
+ if (!m_boundCols) return false;
1272
+
1273
+ for (int i = 0; i < colCount; i++)
1274
+ {
1275
+ SQLSMALLINT cType;
1276
+ SQLLEN elemSize;
1277
+
1278
+ switch (columns[i].type)
1279
+ {
1280
+ case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT:
1281
+ cType = SQL_C_SLONG; elemSize = sizeof(SQLINTEGER); break;
1282
+ case SQL_BIGINT:
1283
+ cType = SQL_C_CHAR; elemSize = 22; break;
1284
+ case SQL_FLOAT: case SQL_REAL: case SQL_DOUBLE: case SQL_NUMERIC: case SQL_DECIMAL: case SQL_DECFLOAT:
1285
+ cType = SQL_C_DOUBLE; elemSize = sizeof(double); break;
1286
+ case SQL_BIT:
1287
+ cType = SQL_C_CHAR; elemSize = 4; break;
1288
+ case SQL_DATETIME: case SQL_TIMESTAMP: case SQL_TYPE_TIMESTAMP:
1289
+ #ifdef _WIN32
1290
+ cType = SQL_C_CHAR; elemSize = sizeof(SQL_TIMESTAMP_STRUCT); break;
1291
+ #else
1292
+ cType = SQL_C_TYPE_TIMESTAMP; elemSize = sizeof(SQL_TIMESTAMP_STRUCT); break;
1293
+ #endif
1294
+ case SQL_BLOB: case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY:
1295
+ {
1296
+ cType = SQL_C_BINARY;
1297
+ SQLLEN colSize = columns[i].field_len;
1298
+ if (colSize <= 0 || colSize > BLOCK_FETCH_MAX_COL_SIZE) colSize = BLOCK_FETCH_MAX_COL_SIZE;
1299
+ elemSize = colSize;
1300
+ break;
1301
+ }
1302
+ default:
1303
+ {
1304
+ cType = SQL_C_CHAR;
1305
+ #ifdef UNICODE
1306
+ cType = SQL_C_WCHAR;
1307
+ #endif
1308
+ SQLLEN colSize = columns[i].field_len;
1309
+ if (colSize <= 0 || colSize > BLOCK_FETCH_MAX_COL_SIZE) colSize = BLOCK_FETCH_MAX_COL_SIZE;
1310
+ #ifdef UNICODE
1311
+ elemSize = (colSize + 1) * sizeof(uint16_t);
1312
+ #else
1313
+ elemSize = colSize + 1;
1314
+ #endif
1315
+ break;
1316
+ }
1317
+ }
1318
+
1319
+ m_boundCols[i].cType = cType;
1320
+ m_boundCols[i].elementSize = elemSize;
1321
+ m_boundCols[i].data = calloc(m_rowArraySize, elemSize);
1322
+ if (!m_boundCols[i].data) return false;
1323
+ m_boundCols[i].indicators = (SQLLEN *)calloc(m_rowArraySize, sizeof(SQLLEN));
1324
+ if (!m_boundCols[i].indicators) return false;
1325
+
1326
+ SQLRETURN ret = SQLBindCol(m_hSTMT, (SQLUSMALLINT)(i + 1), cType, m_boundCols[i].data, elemSize, m_boundCols[i].indicators);
1327
+ if (!SQL_SUCCEEDED(ret)) return false;
1328
+ }
1329
+ return true;
1330
+ }
1331
+
1332
+ void ODBCResult::FreeBlockFetchBuffers()
1333
+ {
1334
+ if (m_boundCols)
1335
+ {
1336
+ for (int i = 0; i < colCount; i++)
1337
+ {
1338
+ if (m_boundCols[i].data) { free(m_boundCols[i].data); m_boundCols[i].data = NULL; }
1339
+ if (m_boundCols[i].indicators) { free(m_boundCols[i].indicators); m_boundCols[i].indicators = NULL; }
1340
+ }
1341
+ free(m_boundCols); m_boundCols = NULL;
1342
+ }
1343
+ if (m_rowStatusArray) { free(m_rowStatusArray); m_rowStatusArray = NULL; }
1344
+ m_blockFetchInitialized = false;
1345
+ m_rowArraySize = 0;
1346
+ }
1347
+
1348
+ Napi::Value ODBCResult::GetBoundColumnValue(Napi::Env env, int colIndex, SQLULEN rowIndex)
1349
+ {
1350
+ if (m_fileColBindings && colIndex < m_fileColCount && m_fileColBindings[colIndex].bound)
1351
+ {
1352
+ FileColumnBinding *fcb = &m_fileColBindings[colIndex];
1353
+ if (fcb->indicator == SQL_NULL_DATA) return env.Null();
1354
+ return Napi::String::New(env, fcb->fileName);
1355
+ }
1356
+
1357
+ BoundColumn *bc = &m_boundCols[colIndex];
1358
+ SQLLEN indicator = bc->indicators[rowIndex];
1359
+ if (indicator == SQL_NULL_DATA) return env.Null();
1360
+
1361
+ char *rowData = (char *)bc->data + (rowIndex * bc->elementSize);
1362
+
1363
+ switch (columns[colIndex].type)
1364
+ {
1365
+ case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT:
1366
+ return Napi::Number::New(env, (int)(*(SQLINTEGER *)rowData));
1367
+
1368
+ case SQL_BIGINT:
1369
+ return Napi::String::New(env, (const char *)rowData);
1370
+
1371
+ case SQL_FLOAT: case SQL_REAL: case SQL_DOUBLE: case SQL_NUMERIC: case SQL_DECIMAL: case SQL_DECFLOAT:
1372
+ return Napi::Number::New(env, *(double *)rowData);
1373
+
1374
+ case SQL_BIT:
1375
+ return Napi::Boolean::New(env, (*rowData != '0'));
1376
+
1377
+ case SQL_DATETIME: case SQL_TIMESTAMP: case SQL_TYPE_TIMESTAMP:
1378
+ {
1379
+ SQL_TIMESTAMP_STRUCT *ts = (SQL_TIMESTAMP_STRUCT *)rowData;
1380
+ #ifdef _WIN32
1381
+ struct tm timeInfo = {};
1382
+ #elif defined(_AIX) || defined(__MVS__)
1383
+ struct tm timeInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0};
1384
+ #else
1385
+ struct tm timeInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1386
+ #endif
1387
+ timeInfo.tm_year = ts->year - 1900;
1388
+ timeInfo.tm_mon = ts->month - 1;
1389
+ timeInfo.tm_mday = ts->day;
1390
+ timeInfo.tm_hour = ts->hour;
1391
+ timeInfo.tm_min = ts->minute;
1392
+ timeInfo.tm_sec = ts->second;
1393
+ timeInfo.tm_isdst = -1;
1394
+
1395
+ #ifdef TIMEGM
1396
+ double msec = (double(timegm(&timeInfo)) * 1000) + (ts->fraction / 1000000);
1397
+ #else
1398
+ double msec = (double(mktime(&timeInfo)) * 1000) + (ts->fraction / 1000000);
1399
+ #endif
1400
+ return Napi::Date::New(env, msec);
1401
+ }
1402
+
1403
+ case SQL_BLOB: case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY:
1404
+ {
1405
+ SQLLEN dataLen = indicator;
1406
+ if (dataLen > bc->elementSize) dataLen = bc->elementSize;
1407
+ return Napi::Buffer<char>::Copy(env, rowData, (size_t)dataLen);
1408
+ }
1409
+
1410
+ default:
1411
+ {
1412
+ #ifdef UNICODE
1413
+ return Napi::String::New(env, (const char16_t *)rowData);
1414
+ #else
1415
+ return Napi::String::New(env, (const char *)rowData);
1416
+ #endif
1417
+ }
1418
+ }
1419
+ }
1420
+
1421
+ SQLRETURN ODBCResult::BlockFetchNextRow(SQLULEN *outRowIndex)
1422
+ {
1423
+ if (m_blockExhausted) return SQL_NO_DATA;
1424
+
1425
+ while (m_currentRowInBlock < m_rowsFetched)
1426
+ {
1427
+ SQLUSMALLINT st = m_rowStatusArray[m_currentRowInBlock];
1428
+ if (st == SQL_ROW_SUCCESS || st == SQL_ROW_SUCCESS_WITH_INFO)
1429
+ {
1430
+ *outRowIndex = m_currentRowInBlock;
1431
+ m_currentRowInBlock++;
1432
+ return SQL_SUCCESS;
1433
+ }
1434
+ m_currentRowInBlock++;
1435
+ }
1436
+
1437
+ m_rowsFetched = 0;
1438
+ m_currentRowInBlock = 0;
1439
+ SQLRETURN ret = SQLFetch(m_hSTMT);
1440
+
1441
+ if (ret == SQL_NO_DATA) { m_blockExhausted = true; return SQL_NO_DATA; }
1442
+ if (ret == SQL_ERROR) return SQL_ERROR;
1443
+
1444
+ while (m_currentRowInBlock < m_rowsFetched)
1445
+ {
1446
+ SQLUSMALLINT st = m_rowStatusArray[m_currentRowInBlock];
1447
+ if (st == SQL_ROW_SUCCESS || st == SQL_ROW_SUCCESS_WITH_INFO)
1448
+ {
1449
+ *outRowIndex = m_currentRowInBlock;
1450
+ m_currentRowInBlock++;
1451
+ return SQL_SUCCESS;
1452
+ }
1453
+ m_currentRowInBlock++;
1454
+ }
1455
+
1456
+ return BlockFetchNextRow(outRowIndex);
1457
+ }
1458
+
1459
+ /*
1460
+ * BindFileToColSync
1461
+ */
1462
+ Napi::Value ODBCResult::BindFileToColSync(const Napi::CallbackInfo &info)
1463
+ {
1464
+ DEBUG_PRINTF("ODBCResult::BindFileToColSync - Entry\n");
1465
+ Napi::Env env = info.Env();
1466
+
1467
+ if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsString())
1468
+ {
1469
+ Napi::TypeError::New(env, "bindFileToColSync(colNum, filePath[, fileOption]): colNum (integer) and filePath (string) are required.").ThrowAsJavaScriptException();
1470
+ return env.Undefined();
1471
+ }
1472
+
1473
+ int colNum = info[0].As<Napi::Number>().Int32Value();
1474
+ std::string filePath = info[1].As<Napi::String>().Utf8Value();
1475
+ int filePathLen = (int)filePath.length();
1476
+
1477
+ if (colNum < 1) { Napi::RangeError::New(env, "colNum must be >= 1").ThrowAsJavaScriptException(); return env.Undefined(); }
1478
+ if (filePathLen <= 0 || filePathLen >= FILE_COL_MAX_PATH) { Napi::RangeError::New(env, "filePath is empty or exceeds max path length").ThrowAsJavaScriptException(); return env.Undefined(); }
1479
+
1480
+ SQLUINTEGER fileOption = SQL_FILE_OVERWRITE;
1481
+ if (info.Length() >= 3 && info[2].IsNumber())
1482
+ fileOption = info[2].As<Napi::Number>().Uint32Value();
1483
+
1484
+ if (colCount == 0) columns = ODBC::GetColumns(m_hSTMT, &colCount);
1485
+ if (colNum > colCount) { Napi::RangeError::New(env, "colNum exceeds the number of columns in the result set").ThrowAsJavaScriptException(); return env.Undefined(); }
1486
+
1487
+ if (!m_fileColBindings)
1488
+ {
1489
+ m_fileColBindings = (FileColumnBinding *)calloc(colCount, sizeof(FileColumnBinding));
1490
+ if (!m_fileColBindings) { Napi::Error::New(env, "Failed to allocate memory for file column bindings").ThrowAsJavaScriptException(); return env.Undefined(); }
1491
+ m_fileColCount = colCount;
1492
+ }
1493
+
1494
+ FileColumnBinding *fcb = &m_fileColBindings[colNum - 1];
1495
+ memset(fcb, 0, sizeof(FileColumnBinding));
1496
+ strncpy(fcb->fileName, filePath.c_str(), FILE_COL_MAX_PATH - 1);
1497
+ fcb->fileName[FILE_COL_MAX_PATH - 1] = '\0';
1498
+ fcb->fileNameLength = (SQLSMALLINT)filePathLen;
1499
+ fcb->fileOption = fileOption;
1500
+ fcb->stringLength = 0;
1501
+ fcb->indicator = 0;
1502
+
1503
+ SQLRETURN ret = SQLBindFileToCol(m_hSTMT, (SQLUSMALLINT)colNum,
1504
+ (SQLCHAR *)fcb->fileName, &fcb->fileNameLength, &fcb->fileOption,
1505
+ (SQLSMALLINT)FILE_COL_MAX_PATH, &fcb->stringLength, &fcb->indicator);
1506
+
1507
+ if (!SQL_SUCCEEDED(ret))
1508
+ {
1509
+ napi_throw(env, ODBC::GetSQLError(env, SQL_HANDLE_STMT, m_hSTMT, (char *)"Error in ODBCResult::BindFileToColSync"));
1510
+ return env.Undefined();
1511
+ }
1512
+
1513
+ fcb->bound = true;
1514
+ columns[colNum - 1].isFileBound = true;
1515
+ return Napi::Boolean::New(env, true);
1516
+ }
1517
+
1518
+ /*
1519
+ * OverrideFileColumns
1520
+ */
1521
+ void ODBCResult::OverrideFileColumns(Napi::Env env, Napi::Value row, int fetchMode)
1522
+ {
1523
+ if (!m_fileColBindings || !row.IsObject() || row.IsNull()) return;
1524
+
1525
+ Napi::Object obj = row.As<Napi::Object>();
1526
+
1527
+ for (int i = 0; i < m_fileColCount && i < colCount; i++)
1528
+ {
1529
+ FileColumnBinding *fcb = &m_fileColBindings[i];
1530
+ if (!fcb->bound) continue;
1531
+
1532
+ Napi::Value val;
1533
+ if (fcb->indicator == SQL_NULL_DATA) val = env.Null();
1534
+ else val = Napi::String::New(env, fcb->fileName);
1535
+
1536
+ if (fetchMode == FETCH_ARRAY)
1537
+ obj.Set((uint32_t)i, val);
1538
+ else
1539
+ {
1540
+ #ifdef UNICODE
1541
+ obj.Set(Napi::String::New(env, (const char16_t *)columns[i].name), val);
1542
+ #else
1543
+ obj.Set(Napi::String::New(env, (const char *)columns[i].name), val);
1544
+ #endif
1545
+ }
1546
+ }
1547
+ }