├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── Jenkinsfile ├── dco.yml ├── release.sh └── workflows │ └── stale.yml ├── .gitignore ├── .npmignore ├── .release-it.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── binding.gyp ├── docs ├── README.md └── RELEASE.md ├── lib └── db2a.js ├── package-lock.json ├── package.json ├── src └── db2ia │ ├── db2ia.cc │ ├── dbconn.cc │ ├── dbconn.h │ ├── dberror.h │ ├── dbstmt.cc │ └── dbstmt.h └── test ├── README.md ├── asyncStatementTest.js ├── connectionTest.js ├── dataTypes.js ├── failTests.js ├── misc.js ├── statementTest.js └── syncStatementTest.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'extends': 'airbnb-base', 3 | 'env': { 4 | 'node': true, 5 | 'es6': true, 6 | 'mocha': true 7 | }, 8 | 'parserOptions': { 9 | 'ecmaVersion': 2017 10 | }, 11 | 'rules': { 12 | // Reference 13 | // 'off' or 0 - turn the rule off 14 | // 'warn' or 1 - turn the rule on as a warning (doesn’t affect exit code) 15 | // 'error' or 2 - turn the rule on as an error (exit code is 1 when triggered) 16 | 'array-bracket-spacing': [1, 'never'], // disallow spaces between array brackets and other tokens 17 | 'block-scoped-var': 2, // treat var statements as if they were block scoped (off by default). 0: deep destructuring is not compatible https://github.com/eslint/eslint/issues/1863 18 | 'brace-style': [2, '1tbs', { 'allowSingleLine': true }], // enforces consistent brace style for blocks 19 | 'camelcase': 2, // enforces camelcase style for property names 20 | 'computed-property-spacing': [2, 'never'], // disallows spaces inside computed property brackets 21 | 'comma-spacing': [1, {'before': false, 'after': true}], // enforce spacing before and after comma 22 | 'comma-style': [1, 'last'], // enforce one true comma style (off by default) 23 | 'curly': 2, // enforces curly braces for code block statements, even if one line 24 | 'consistent-this': [1, 'me'], // enforces consistent naming when capturing the current execution context (off by default) 25 | 'eol-last': 1, // enforce newline at the end of file, with no multiple empty lines 26 | 'eqeqeq': [2, 'smart'], // require the use of === and !== 27 | 'max-depth': [1, 3], // disallow more than 3 nested code blocks 28 | 'max-len': [1, 80], // enforces a maximum line length to increase code readability and maintainability 29 | 'max-statements': [1, 15], // enforces a maximum number of statements allowed in function blocks 30 | 'new-cap': 1, // requires constructor names to begin with a capital letter 31 | 'no-extend-native': 2, // disallows directly modifying the prototype of built-in objects 32 | 'no-mixed-spaces-and-tabs': 2, // disallows mixed spaces and tabs for indentation 33 | 'no-trailing-spaces': 2, // disallows trailing whitespace (spaces, tabs, and other Unicode whitespace characters) at the end of lines 34 | 'no-unused-vars': 1, // aimed at eliminating unused variables, functions, and parameters of functions 35 | 'no-use-before-define': [2, 'nofunc'], // error when it encounters a reference to an identifier that has not yet been declared 36 | 'object-curly-spacing': [2, 'never'], // enforce consistent spacing inside braces of object literals, destructuring assignments, and import/export specifiers 37 | 'quotes': [2, 'single', { 'allowTemplateLiterals': true }], // enforces the consistent use of either backticks, double, or single quotes 38 | 'quote-props': [1, 'as-needed', {'keywords': true}], // require quotes around object literal property names (off by default) 39 | 'semi-style': [2, 'last'], // reports line terminators around semicolons 40 | 'keyword-spacing': [2, {'before': true, 'after': true}], // enforces consistent spacing around keywords and keyword-like tokens 41 | 'space-unary-ops': 2, // enforces consistency regarding the spaces after words unary operators and after/before nonwords unary operators 42 | 'indent': ['error', 2], // enforces a consistent indentation style of 2 spaces 43 | 'vars-on-top': 2, // requires to declare all vars on top of their containing scope (off by default) 44 | 'no-else-return': 2, // disallow else after a return in an if (off by default) 45 | 'semi' :['error', 'always'], 46 | 'import/no-unresolved': 0, 47 | 'no-console': 'off', 48 | 'no-unused-expressions': 'off', 49 | 'no-shadow': ['error', { 'allow': ['error', 'result'] }], 50 | 'no-empty': ['error', { 'allowEmptyCatch': true }], 51 | 'import/no-dynamic-require': 0, 52 | } 53 | }; -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 24 | **Describe the bug** 25 | A clear and concise description of what the bug is. 26 | 27 | **To Reproduce** 28 | Steps to reproduce the behavior: 29 | 1. 30 | 2. 31 | 3. 32 | 4. 33 | 34 | **Expected behavior** 35 | A clear and concise description of what you expected to happen. 36 | 37 | **Screenshots** 38 | If applicable, add screenshots to help explain your problem. 39 | 40 | * **Node.js version**: 41 | * **idb-connector version**: 42 | * **IBM i version:**: 43 | 44 | 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | node { 4 | label 'ibmi7.3' 5 | } 6 | } 7 | environment { 8 | NODE_PATH = '/QOpenSys/pkgs/lib/nodejs18/bin' 9 | } 10 | stages { 11 | stage('build') { 12 | steps { 13 | sh ''' 14 | export PATH=$NODE_PATH:$PATH 15 | npm i 16 | ./node_modules/.bin/node-pre-gyp rebuild --production 17 | ./node_modules/.bin/node-pre-gyp package 18 | ''' 19 | } 20 | } 21 | stage('create-gh-release') { 22 | environment { 23 | GH_TOKEN = credentials('idb-connector-gh-token') 24 | } 25 | steps { 26 | sh ''' 27 | export PATH=$NODE_PATH:$PATH 28 | export GITHUB_TOKEN=$GH_TOKEN_PSW 29 | ./node_modules/release-it/bin/release-it.js --ci --no-increment --no-git --github.release --github.update --no-plugins.@release-it/conventional-changelog.infile 30 | ''' 31 | } 32 | } 33 | stage('create-npm-release') { 34 | environment { 35 | NPM_TOKEN = credentials('idb-connector-npm-token') 36 | } 37 | steps { 38 | sh ''' 39 | export PATH=$NODE_PATH:$PATH 40 | npm config set --location=project //registry.npmjs.org/:_authToken $NPM_TOKEN 41 | ./node_modules/release-it/bin/release-it.js --ci --no-increment --no-git --npm.publish --npm.skipChecks --no-plugins.@release-it/conventional-changelog.infile 42 | ''' 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/dco.yml: -------------------------------------------------------------------------------- 1 | require: 2 | members: false 3 | -------------------------------------------------------------------------------- /.github/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | PATH="/QOpenSys/pkgs/bin:$PATH" 5 | DATE=$(date +"%Y-%m-%d_%H-%M-%S") 6 | BRANCH="release-$DATE" 7 | REMOTE="origin" 8 | MAIN_BRANCH="master" 9 | 10 | if [ ! -d node_modules ]; then 11 | # install deps 12 | npm install 13 | fi 14 | 15 | # checkout main branch and pull the latest changes 16 | git checkout $MAIN_BRNACH 17 | git pull $REMOTE $MAIN_BRANCH 18 | # create release branch and run release 19 | git checkout -b "$BRANCH" $REMOTE/$MAIN_BRANCH 20 | npm run release 21 | # push branch to upstream remote to open a PR 22 | git push $REMOTE $BRANCH 23 | 24 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | 7 | jobs: 8 | stale: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/stale@v3 14 | with: 15 | repo-token: ${{ secrets.GITHUB_TOKEN }} 16 | stale-issue-message: ':wave: Hi! This issue has been marked stale due to inactivity. If no further activity occurs, it will automatically be closed.' 17 | stale-pr-message: ':wave: Hi! This pull request has been marked stale due to inactivity. If no further activity occurs, it will automatically be closed.' 18 | stale-issue-label: 'stale' 19 | stale-pr-label: 'stale' 20 | exempt-issue-labels: 'keep-open' 21 | exempt-pr-labels: 'keep-open' 22 | days-before-stale: 30 23 | days-before-close: 7 24 | remove-stale-when-updated: true 25 | 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build* 3 | .lock* 4 | lib/binding/ 5 | npm-debug.log 6 | .npmrc 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .github/ 2 | .eslintrc 3 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "npm": { 3 | "publish": false 4 | }, 5 | "git": { 6 | "commitMessage": "chore: release ${version}", 7 | "push": false, 8 | "tag": false 9 | }, 10 | "github":{ 11 | "assets": ["build/stage/${version}/*.tar.gz"], 12 | "release": false, 13 | "autoGenerate": true, 14 | "releaseName": "${version}" 15 | }, 16 | "plugins": { 17 | "@release-it/conventional-changelog": { 18 | "header": "# idb-connector changelog", 19 | "preset": "conventionalcommits", 20 | "infile": "CHANGELOG.md", 21 | "ignoreRecommendedBump": true 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # idb-connector changelog 2 | 3 | ## [1.2.19](https://github.com/IBM/nodejs-idb-connector/compare/1.2.18...1.2.19) (2024-02-05) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * stop returning nulls ([#170](https://github.com/IBM/nodejs-idb-connector/issues/170)) ([f3924b4](https://github.com/IBM/nodejs-idb-connector/commit/f3924b476874e7954f96c9911c71ad44891a619e)) 9 | 10 | ## 1.2.18 11 | - build: Fix the install failure with the lastest npm 12 | 13 | ## 1.2.17 14 | - build(deps): Update deps to latest version 15 | - build: Add package-lock.json 16 | - fix: Binding parameters issues (#157) 17 | - ci: Add npm publish action (#161) 18 | 19 | ## 1.2.16 20 | - [build] Fix EBADPLATFORM build error on Node 18 / Python 3.9 21 | - [build] Support Python 3.9 changes 22 | 23 | ## 1.2.15 24 | - [fix] Check for Javascript empty-string in bindParams 25 | - [feat] Set SQL_ATTR_NON_HEXCCSID by default (#136) 26 | 27 | ## 1.2.14 28 | - [fix] No error return on SQL_NO_DATA_FOUND (sync) (#146) 29 | 30 | ## 1.2.13 31 | - [build] support Node.js v16 (#141) 32 | 33 | ## 1.2.12 34 | - [fix] stop setting error for SQL_NO_DATA_FOUND (#138) 35 | - [build] fix compiler warnings (#132) 36 | 37 | ## 1.2.11 38 | - [fix] UTF-8 data not properly formed error (#129) 39 | 40 | ## 1.2.10 41 | - [test] add test case for the input parameter truncation issue (#127) 42 | - [dbstmt] detect data length first to fix the messcode issue (#123) 43 | - [doc] update bindParameters examples (#121) 44 | 45 | ## 1.2.9 46 | - [dbstmt] add a new API fieldInfo (#115) 47 | 48 | ## 1.2.8 49 | - [dbstmt] fix: select-insert when binding strings (#114) 50 | 51 | ## 1.2.7 52 | - [dbstmt] add new simplified API bindParameters (#111) 53 | - [dbstmt] fix: Free LOB locators using CLI functions (#110) 54 | 55 | For the details of the new API bindParameters, please refer to the manual and the new example. 56 | The old API bindParams will be deprecated accordingly. 57 | 58 | ## 1.2.6 59 | - [dbstmt] Refine data length assumptions (#104) 60 | - [dbconn] Have dbconn.close disconnect if connected (#106) 61 | - [ci] Use the enhancement label for the feature template 62 | 63 | ## 1.2.5 64 | - [dbstmt] Fix incorrect float value returned from procedures (#99) 65 | - [dbstmt] Enable returning SQL_DOUBLE type as a JS Number (#104) 66 | - [dberror] Check SQLState and SQLCode when query success with info 67 | - [test] Refine test cases 68 | - [ci] Add issue template (#100) 69 | 70 | ## 1.2.4 71 | - [dbstmt] detect inconsistent data (#94) 72 | - [dberror] move all debugging functions to dberror.h 73 | - [test] add more test cases 74 | 75 | ## 1.2.3 76 | - [dbstmt] fix a memory leak issue (#90) 77 | - [dbstmt] use more meaningful variable names in code 78 | 79 | ## 1.2.2 80 | - [dbstmt] get accurate CLOB data length (#78) 81 | - [dbstmt] finalize buffer of binary data (#86) 82 | 83 | ## 1.2.1 84 | - [dbstmt] support returning numeric SQL data (#77) 85 | - [dbstmt] support binding to CLOB for long text (#78) 86 | - [dbstmt] fix the truncated issue for data type REAL (#64) 87 | - [doc] add SET PATH details (#74) 88 | - [doc] stmtError() conflicts with dbconn.debug(true) (#81) 89 | 90 | ## 1.2.0 91 | - Use N-API versions for build (one binary for all Node.js) 92 | - Patch to allow binding null values (#75) 93 | - Add Node.js v12 support 94 | - Remove Node.js v6 support 95 | 96 | ## 1.1.10 97 | 98 | - Retrieve data when SQLFetch return SQL_SUCCESS_WITH_INFO(#71) 99 | - Add the SQLCLI dependencies for developement build(#69) 100 | - Update example code for eslint(#60) 101 | 102 | ## 1.1.9 103 | 104 | - Guard bindings.gyp and build dummy when not on IBM i(#58) 105 | - Clearly state it is not usable on any platform except IBM i(#57) 106 | 107 | ## 1.1.8 108 | 109 | - Skip build unless installing on IBM i(#54) 110 | - Moved the repository to GitHub 111 | 112 | ## 1.1.7 113 | 114 | - Fixed the connection failure(#52) with user/password provided 115 | - Updated some test cases 116 | 117 | ## 1.1.6 118 | 119 | - Added alias shorthands for use during bindParams() 120 | - Added Add checks for value, io type and bindIndicator during bindParams() 121 | - Return null instead of empty [] when output params are not present, during execute() 122 | - Updated examples, API reference and test cases 123 | 124 | ## 1.1.5 125 | 126 | - Refined docs and trace for scrollable cursor 127 | - Added a new API stmt.getStmtDiag() as alias of stmt.stmtError() 128 | - Added CHANGLOG.md 129 | - Made buffer bigger for some unicode characters 130 | 131 | ## 1.1.4 132 | 133 | - Added test cases for the callback in dbconn.conn() 134 | - Updated examples with ES6 syntax 135 | - Required dep 'node-pre-gyp' 0.11.0 and above 136 | 137 | ## 1.1.3 138 | 139 | - Fixed the NAPI callback error in DbConn::Conn() 140 | - Ported and refined the API manual from 'developerWorks' 141 | 142 | ## 1.1.2 143 | 144 | - Removed 'node-gyp' from dependency list 145 | - Exposed data types symbols for params binding 146 | 147 | ## 1.1.1 148 | 149 | - Supported fetching result set from stored procedures 150 | 151 | ## 1.1.0 152 | 153 | - Swithed to N-API 154 | - Added Blob/Binary support and corresponding docs 155 | - Refined test cases and trace 156 | 157 | ## 1.0.13 158 | 159 | - Supported pre-compiled binary downloading 160 | - Fixed a memory leak issue 161 | 162 | ## 1.0.12 163 | 164 | - Fixed the truncate issue for big numbers 165 | 166 | ## 1.0.11 167 | 168 | - Supported Node.js v10.x 169 | 170 | ## 1.0.10 171 | 172 | - Refined the code for fetching results and binding params 173 | - Fixed a string param binding issue 174 | 175 | ## 1.0.9 176 | 177 | - Fixed a statement executing issue 178 | - Fixed a output param buffer size issue 179 | 180 | ## 1.0.8 181 | 182 | - Added a new API stmt.reset() 183 | - Fixed a param binding issue 184 | 185 | ## 1.0.7 186 | 187 | - Added support for BINARY SQL data 188 | - Fixed the 'in/out' param missing issue 189 | 190 | ## 1.0.6 191 | 192 | - Used accurate column width to save memory usage 193 | - Supported dynamic column width 194 | 195 | ## 1.0.5 196 | 197 | - Provided pre-compiled binary for npm 198 | - Renamed to idb-connector 199 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing In General 2 | 3 | Our project welcomes external contributions. If you have an itch, please feel 4 | free to scratch it. 5 | 6 | To contribute code or documentation, please submit a [pull request](https://github.com/IBM/nodejs-idb-connector/pulls). 7 | 8 | A good way to familiarize yourself with the codebase and contribution process is 9 | to look for and tackle low-hanging fruit in the [issue tracker](https://github.com/IBM/nodejs-idb-connector/issues). 10 | 11 | **Note: We appreciate your effort, and want to avoid a situation where a contribution 12 | requires extensive rework (by you or by us), sits in backlog for a long time, or 13 | cannot be accepted at all!** 14 | 15 | ## Proposing new features 16 | 17 | If you would like to implement a new feature, please [raise an issue](https://github.com/IBM/nodejs-idb-connector/issues) 18 | before sending a pull request so the feature can be discussed. This is to avoid 19 | you wasting your valuable time working on a feature that the project developers 20 | are not interested in accepting into the code base. 21 | 22 | ## Fixing bugs 23 | 24 | If you would like to fix a bug, please [raise an issue](https://github.com/IBM/nodejs-idb-connector/issues) before sending a 25 | pull request so it can be tracked. 26 | 27 | ## Legal 28 | 29 | We have tried to make it as easy as possible to make contributions. This 30 | applies to how we handle the legal aspects of contribution. We use the 31 | same approach - the [Developer's Certificate of Origin 1.1 (DCO)](https://github.com/hyperledger/fabric/blob/master/docs/source/DCO1.1.txt) - that the Linux® Kernel [community](https://elinux.org/Developer_Certificate_Of_Origin) 32 | uses to manage code contributions. 33 | 34 | We simply ask that when submitting a patch for review, the developer 35 | must include a sign-off statement in the commit message. 36 | 37 | Here is an example Signed-off-by line, which indicates that the 38 | submitter accepts the DCO: 39 | 40 | ```sh 41 | Signed-off-by: John Doe 42 | ``` 43 | 44 | You can include this automatically when you commit a change to your 45 | local git repository using the following command: 46 | 47 | ```sh 48 | git commit -s 49 | ``` 50 | 51 | ## Communication 52 | 53 | Please feel free to connect with us on our [Ryver forum](https://ibmioss.ryver.com/index.html#forums/1000127). 54 | 55 | You can join the Ryver community [here](https://ibmioss.ryver.com/application/signup/members/9tJsXDG7_iSSi1Q). 56 | 57 | ## Setup 58 | 59 | Ensure that all dependencies are installed. 60 | 61 | From the root of the project directory run: 62 | 63 | `npm install` 64 | 65 | ## Testing 66 | 67 | This project uses mocha for its tests. 68 | 69 | From the root of the project directory run: 70 | 71 | `npm test test/` 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) International Business Machines Corp. 2017 2 | All Rights Reserved 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 12 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 13 | IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js iDB Connector for IBM i 2 | 3 | The Node.js iDB Connector is an IBM i Node.js Db2 driver open source project from IBM 4 | 5 | [![NPM](https://nodei.co/npm/idb-connector.png?downloads=true&downloadRank=true)](https://nodei.co/npm/idb-connector/) 6 | 7 | [![Node-API v3 Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20v3%20Badge.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_node_api_version_matrix) 8 | 9 | ## Installation 10 | 11 | ```sh 12 | npm i idb-connector 13 | ``` 14 | 15 | **NOTE** This package only installs on IBM i systems. 16 | 17 | Then you can _require_ in your code, as shown below. 18 | 19 | ```js 20 | const db = require('idb-connector'); 21 | ``` 22 | 23 | ## Quick Example 24 | 25 | ### Example 1: Fetching data using the exec() API 26 | 27 | ```js 28 | const {dbconn, dbstmt} = require('idb-connector'); 29 | 30 | const sSql = 'SELECT STATE FROM QIWS.QCUSTCDT'; 31 | const connection = new dbconn(); 32 | connection.conn('*LOCAL'); 33 | const statement = new dbstmt(connection); 34 | 35 | statement.exec(sSql, (x) => { 36 | console.log(JSON.stringify(x)); 37 | statement.close(); 38 | connection.disconn(); 39 | connection.close(); 40 | }); 41 | 42 | ``` 43 | 44 | ### Example 2: Fetching data using the fetchAll() API 45 | 46 | ```js 47 | 48 | const {dbconn, dbstmt} = require('idb-connector'); 49 | 50 | const sSql = 'SELECT STATE FROM QIWS.QCUSTCDT'; 51 | const connection = new dbconn(); 52 | connection.conn('*LOCAL'); 53 | const statement = new dbstmt(connection); 54 | 55 | statement.prepare(sSql, () => { 56 | statement.execute(() => { 57 | statement.fetchAll((x) => { 58 | console.log(`Result is : ${JSON.stringify(x)}`); 59 | statement.close(); 60 | }); 61 | }); 62 | }); 63 | 64 | ``` 65 | 66 | ### Example 3: Call stored procedures 67 | 68 | ```js 69 | const {dbconn, dbstmt} = require('idb-connector'); 70 | 71 | const sql = 'CALL QXMLSERV.iPLUG512K(?,?,?,?)'; 72 | const connection = new dbconn(); 73 | connection.conn('*LOCAL'); 74 | const statement = new dbstmt(connection); 75 | 76 | const ipc = '*NA'; 77 | const ctl = '*here'; 78 | const xmlIn = `system 'wrksbs'`; 79 | const xmlOut = ''; 80 | 81 | statement.prepare(sql, () => { 82 | statement.bindParameters([ipc, ctl, xmlIn, xmlOut], () => { 83 | statement.execute((out) => { 84 | for (let i = 0; i < out.length; i += 1) { 85 | console.log(out[i]); 86 | } 87 | statement.close(); 88 | connection.disconn(); 89 | connection.close(); 90 | }); 91 | }); 92 | }); 93 | 94 | ``` 95 | 96 | ## API Reference 97 | 98 | [Db2 for i Access APIs](https://github.com/IBM/nodejs-idb-connector/blob/master/docs/README.md) 99 | 100 | ## Change Log 101 | 102 | View [`CHANGELOG.md`](https://github.com/IBM/nodejs-idb-connector/blob/master/CHANGELOG.md) file. 103 | 104 | ## Build 105 | 106 | Note that building isn't necessary for end-users and is more for developers looking to compile the native Node.js extensions (C code). 107 | 108 | ```sh 109 | git clone git@github.com:IBM/nodejs-idb-connector.git 110 | cd nodejs-idb-connector 111 | npm install --build-from-source 112 | ``` 113 | ### Build Dependencies 114 | Note: sqlcli header files, GCC, and Python are required to compile the code. 115 | 116 | ```sh 117 | yum install sqlcli-devel 118 | yum group install 'Development tools' 119 | yum install python2 120 | ``` 121 | 122 | ## License 123 | 124 | [`MIT`](https://github.com/IBM/nodejs-idb-connector/blob/master/LICENSE) 125 | 126 | ## **Contributing** 127 | 128 | Please read the [contribution guidelines](https://github.com/IBM/nodejs-idb-connector/blob/master/CONTRIBUTING.md). 129 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'db2ia', 5 | "conditions": [ 6 | ['OS in "aix os400"', { 7 | 'variables': { 8 | 'os_name': ' { 97 | if (error) { 98 | throw error; 99 | } 100 | console.log(`Result Set: ${JSON.stringify(result)}`); 101 | 102 | statement.close(); // Clean up the statement object. 103 | connection.disconn(); // Disconnect from the database. 104 | connection.close(); // Clean up the connection object. 105 | }); 106 | 107 | ``` 108 | 109 | ___ 110 | 111 | ### Sync Exec 112 | 113 | ```javascript 114 | 115 | const {dbconn, dbstmt} = require('idb-connector'); 116 | 117 | const sql = 'SELECT STATE FROM QIWS.QCUSTCDT'; 118 | const connection = new dbconn(); // Create a connection object. 119 | 120 | connection.conn('*LOCAL'); // Connect to the database. 121 | 122 | const statement = new dbstmt(connection); // Create a statement object. 123 | 124 | const result = statement.execSync(sql); 125 | console.log(`Result Set: ${JSON.stringify(result)}`); 126 | 127 | statement.close(); // Clean up the statement object. 128 | connection.disconn(); // Disconnect from the database. 129 | connection.close(); // Clean up the connection object. 130 | 131 | ``` 132 | 133 | ___ 134 | 135 | ### Async Prepare Bind Execute 136 | 137 | Calling a Stored Procedure 138 | 139 | ```javascript 140 | 141 | const {dbconn, dbstmt} = require('idb-connector'); 142 | 143 | const sql = 'call QXMLSERV.iPLUG512K(?,?,?,?)'; 144 | const connection = new dbconn(); 145 | const ipc = '*NA'; 146 | const ctl = '*here'; 147 | const xmlIn = `system 'wrksbs'`; 148 | const xmlOut = ''; 149 | const params = [ipc, ctl, xmlIn, xmlOut]; 150 | 151 | connection.conn('*LOCAL'); 152 | const statement = new dbstmt(connection); 153 | 154 | statement.prepare(sql, (error) => { 155 | if (error) { 156 | throw error; 157 | } 158 | statement.bindParameters(params, (error) => { 159 | if (error) { 160 | throw error; 161 | } 162 | statement.execute((out, error) => { 163 | if (error) { 164 | throw error; 165 | } 166 | console.log(`Parameters: ${JSON.stringify(out)}`); 167 | statement.close(); 168 | connection.disconn(); 169 | connection.close(); 170 | }); 171 | }); 172 | }); 173 | 174 | ``` 175 | 176 | ___ 177 | 178 | ### Sync Prepare Bind Execute 179 | 180 | ```javascript 181 | 182 | const {dbconn, dbstmt} = require('idb-connector'); 183 | 184 | const sql = 'call QXMLSERV.iPLUG512K(?,?,?,?)'; 185 | const connection = new dbconn(); 186 | const ipc = '*NA'; 187 | const ctl = '*here'; 188 | const xmlIn = `system 'wrksbs'`; 189 | const xmlOut = ''; 190 | const params = [ipc, ctl, xmlIn, xmlOut]; 191 | 192 | connection.conn('*LOCAL'); 193 | 194 | const statement = new dbstmt(connection); 195 | 196 | statement.prepareSync(sql); 197 | statement.bindParametersSync(params); 198 | 199 | const out = statement.executeSync(); 200 | 201 | console.log(`Parameters: ${JSON.stringify(out)}`); 202 | 203 | statement.close(); 204 | connection.disconn(); 205 | connection.close(); 206 | 207 | ``` 208 | 209 | ___ 210 | 211 | ### Async Fetch 212 | 213 | Asynchronously retrieve one row from the result set 214 | 215 | ```javascript 216 | 217 | const {dbconn, dbstmt} = require('idb-connector'); 218 | 219 | const connection = new dbconn(); 220 | 221 | connection.conn('*LOCAL'); 222 | 223 | const statement = new dbstmt(connection); 224 | 225 | statement.prepare('SELECT * FROM QIWS.QCUSTCDT', (error) => { 226 | if (error) { 227 | throw error; 228 | } 229 | statement.execute((error) => { 230 | if (error) { 231 | throw error; 232 | } 233 | 234 | statement.fetch((row, rc) => { 235 | if (rc instanceof Error) { 236 | throw rc; 237 | } 238 | console.log(`Row: ${JSON.stringify(row)}\n`); 239 | 240 | statement.close(); 241 | connection.disconn(); 242 | connection.close(); 243 | }); 244 | }); 245 | }); 246 | 247 | ``` 248 | 249 | ___ 250 | 251 | ### Sync Fetch 252 | 253 | Synchronously retrieve one row from the result set 254 | 255 | ```javascript 256 | 257 | const {dbconn, dbstmt} = require('idb-connector'); 258 | 259 | const connection = new dbconn(); 260 | 261 | connection.conn('*LOCAL'); 262 | 263 | const statement = new dbstmt(connection); 264 | 265 | statement.prepareSync('SELECT * FROM QIWS.QCUSTCDT'); 266 | statement.executeSync(); 267 | 268 | const row = statement.fetchSync(); 269 | 270 | console.log(`Row:${JSON.stringify(row)}\n`); 271 | 272 | statement.close(); 273 | connection.disconn(); 274 | connection.close(); 275 | 276 | ``` 277 | 278 | ___ 279 | 280 | ### Async FetchAll 281 | 282 | Asynchronously retrieve all rows from the result set 283 | 284 | ```javascript 285 | 286 | const {dbstmt, dbconn} = require('idb-connector'); 287 | 288 | const connection = new dbconn(); 289 | 290 | connection.conn('*LOCAL'); 291 | 292 | const statement = new dbstmt(connection); 293 | 294 | statement.prepare('SELECT * FROM QIWS.QCUSTCDT', (error) => { 295 | if (error) { 296 | throw error; 297 | } 298 | statement.execute((error) => { 299 | if (error) { 300 | throw error; 301 | } 302 | statement.fetchAll((result, error) => { 303 | if (error) { 304 | throw error; 305 | } 306 | console.log(`Result Set:${JSON.stringify(result)}\n`); 307 | statement.close(); 308 | connection.disconn(); 309 | connection.close(); 310 | }); 311 | }); 312 | }); 313 | 314 | ``` 315 | 316 | ___ 317 | 318 | ### Sync FetchAll 319 | 320 | Synchronously retrieve all rows from the result set 321 | 322 | ```javascript 323 | 324 | const {dbconn, dbstmt} = require('idb-connector'); 325 | 326 | const connection = new dbconn(); 327 | 328 | connection.conn('*LOCAL'); 329 | 330 | const statement = new dbstmt(connection); 331 | 332 | statement.prepareSync('SELECT * FROM SCHEMA.MYTABLE'); 333 | statement.executeSync(); 334 | 335 | const result = statement.fetchAllSync(); 336 | 337 | console.log(`Result Set:${JSON.stringify(result)}`); 338 | 339 | statement.close(); 340 | connection.disconn(); 341 | connection.close(); 342 | 343 | ``` 344 | 345 | ## Class: dbconn 346 | 347 | The dbconn class is used to create a connection object. 348 | 349 | Once the idb-connector is installed you can gain access with: 350 | 351 | `const {dbconn} = require('idb-connector');` 352 | 353 | use the new operator to instantiate it 354 | 355 | `const conn = new dbconn();` 356 | 357 | Once instantiated the methods documented below can be performed. 358 | 359 | Make sure to call `disconn` and `close` when finished. 360 | 361 | ### Contructor: dbconn() 362 | 363 | **Description:** 364 | 365 | Allocates a new connection handle, ensure `conn` function is called to connect to the target database. 366 | 367 | ___ 368 | 369 | ### dbconn.setConnAttr(attribute, value) 370 | 371 | **Description:** 372 | 373 | Sets a connection attribute to a provided valid value. 374 | 375 | **Syntax:** 376 | 377 | setConnAttr(attribute, value) 378 | 379 | **Parameters** 380 | 381 | - **attribute:** `number(integer)` is the connection attribute to set. 382 | - **value:** `string | number(integer)` the value to set the specified attribute to. 383 | 384 | **Returns:** 385 | 386 | `boolean(true)` upon success otherwise an error is thrown. 387 | 388 | **DB2 CLI API:** SQLSetConnectAttr 389 | 390 | **Valid Scope:** Before connecting to a database. 391 | 392 | **Comments:** 393 | 394 | Auto Commit attribute is automatically enabled. 395 | 396 | Refer to this [table](https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/cli/rzadpfnsconx.htm%23rzadpfnsconx__tbcono) for more details. 397 | 398 | ___ 399 | 400 | ### dbconn.getConnAttr(attribute) 401 | 402 | **Description:** 403 | 404 | Returns the current settings for the specified connection attribute. 405 | 406 | **Syntax:** 407 | 408 | getConnAttr(attribute) 409 | 410 | **Parameters:** 411 | 412 | - **attribute:** `number(integer)` the connection attribute to set. 413 | 414 | **Returns:** 415 | 416 | `string | number(integer)` depending on the attribute. 417 | 418 | **DB2 CLI API:** SQLGetConnectAttr 419 | 420 | **Valid Scope:** After instantiated the connection object. 421 | 422 | **Comments:** 423 | 424 | Refer to this [table](https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/cli/rzadpfngcona.htm) for more details. 425 | ___ 426 | 427 | ### dbconn.conn(database) 428 | 429 | **Description:** 430 | 431 | Establishes a connection to the target database. 432 | 433 | **NOTE** 434 | 435 | - `*LOCAL` can be provided as the `database` when connecting to a local database, allowing `user` & `password` to be optionally passed. 436 | 437 | - To access SQL functions and stored procedures without fully qualifying them, ensure that the CURRENT PATH is set correctly using the SET PATH SQL statement. See [here](https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_74/db2/rbafzsetpath.htm) for more info. 438 | 439 | **Syntax 1:** 440 | conn(database) 441 | 442 | **Syntax 2:** 443 | conn(database, callback) 444 | 445 | **Syntax 3:** 446 | conn(database, user, password) 447 | 448 | **Syntax 4:** 449 | conn(database, user, password, callback) 450 | 451 | **Parameters:** 452 | 453 | - **database:** `string` is the name or alias name of the database. 454 | 455 | - **user:** `string` is the authorization name (user identifier). 456 | 457 | - **password:** `string` is the authentication string (password). 458 | 459 | - **callback:** `function` is a callback function running after the `conn` is complete 460 | 461 | **DB2 CLI API:** SQLConnect 462 | 463 | **Valid Scope:** Before calling the dbstmt.exec() or dbstmt.prepare() function. 464 | ___ 465 | 466 | ### dbconn.disconn() 467 | 468 | **Description:** 469 | 470 | Ends the connection associated with the database connection handle. 471 | 472 | After calling this function, either call `conn` to connect to another database or `close`. 473 | 474 | **Syntax:** 475 | 476 | disconn() 477 | 478 | **DB2 CLI API:** SQLDisconnect 479 | 480 | **Valid Scope:** After calling the conn() function. 481 | 482 | **Returns:** 483 | 484 | `boolean(true)` upon success otherwise an error is thrown. 485 | 486 | ___ 487 | 488 | ### dbconn.close() 489 | 490 | **Description:** 491 | 492 | Frees all DB2 for i resources associated with the connection object. 493 | 494 | `disconn()` must be called before calling this function. 495 | 496 | **Syntax:** 497 | 498 | close() 499 | 500 | **DB2 CLI API:** SQLFreeConnect 501 | 502 | **Valid Scope:** After calling the disconn() function. 503 | 504 | **Returns:** 505 | 506 | `boolean(true)` upon success otherwise an error is thrown. 507 | 508 | ___ 509 | 510 | ### dbconn.debug(flag) 511 | 512 | **Description:** 513 | 514 | Enables or disables verbose output to the console. 515 | 516 | **Syntax:** 517 | 518 | debug(flag) 519 | 520 | **Parameters:** 521 | 522 | - **flag:** `boolean` to turn debug mode on or off. Default value is `false`. 523 | 524 | **Returns:** 525 | 526 | `boolean` the current state of the debug flag otherwise an error is thrown. 527 | 528 | **Valid Scope:** Entire life cycle. 529 | 530 | ___ 531 | 532 | ### dbconn.validStmt(sql) 533 | 534 | **Description:** 535 | 536 | Checks if the SQL string is valid and interprets vendor escape clauses. 537 | 538 | If the original SQL string that is passed by the application contains vendor escape clause sequences, 539 | 540 | DB2 for i CLI returns the transformed SQL string that is seen by the data source (with vendor escape clauses either converted or discarded as appropriate). 541 | 542 | **Syntax:** 543 | 544 | validStmt(sql) 545 | 546 | **Parameters:** 547 | 548 | - **sql:** `string` that needs to be checked and escaped. 549 | 550 | **Returns:** 551 | 552 | `string` the transformed sql string upon success, otherwise an error is thrown. 553 | 554 | **DB2 CLI API:** SQLNativeSql 555 | 556 | **Valid Scope:** After calling `conn` function 557 | 558 | ___ 559 | 560 | ## Class dbstmt 561 | 562 | Once the idb-connector is installed you can gain access with: 563 | 564 | `const {dbstmt} = require('idb-connector');` 565 | 566 | A connected `dbconn` object is required to create a new `dbstmt` object from. 567 | 568 | ```javascript 569 | const {dbconn, dbstmt} = require('idb-connector'); 570 | 571 | const connection = new dbconn(); 572 | 573 | connection.conn('*LOCAL'); 574 | 575 | let statement = new dbstmt(dbconn); 576 | 577 | ``` 578 | 579 | Once instantiated the methods documented below can be performed. 580 | 581 | Make sure to call `close` when finished with the statement object. 582 | 583 | ### Constructor: dbstmt(connection) 584 | 585 | **Parameters:** 586 | 587 | - **dbconn:** `dbconn object` the connection object to create the statement from. 588 | 589 | - Ensure `connection` has connected to the database first with `conn` function. 590 | 591 | ___ 592 | 593 | ### dbstmt.setStmtAttr(attribute, value) 594 | 595 | **Description:** 596 | 597 | Set an attribute of a specific statement handle. 598 | 599 | To set an option for all statement handles associated with a connection handle `setConnAttr` can be used. 600 | 601 | **Syntax:** 602 | 603 | setStmtAttr(attribute, value) 604 | 605 | **Parameters:** 606 | 607 | - **attribute:** `number(integer)` is the statement attribute to set. 608 | 609 | - **value:** `string | number (integer)` the value to set the specified attribute to. 610 | 611 | **DB2 CLI API:** SQLSetStmtAttr 612 | 613 | **Valid Scope:** After allocating the statement handler. 614 | ___ 615 | 616 | ### dbstmt.getStmtAttr() 617 | 618 | **Description:** 619 | 620 | Returns the current settings for the specified connection option 621 | 622 | **Syntax:** 623 | 624 | getStmtAttr(int Attribute) 625 | 626 | **Parameters** 627 | 628 | - **attribute:** is the connection attribute to set. Refer to this [table](https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/cli/rzadpfngstma.htm) for more details. 629 | 630 | **Returns:** 631 | 632 | `string | number (integer)` depending on the attribute. 633 | 634 | **DB2 CLI API:** SQLGetStmtAttr 635 | 636 | **Valid Scope:** After allocating the statement handler. 637 | 638 | ___ 639 | 640 | ### dbstmt.exec(sql, callback) 641 | 642 | **Description:** 643 | 644 | Asynchronously runs the specified SQL statement. 645 | 646 | **NOTE** use the `execute` function to call stored procedures 647 | 648 | **Syntax:** 649 | 650 | exec(sql,callback) 651 | 652 | **Parameters** 653 | 654 | - **sql:** `string` is the sql statement to execute. 655 | 656 | - **callback(resultSet, error):** `function` to process after `exec` is complete. 657 | - **resultSet:** `array` of `objects` each object represents a row of data. 658 | - If an error occurred or there is no `resultSet` it is set to `null`. 659 | - **error:** `Error object` when `execSync` is unsuccessful. Otherwise `error` is set to `null`. 660 | 661 | **DB2 CLI API:** SQLExecDirect 662 | 663 | **Valid Scope:** After calling the `conn` function. 664 | 665 | **Example:** [Here](#async-exec) 666 | 667 | ___ 668 | 669 | ### dbstmt.execSync(sql [, callback]) 670 | 671 | **Description:** 672 | 673 | The synchronous blocking version of `exec`. 674 | 675 | **Syntax 1:** 676 | 677 | execSync(sql) 678 | 679 | **Parameters** 680 | 681 | - **sql:** `string` is the sql statement to execute. 682 | 683 | **Returns:** 684 | 685 | - **resultSet:** `array` of `objects` each object represents a row of data. 686 | - If an error occurred or there is no `resultSet` , `null` will be returned. 687 | 688 | **Syntax 2:** 689 | 690 | execSync(sql, callback(resultSet, error)) 691 | 692 | **Parameters** 693 | 694 | - **sql:** `string` is the sql statement to execute. 695 | 696 | - **callback(resultSet, error):** `function` to process after `execSync` is complete. 697 | - **resultSet:** `array` of `objects` each object represents a row of data. If an error occurred or there is no `resultSet` it is set to `null`. 698 | 699 | - **error:** `Error object` when `execSync` is unsuccessful. Otherwise `error` is set to `null`. 700 | 701 | **Comments:** 702 | 703 | - It is recommended to invoke the `numFields`, `numRows`, `fieldName` and other result set related functions in this callback function, because they rely on the temporal result set in memory. 704 | - After running `execSync` the result set will be destroyed and cleaned up. 705 | 706 | **DB2 CLI API:** SQLExecDirect 707 | 708 | **Valid Scope:** After calling the conn() function. 709 | 710 | **Example:** [Here](#sync-exec) 711 | 712 | ___ 713 | 714 | ### dbstmt.prepare(sql, callback) 715 | 716 | **Description:** 717 | 718 | Asynchronously associates an SQL statement with the input statement handle and sends the statement to the DBMS to be prepared. 719 | 720 | **Syntax:** 721 | 722 | prepare(sql, callback) 723 | 724 | **Parameters** 725 | 726 | - **sql:** is the SQL statement string. 727 | 728 | - **callback(error)**: `function` to process after `prepare` is complete. 729 | - **error**: `Error object` when `prepare` is unsuccessful. Otherwise `error` is set to `null`. 730 | 731 | **Comments:** 732 | 733 | If the statement handler has been used with a SELECT statement, 734 | 735 | `closeCursor` must be called to close the cursor, before calling `prepare` again. 736 | 737 | **DB2 CLI API:** SQLPrepare 738 | 739 | **Valid Scope:** 740 | 741 | - After calling the conn() function. 742 | 743 | - Before calling the execute() or bindParam() function. 744 | 745 | **Example:** [Here](#async-prepare-bind-execute) 746 | ___ 747 | 748 | ### dbstmt.prepareSync(sql [, callback]) 749 | 750 | **Description:** 751 | 752 | Synchronous version of `prepare`. 753 | 754 | **Syntax 1:** 755 | 756 | prepareSync(sql) 757 | 758 | **Parameters:** 759 | 760 | - **sql:** `string` the sql statement to prepare. 761 | 762 | **Returns:** 763 | 764 | `void` no return type, if an error occurred it will be thrown. 765 | 766 | **Syntax 2:** 767 | 768 | prepareSync(sql, callback) 769 | 770 | **Parameters:** 771 | 772 | - **sql:** `string` the sql statement to prepare. 773 | 774 | - **callback(error)**: `function` to process after `prepareSync` is complete. 775 | - **error**: `Error object` when `prepareSync` is unsuccessful. Otherwise `error` is set to `null`. 776 | 777 | **Comments:** 778 | 779 | If the statement handler has been used with a SELECT statement 780 | `closeCursor` must be called first before calling `prepareSync` again. 781 | 782 | **DB2 CLI API:** SQLPrepare 783 | 784 | **Valid Scope:** 785 | 786 | - After calling the `conn` function. 787 | 788 | - Before calling the `executeSync` or `bindParamSync` function. 789 | 790 | **Example:** [Here](#sync-prepare-bind-execute) 791 | 792 | ___ 793 | 794 | ### dbstmt.bindParam(params, callback) [deprecated] 795 | 796 | **Description:** 797 | 798 | Asynchronously associate (bind) parameter markers in an SQL statement to application variables. 799 | 800 | Data is transferred from the application to the Database Management System (DBMS) when `execute` function is called. 801 | 802 | Data conversion might occur when the data is transferred. 803 | 804 | This function must also be used to bind to a parameter of a stored procedure where the parameter can be: input, output, or both 805 | 806 | **Syntax:** 807 | 808 | bindParam(params, callback) 809 | 810 | **Parameters:** 811 | 812 | - **params**: `array` representing the binding parameter list. Each parameter element will also be an Array with 3 values `[value, io, indicator]`. 813 | 814 | - `value` is the parameter to bind. 815 | - `io` specifies whether the parameter is for input, output, or both. 816 | - `io` can be: 817 | - SQL_PARAM_INPUT 818 | - SQL_PARAM_OUTPUT 819 | - SQL_PARAM_INPUT_OUTPUT 820 | - Shorthand equivleants are: 821 | - IN 822 | - OUT 823 | - INOUT 824 | - `indicator` specifies how to process the parameter. For example to process a string as a CLOB set the indicator to CLOB. 825 | - `indcator` can be: 826 | - CHAR 827 | - INT 828 | - NUMERIC 829 | - BINARY 830 | - BLOB 831 | - CLOB 832 | - BOOLEAN 833 | - NULL 834 | 835 | These values are constants which are attached to object returned when you `const idb = require('idb-connector')`. 836 | 837 | You can access the constants like: `idb.IN`. 838 | 839 | - **callback(error):** `function` to process after `bindParam` is complete. 840 | - **error:** `Error object` when `bindParam` is unsuccessful. Otherwise `error` is set to `null`. 841 | 842 | **DB2 CLI API:** SQLBindParameter 843 | 844 | **Valid Scope:** In the callback function of the `prepare` function. 845 | 846 | ___ 847 | 848 | ### dbstmt.bindParamSync(params [, callback]) [deprecated] 849 | 850 | **Description:** 851 | 852 | Synchronous version of `bindParam`. 853 | 854 | **Syntax 1:** 855 | 856 | bindParamSync(params) 857 | 858 | **Parmeters:** 859 | 860 | - **params**: as described in [bindParam](#dbstmtbindparamparams-callback) 861 | 862 | **Returns:** 863 | 864 | `void` no return type, if an error occurred it will be thrown. 865 | 866 | **Syntax 2:** 867 | 868 | bindParamSync(params, callback) 869 | 870 | **Parameters** 871 | 872 | - **params**: as described in [bindParam](#dbstmtbindparamparams-callback) 873 | 874 | - **callback(error)**: `function` to process after `bindParamSync` is complete. 875 | - **error**: `Error object` when `bindParamSync` is unsuccessful. Otherwise `error` is set to `null`. 876 | 877 | **DB2 CLI API:** SQLBindParameter 878 | 879 | **Valid Scope:** 880 | 881 | - After calling the `prepareSync` function. 882 | - Before calling the `executeSync` function. 883 | 884 | ___ 885 | 886 | ### dbstmt.bindParameters(params, callback) 887 | 888 | **Description:** 889 | 890 | Asynchronously associate (bind) parameter markers in an SQL statement to application variables. 891 | 892 | Data is transferred from the application to the Database Management System (DBMS) when `execute` function is called. 893 | 894 | Data conversion might occur when the data is transferred. 895 | 896 | This function must also be used to bind to a parameter of a stored procedure where the parameter can be: input, output, or both 897 | 898 | **Syntax:** 899 | 900 | bindParameters(params, callback) 901 | 902 | **Parameters:** 903 | 904 | - **params**: `array` representing the binding parameter list. 905 | 906 | - **callback(error):** `function` to process after `bindParameters` is complete. 907 | - **error:** `Error object` when `bindParameters` is unsuccessful. Otherwise `error` is set to `null`. 908 | 909 | **DB2 CLI API:** SQLBindParameter 910 | 911 | **Valid Scope:** In the callback function of the `prepare` function. 912 | 913 | **Example:** [Here](#async-prepare-bind-execute) 914 | 915 | ___ 916 | 917 | ### dbstmt.bindParameterSync(params [, callback]) 918 | 919 | **Description:** 920 | 921 | Synchronous version of `bindParameters`. 922 | 923 | **Syntax 1:** 924 | 925 | bindParameterSync(params) 926 | 927 | **Parmeters:** 928 | 929 | - **params**: as described in [bindParameters](#dbstmtbindparametersparams-callback) 930 | 931 | **Returns:** 932 | 933 | `void` no return type, if an error occurred it will be thrown. 934 | 935 | **Syntax 2:** 936 | 937 | bindParameterSync(params, callback) 938 | 939 | **Parameters** 940 | 941 | - **params**: as described in [bindParameters](#dbstmtbindparametersparams-callback) 942 | 943 | - **callback(error)**: `function` to process after `bindParameterSync` is complete. 944 | - **error**: `Error object` when `bindParameterSync` is unsuccessful. Otherwise `error` is set to `null`. 945 | 946 | **Example:** [Here](#sync-prepare-bind-execute) 947 | 948 | **DB2 CLI API:** SQLBindParameter 949 | 950 | **Valid Scope:** 951 | 952 | - After calling the `prepareSync` function. 953 | - Before calling the `executeSync` function. 954 | 955 | ___ 956 | 957 | ### dbstmt.execute(callback) 958 | 959 | **Description:** 960 | 961 | Asynchronously Runs a statement that was successfully prepared using `prepare`. 962 | 963 | The statement is processed with the current values of any application variables that were bound to parameters markers by `bindParam`. 964 | 965 | **Syntax:** 966 | 967 | execute(callback(outputParams, error)) 968 | 969 | **Parameters** 970 | 971 | - **callback(outputParams, error):** `function` to process after `execute` is complete. 972 | - **outputParams**: an `array` of an output parameters. If an error occurred or no output parameters are available `outputParams` is set to `null`. 973 | 974 | - **error**: `Error object` when `execute` is unsuccessful. Otherwise `error` is set to `null`. 975 | 976 | **DB2 CLI API:** SQLExecute 977 | 978 | **Valid Scope:** In the callback function of the `prepare` or `bindParam` function. 979 | 980 | **Example:** [Here](#async-prepare-bind-execute) 981 | 982 | ___ 983 | 984 | ### dbstmt.executeSync([callback]) 985 | 986 | **Description:** 987 | 988 | The synchronized version of `execute`. 989 | 990 | **Syntax 1:** 991 | 992 | executeSync() 993 | 994 | **Returns:** 995 | 996 | - **outputParams**: an `array` of output parameters. If an error occured it is thrown. 997 | - If no output parameters are available `null` is returned. 998 | 999 | **Syntax 2:** 1000 | 1001 | executeSync(callback) 1002 | 1003 | **Parameters:** 1004 | 1005 | - **callback(outputParams, error):** `function` to process after `executeSync` is complete. 1006 | - **outputParams**: `array` of an output parameters. If an error occured or no output parameters are available `outputParams` is set to `null`. 1007 | 1008 | - **error**: `Error object` when `executeSync` is unsuccessful. Otherwise `error` is set to `null`. 1009 | 1010 | **Comments:** 1011 | 1012 | If the statement also returns a result set, user can issue the `fetch` function to retrieve the data row by row. 1013 | 1014 | **DB2 CLI API:** SQLExecute 1015 | 1016 | **Valid Scope:** After calling the prepareSync() function. 1017 | 1018 | **Example:** [Here](#sync-prepare-bind-execute) 1019 | ___ 1020 | 1021 | ### dbstmt.nextResult() 1022 | 1023 | **Description:** 1024 | 1025 | Determines whether there is more information available on the statement handle that has been associated with a stored procedure that is returning multiple result sets. 1026 | 1027 | **Syntax:** 1028 | 1029 | nextResult() 1030 | 1031 | **Comments:** 1032 | 1033 | After completely processing the first result set, the application can call `nextResult` to determine if another result set is available. 1034 | 1035 | If the current result set has unfetched rows, `nextResult` discards them by closing the cursor. 1036 | 1037 | **DB2 CLI API:** SQLMoreResults 1038 | 1039 | **Valid Scope:** After calling the execute() function. 1040 | 1041 | ___ 1042 | 1043 | ### dbstmt.commit() 1044 | 1045 | **Description:** 1046 | 1047 | Commit all changes to the database that have been made on the connection since connect time or the previous call to `commit`. 1048 | 1049 | **Syntax:** 1050 | 1051 | commit() 1052 | 1053 | **DB2 CLI API:** SQLTransact 1054 | 1055 | **Valid Scope:** After calling the execute() or exec() function. 1056 | 1057 | **Comments:** 1058 | 1059 | Auto commit is enabled by default. 1060 | 1061 | ___ 1062 | 1063 | ### dbstmt.rollback() 1064 | 1065 | **Description:** 1066 | 1067 | Rollback all changes to the database that have been made on the connection since connect time or the previous call to `commit`. 1068 | 1069 | **Syntax:** 1070 | 1071 | rollback() 1072 | 1073 | **DB2 CLI API:** SQLTransact 1074 | 1075 | **Valid Scope:** After calling the `execute` or `exec` function. 1076 | 1077 | ___ 1078 | 1079 | ### dbstmt.closeCursor() 1080 | 1081 | **Description:** 1082 | 1083 | Calling `closeCursor` closes any cursor associated with the dbstmt object and discards any pending results. 1084 | 1085 | If no open cursor is associated with the dbstmt object, the function has no effect. 1086 | 1087 | If the dbstmt object references a stored procedure that has multiple result sets, `closeCursor` closes only the current result set. Any additional result sets remain open and usable. 1088 | 1089 | If you want to reuse the dbstmt object, please call `closeCursor` before calling `exec` or `prepare` for another SQL statement. 1090 | 1091 | **Syntax:** 1092 | 1093 | closeCursor() 1094 | 1095 | **DB2 CLI API:** SQLCloseCursor 1096 | 1097 | **Valid Scope:** After calling the fetch() or fetchAll() function. 1098 | 1099 | ___ 1100 | 1101 | ### dbstmt.close() 1102 | 1103 | **Description:** 1104 | 1105 | DB2 for i resources associated with the statement object are freed. 1106 | 1107 | The open cursor, if any, is closed and all pending results are discarded. 1108 | 1109 | **Syntax:** 1110 | 1111 | close() 1112 | 1113 | **DB2 CLI API:** SQLFreeStmt 1114 | 1115 | **Valid Scope:** After executing an SQL statement and processing the results. 1116 | 1117 | ___ 1118 | 1119 | ### dbstmt.fetch([orient,] [offset,] callback) 1120 | 1121 | **Description:** 1122 | 1123 | Asynchronously advances the cursor to the next row of the result set, and retrieves any bound columns. 1124 | 1125 | Or positions the cursor based on the requested orientation and then retrieves any bound columns. 1126 | 1127 | 1128 | **Syntax 1:** 1129 | 1130 | fetch(callback) 1131 | 1132 | **Parameters** 1133 | 1134 | - **callback(row, error):** `function` to process after `fetchS` is complete. 1135 | - **row**: `object` representing a row of data. If an error occured or there is nothing to fetch `row` is set to `null`. 1136 | 1137 | - **error**: `Error object` when `fetch` is unsuccessful. Otherwise `error` is set to the return code from `SQLFETCH`. When error = `SQL_NO_DATA_FOUND` the end of the result set has been reached. 1138 | 1139 | **Syntax 2:** 1140 | 1141 | fetch(orient, offset, callback) 1142 | 1143 | **Parameters** 1144 | 1145 | - **orient:** `number(integer)` sets the fetch orientation. The valid values are below: 1146 | 1147 | - `SQL_FETCH_ABSOLUTE`: Move to the row specified by the Offset argument. 1148 | 1149 | - `SQL_FETCH_FIRST`: Move to the first row of the result set. 1150 | 1151 | - `SQL_FETCH_LAST`: Move to the last row of the result set. 1152 | 1153 | - `SQL_FETCH_NEXT`: Move to the row following the current cursor position. 1154 | 1155 | - `SQL_FETCH_PRIOR`: Move to the row preceding the current cursor position. 1156 | 1157 | - `SQL_FETCH_RELATIVE` If Offset is: 1158 | 1159 | - Positive, advance the cursor that number of rows. 1160 | 1161 | - Negative, back up the cursor that number of rows. 1162 | 1163 | - Zero, do not move the cursor. 1164 | 1165 | - **Offset:** `number(integer)` is the row offset for relative positioning. 1166 | 1167 | **Note:** To use orientation `SQL_FETCH_RELATIVE` with `Offset`, the cursor must be **dynamic**. 1168 | 1169 | `stmt.setStmtAttr(idb.SQL_ATTR_CURSOR_TYPE, idb.SQL_CURSOR_DYNAMIC);` 1170 | 1171 | - **callback(row, rc):** `function` to process after `fetch` is complete. 1172 | - **row**: `object` representing a row of data. If an error occured or there is nothing to fetch `row` is set to `null`. 1173 | 1174 | - **rc**: `Error object` when `fetch` is unsuccessful. Otherwise `rc` is set to the return code from `SQLFETCH`. When error = `SQL_NO_DATA` the end of the result set has been reached. 1175 | 1176 | **DB2 CLI API:** SQLFetch or SQLFetchScroll 1177 | 1178 | **Valid Scope:** When the result set is available. 1179 | 1180 | **Example:** [Here](#async-fetch) 1181 | 1182 | ___ 1183 | 1184 | ### dbstmt.fetchSync() 1185 | 1186 | **Description:** 1187 | 1188 | Synchronous version of `fetch`. 1189 | 1190 | **Syntax 1:** 1191 | 1192 | fetchSync() 1193 | 1194 | **Syntax 2:** 1195 | 1196 | fetchSync(function Callback(Row)) 1197 | 1198 | **Syntax 3:** 1199 | 1200 | fetchSync(int Orient, int Offset) 1201 | 1202 | **Syntax 4:** 1203 | 1204 | fetchSync(int Orient, int Offset, function Callback(Row)) 1205 | 1206 | **Parameters** 1207 | 1208 | - **orient:** as described in `fetch` above 1209 | 1210 | - **offset:** as described in `fetch` above. 1211 | 1212 | - **callback(row, error):** `function` to process after `fetchSync` is complete. 1213 | - **row**: `object` representing a row of data. If an error occured or there is nothing to fetch `row` is set to `null`. 1214 | 1215 | - **error**: `Error object` when `fetch` is unsuccessful. Otherwise `error` is set to the return code from `SQLFETCH`. When error = `SQL_NO_DATA` the end of the result set has been reached. 1216 | 1217 | **DB2 CLI API:** SQLFetch or SQLFetchScroll 1218 | 1219 | **Valid Scope:** When the result set is available. 1220 | 1221 | **Example:** [Here](#sync-fetch) 1222 | 1223 | ### dbstmt.fetchAll(callback) 1224 | 1225 | **Description:** 1226 | 1227 | Asynchronously retrieves all the rows from the result set if available. 1228 | 1229 | **Syntax:** 1230 | 1231 | fetchAll(callback) 1232 | 1233 | **Parameters** 1234 | 1235 | - **callback(resultSet, error):** `function` to process after `fetchAll` is complete. 1236 | - **resultSet**: an `array` of `objects` each object represents a row of data. 1237 | - If an error occured or there is no `resultSet` it is set to `null` 1238 | 1239 | - **error**: `Error object` when `fetchAll` is unsuccessful. Otherwise `error` is set to `null`. 1240 | 1241 | **DB2 CLI API:** SQLFetch 1242 | 1243 | **Valid Scope:** When the result set is available. 1244 | 1245 | **Example:** [Here](#async-fetchAll) 1246 | ___ 1247 | 1248 | ### dbstmt.fetchAllSync([callback]) 1249 | 1250 | **Description:** 1251 | 1252 | Synchronous version of `fetchAll`. 1253 | 1254 | **Syntax 1:** 1255 | fetchAllSync() 1256 | 1257 | **Returns** 1258 | 1259 | - **resultSet**: an `array` of `objects` each object represents a row of data. 1260 | - If there is no result set `null` is returned. 1261 | - If an error occurs it will be thrown. 1262 | 1263 | **Syntax 2:** 1264 | 1265 | fetchAllSync(callback) 1266 | 1267 | **Parameters** 1268 | 1269 | - **callback(resultSet, error):** `function` to process after `fetchAll` is complete. 1270 | - **resultSet**: an `array` of `objects` each object represents a row of data. 1271 | - If an error occured `resultSet` is set to `null` 1272 | - **error**: `Error object` when `fetchAllSync` is unsuccessful. Otherwise `error` is set to `null`. 1273 | 1274 | **Example:** [Here](#sync-fetchAll) 1275 | 1276 | **DB2 CLI API:** SQLFetch 1277 | 1278 | **Valid Scope:** When the result set is available. 1279 | 1280 | ___ 1281 | 1282 | ### dbstmt.numFields() 1283 | 1284 | **Description:** 1285 | 1286 | Retrieves number of fields contained in the result set if available. 1287 | 1288 | **Syntax:** 1289 | 1290 | numFields() 1291 | 1292 | **Returns:** 1293 | 1294 | `number(integer)` indicating number of fields in the result set. 1295 | 1296 | **DB2 CLI API:** SQLNumResultCols 1297 | 1298 | **Valid Scope:** When the result set is available. 1299 | ___ 1300 | 1301 | ### dbstmt.numRows() 1302 | 1303 | **Description:** 1304 | 1305 | Returns the number of rows in a table affected by the last executed sql statement if available. 1306 | 1307 | **Syntax:** 1308 | 1309 | numRows() 1310 | 1311 | **Returns:** 1312 | 1313 | `number(integer)` indicating number of rows affected by the operation. 1314 | 1315 | **DB2 CLI API:** SQLRowCount 1316 | 1317 | **Valid Scope:** When the result set is available. 1318 | ___ 1319 | 1320 | ### dbstmt.fieldType(index) 1321 | 1322 | **Description:** 1323 | 1324 | If a valid index is provided, `fieldType` returns the data type of the indicated field. 1325 | 1326 | **Syntax:** 1327 | 1328 | fieldType(index) 1329 | 1330 | **Parameters:** 1331 | 1332 | - **index:** `number(integer)` the column number in a result set, ordered sequentially left to right, starting at 0. 1333 | 1334 | **Returns:** 1335 | 1336 | `number(integer)` indicating the data type of the specified column in the result set. 1337 | 1338 | **DB2 CLI API:** SQLColAttribute 1339 | 1340 | **Valid Scope:** When the result set is available. 1341 | ___ 1342 | 1343 | ### dbstmt.fieldWidth(index) 1344 | **Description:** 1345 | 1346 | If a valid index is provided, `fieldWidth` returns the field width of the indicated field. 1347 | 1348 | **Syntax:** 1349 | 1350 | fieldWidth(index) 1351 | 1352 | **Parameters** 1353 | 1354 | - **index:** `number(integer)` the column number in a result set, ordered sequentially left to right, starting at 0. 1355 | 1356 | **Returns:** 1357 | 1358 | `number(integer)` indicating the width of the specified column in the result set. 1359 | 1360 | **DB2 CLI API:** SQLColAttribute 1361 | 1362 | **Valid Scope:** When the result set is available. 1363 | ___ 1364 | 1365 | ### dbstmt.fieldNullable(index) 1366 | 1367 | **Description:** 1368 | 1369 | If a valid index is provided, fieldNullable returns true | false indicating if field can be set to null. 1370 | 1371 | **Syntax:** 1372 | 1373 | fieldNullable(index) 1374 | 1375 | **Parameters** 1376 | 1377 | - **index:** `number(integer)` the column number in a result set, ordered sequentially left to right, starting at 0. 1378 | 1379 | **Returns:** 1380 | 1381 | `boolean` indicating if the column can be set to NULL. 1382 | 1383 | **DB2 CLI API:** SQLColAttribute 1384 | 1385 | **Valid Scope:** When the result set is available. 1386 | 1387 | ___ 1388 | 1389 | ### dbstmt.fieldName(index) 1390 | 1391 | **Description:** 1392 | 1393 | If a valid index is provided, `fieldName` returns the name of the indicated field. 1394 | 1395 | **Syntax:** 1396 | 1397 | fieldName(index) 1398 | 1399 | **Parameters** 1400 | 1401 | - **index:** `number(integer)` the column number in a result set, ordered sequentially left to right, starting at 0. 1402 | 1403 | **Returns:** 1404 | 1405 | `string` indicating the name of the specified column in the result set. 1406 | 1407 | **DB2 CLI API:** SQLColAttribute 1408 | 1409 | **Valid Scope:** When the result set is available. 1410 | ___ 1411 | 1412 | ### dbstmt.fieldPrecise(index) 1413 | 1414 | **Description:** 1415 | 1416 | If a valid index is provided, `fieldPrecise` returns the precision of the indicated field. 1417 | 1418 | **Syntax:** 1419 | 1420 | fieldPrecise(index) 1421 | 1422 | **Parameters** 1423 | 1424 | - **index:** `number(integer)` the column number in a result set, ordered sequentially left to right, starting at 0. 1425 | 1426 | **Returns:** 1427 | 1428 | `number(integer)` indicating the precision of the specified column in the result set. 1429 | 1430 | **DB2 CLI API:** SQLColAttribute 1431 | 1432 | **Valid Scope:** When the result set is available. 1433 | ___ 1434 | 1435 | ### dbstmt.fieldScale(index) 1436 | **Description:** 1437 | 1438 | If a valid index is provided, `fieldScale` returns the scale of the indicated column. 1439 | 1440 | **Syntax:** 1441 | 1442 | fieldScale(index) 1443 | 1444 | **Parameters:** 1445 | 1446 | - **index:** `number(integer)` the column number in a result set, ordered sequentially left to right, starting at 0. 1447 | 1448 | **Returns:** 1449 | 1450 | `number(integer)` indicating the scale of the specified column in the result set. 1451 | 1452 | **DB2 CLI API:** SQLColAttribute 1453 | 1454 | **Valid Scope:** When the result set is available. 1455 | ___ 1456 | 1457 | ### dbstmt.fieldInfo(index) 1458 | **Description:** 1459 | 1460 | If a valid index is provided, `fieldInfo` returns the information of the indicated column. 1461 | 1462 | **Syntax:** 1463 | 1464 | fieldInfo(index) 1465 | 1466 | **Parameters:** 1467 | 1468 | - **index:** `number(integer)` the column number in a result set, ordered sequentially left to right, starting at 0. 1469 | 1470 | **Returns:** 1471 | 1472 | An `object` indicating the information of the specified column in the result set. It contains following fields: 1473 | - `Name(string)` indicating the name of the specified column in the result set. 1474 | - `Type(number)` indicating the data type of the specified column in the result set. 1475 | - `TypeName(string)` indicating the data type name of the specified column in the result set. 1476 | - `Width(number)` indicating the width of the specified column in the result set. 1477 | - `Precise(number)` indicating the precision of the specified column in the result set. 1478 | - `Scale(number)` indicating the scale of the specified column in the result set. 1479 | - `Nullable(boolean)` indicating if the column can be set to NULL. 1480 | 1481 | **DB2 CLI API:** SQLColAttribute 1482 | 1483 | **Valid Scope:** When the result set is available. 1484 | ___ 1485 | 1486 | ### dbstmt.stmtError(callback) 1487 | 1488 | **Description:** 1489 | 1490 | Returns the diagnostic information associated with the most recently called function for a particular statement, connection, or environment handler. 1491 | 1492 | **NOTE** `dbstmt.stmtError` may not work properly when using with `dbconn.debug(true)` 1493 | 1494 | **Syntax:** 1495 | 1496 | stmtError(hType, recno, callback) 1497 | 1498 | **Parameters** 1499 | 1500 | - **hType:** `number(integer)` indicates the handler type of diagnostic information. It can be following values: 1501 | 1502 | - `SQL_HANDLE_ENV`: Retrieve the environment diagnostic information 1503 | 1504 | - `SQL_HANDLE_DBC`: Retrieve the connection diagnostic information 1505 | 1506 | - `SQL_HANDLE_STMT`: Retrieve the statement diagnostic information 1507 | 1508 | - **recno:** `number(integer)` indicates which error should be retrieved. The first error record is number 1. 1509 | 1510 | - **callback(errmsg):** is a callback function to process the retrieved error message. 1511 | - **errmsg:** `string` consists of a standardized SQLSTATE, the error code, and a text message. 1512 | 1513 | **DB2 CLI API:** SQLGetDiagRec 1514 | 1515 | **Valid Scope:** After calling conn() function 1516 | ___ 1517 | 1518 | ### dbstmt.asNumber(flag) 1519 | 1520 | **Description:** 1521 | 1522 | Enables or disables automatic numeric conversion. 1523 | 1524 | **Syntax 1:** 1525 | 1526 | asNumber() 1527 | 1528 | **Syntax 2:** 1529 | 1530 | asNumber(flag) 1531 | 1532 | **Parameters:** 1533 | 1534 | - **flag:** `boolean` to turn automatic data conversion mode on or off. Default value is `false`. 1535 | 1536 | - `true`: The result data is returned as its original type. Due to the data type difference between SQL and Javascript, the data may be truncated. 1537 | 1538 | - `false`: The result data is returned as strings. 1539 | 1540 | **Returns:** 1541 | 1542 | `boolean` the current state of the asNumber flag otherwise an error is thrown. 1543 | 1544 | **Valid Scope:** After allocating the statement handler. 1545 | 1546 | ***Diagnostics*** 1547 | 1548 | | Error Code | Meaning| 1549 | |---|---| 1550 | | 0|Success| 1551 | |-1|Error| 1552 | |1|Success with information| 1553 | | 8001|Invalid parameter numbers| 1554 | | 8002|Invalid parameter type| 1555 | | 8003|Invalid parameter range| 1556 | | 8012|Connection handler is not allocated| 1557 | | 8013|Statement handler is not allocated| 1558 | | 8014|Result set is not ready| 1559 | -------------------------------------------------------------------------------- /docs/RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release 2 | 3 | To create a new release the developer first needs to run `release.sh` 4 | ```sh 5 | ./release.sh 6 | ``` 7 | 8 | This script will create a new release branch, run release-it, push the new branch upstream. 9 | 10 | From there the developer needs to: 11 | 12 | 1) Open a pull request, have it approved by at least 1 reviewer, and merged into the master branch 13 | 2) Create a new release and tag from the [GitHub Web UI](https://github.com/IBM/nodejs-idb-connector/releases/new) with the release and tag name matching the version number in package.json. 14 | 15 | 16 | After the release is created our Jenkins instance will: 17 | 18 | 1) Build the package 19 | 2) Update the GH release with the binary asset 20 | 3) Publish the release to NPM 21 | 22 | After the release, the package version should be bumped to a `*-devel` suffix. 23 | This will make node-pre-gyp build from source in development builds or when installing the project from a git url. 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "idb-connector", 3 | "version": "1.2.19", 4 | "description": "A Node.js DB2 driver for IBM i", 5 | "homepage": "https://github.com/IBM/nodejs-idb-connector", 6 | "author": "IBM", 7 | "license": "MIT", 8 | "keywords": [ 9 | "DB2", 10 | "IBMi", 11 | "iSeries", 12 | "OS400" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "git+ssh://git@github.com/IBM/nodejs-idb-connector.git" 17 | }, 18 | "bugs": { 19 | "url": "https://github.com/IBM/nodejs-idb-connector/issues" 20 | }, 21 | "contributors": [ 22 | "Xu Meng (https://github.com/dmabupt)", 23 | "Abdirahim Musse (https://github.com/abmusse)", 24 | "Aaron Bartell (https://github.com/aaronbartell)", 25 | "Kevin Adler (https://github.com/kadler)", 26 | "Mark Irish (https://github.com/markdirish)" 27 | ], 28 | "os": [ 29 | "os400", 30 | "aix" 31 | ], 32 | "gypfile": true, 33 | "main": "lib/db2a.js", 34 | "files": [ 35 | "src/", 36 | "lib/" 37 | ], 38 | "directories": { 39 | "lib": "lib", 40 | "test": "test" 41 | }, 42 | "scripts": { 43 | "test": "ln -sf ./build-tmp-napi-v3 ./build && ./node_modules/mocha/bin/mocha.js --timeout 5s", 44 | "install": "node-pre-gyp install --fallback-to-build", 45 | "release": "./node_modules/release-it/bin/release-it.js" 46 | }, 47 | "dependencies": { 48 | "@mapbox/node-pre-gyp": "^1.0.10", 49 | "node-addon-api": "^3.1.0" 50 | }, 51 | "devDependencies": { 52 | "@release-it/conventional-changelog": "^8.0.1", 53 | "bindings": "^1.5.0", 54 | "chai": "^4.3.6", 55 | "eslint": "^8.26.0", 56 | "eslint-config-airbnb-base": "^15.0.0", 57 | "eslint-plugin-import": "^2.26.0", 58 | "mocha": "^10.1.0", 59 | "release-it": "^17.0.5" 60 | }, 61 | "binary": { 62 | "module_name": "db2ia", 63 | "module_path": "./lib/binding/{configuration}/napi{napi_build_version}-ibmi-{arch}/", 64 | "remote_path": "./{version}/", 65 | "package_name": "{module_name}-v{version}-napi{napi_build_version}-ibmi-{arch}.tar.gz", 66 | "host": "https://github.com/IBM/nodejs-idb-connector/releases/download", 67 | "napi_versions": [ 68 | 3 69 | ] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/db2ia/db2ia.cc: -------------------------------------------------------------------------------- 1 | // Copyright contributors to the nodejs-idb-connector project 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include 5 | 6 | #ifdef __PASE__ 7 | 8 | #include "dbconn.h" 9 | #include "dbstmt.h" 10 | SQLHENV envh; 11 | 12 | #ifndef SQL_ATTR_NON_HEXCCSID 13 | #define SQL_ATTR_NON_HEXCCSID 10203 14 | #endif 15 | 16 | #endif 17 | 18 | Napi::Object InitAll(Napi::Env env, Napi::Object exports) 19 | { 20 | #ifdef __PASE__ 21 | int param = SQL_TRUE; 22 | char *attr = (char *)"DB2CCSID", *ccsid = NULL; 23 | ccsid = getenv(attr); 24 | if (ccsid != NULL) 25 | SQLOverrideCCSID400(atoi(ccsid)); //CCSID customization. 26 | else 27 | SQLOverrideCCSID400(1208); // Run under CCSID 1208(UTF-8) by default. 28 | 29 | // Doc https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/cli/rzadpfnaenv.htm 30 | // allocates an environment handle and associated resources. 31 | // There can be only one active environment at any one time per application. 32 | SQLRETURN sqlReturnCode = SQLAllocEnv(&envh); 33 | 34 | if (sqlReturnCode != SQL_SUCCESS) 35 | { 36 | /* If SQL_ERROR is returned and phenv is equal to SQL_NULL_HENV, then SQLGetDiagRec() cannot be called 37 | * because there is no handle with which to associate additional diagnostic information. 38 | * If the return code is SQL_ERROR and the pointer to the environment handle is not equal to 39 | * SQL_NULL_HENV, then the handle is a restricted handle. This means the handle can only be used in a call 40 | * to SQLGetDiagRec() to obtain more error information, or to SQLFreeEnv(). 41 | */ 42 | printf("ERROR: SQLALLOCENV(%d)", sqlReturnCode); 43 | if (envh != SQL_NULL_HENV) 44 | { 45 | SQLFreeEnv(envh); 46 | } 47 | } 48 | // Doc https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/cli/rzadpfnsenva.htm 49 | sqlReturnCode = SQLSetEnvAttr(envh, //SQLHENV Environment handle 50 | SQL_ATTR_SERVER_MODE, // SQLINTEGER Attribute - Enable Server Mode by default 51 | ¶m, // SQLPOINTER Value Appropriate value for Attribute 52 | 0); //SQLINTEGER StringLength of Value in bytes if the attribute value is a character string 53 | 54 | // SQL_ATTR_NON_HEXCCSID will change the job CCSID to the default job CCSID if 55 | // the the job CCSID is 65535. The default job CCSID is guaranteed to be a 56 | // non-65535 CCSID. This would help us avoid the common error: 57 | // "Character conversion between CCSID 1208 and CCSID 65535 not valid" 58 | sqlReturnCode = SQLSetEnvAttr(envh, SQL_ATTR_NON_HEXCCSID, ¶m, 0); 59 | 60 | DbConn::Init(env, exports, envh); 61 | DbStmt::Init(env, exports); 62 | #endif 63 | 64 | // when not on PASE return a dummy object 65 | return exports; 66 | } 67 | 68 | NODE_API_MODULE(db2ia, InitAll) -------------------------------------------------------------------------------- /src/db2ia/dbconn.cc: -------------------------------------------------------------------------------- 1 | // Copyright contributors to the nodejs-idb-connector project 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "dbconn.h" 5 | 6 | SQLHENV DbConn::envh; 7 | Napi::FunctionReference DbConn::constructor; 8 | 9 | Napi::Object DbConn::Init(Napi::Env env, Napi::Object exports, SQLHENV envh2) 10 | { 11 | Napi::HandleScope scope(env); 12 | 13 | Napi::Function constructorfunc = DefineClass(env, "dbconn", { 14 | InstanceMethod("setConnAttr", &DbConn::SetConnAttr), 15 | InstanceMethod("getConnAttr", &DbConn::GetConnAttr), 16 | InstanceMethod("conn", &DbConn::Conn), 17 | InstanceMethod("disconn", &DbConn::Disconnect), 18 | InstanceMethod("close", &DbConn::Close), 19 | InstanceMethod("validStmt", &DbConn::ValidStmt), 20 | InstanceMethod("debug", &DbConn::Debug), 21 | InstanceMethod("isConnected", &DbConn::IsConnected), 22 | }); 23 | 24 | constructor = Napi::Persistent(constructorfunc); 25 | constructor.SuppressDestruct(); 26 | 27 | envh = envh2; 28 | 29 | exports.Set("dbconn", constructorfunc); 30 | return exports; 31 | } 32 | 33 | DbConn::DbConn(const Napi::CallbackInfo &info) : Napi::ObjectWrap(info) 34 | { 35 | SQLRETURN sqlReturnCode = -1; 36 | int param = SQL_TRUE; 37 | if (this->connAllocated == false) 38 | { 39 | // Doc https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/cli/rzadpfnacon.htm 40 | sqlReturnCode = SQLAllocConnect(envh, // SQLHENV henv -Environment Handle 41 | &this->connh); //SQLHDBC* phdbc -Pointer to connection handle 42 | if (sqlReturnCode != SQL_SUCCESS) 43 | { 44 | DEBUG(this, "SQLAllocConnect(%d): SQL Connection Allocation Fail", sqlReturnCode); 45 | return; 46 | } 47 | this->connAllocated = true; // Any Connection Handler processing can not be allowed before this. 48 | } 49 | 50 | sqlReturnCode = SQLSetConnectAttr(this->connh, SQL_ATTR_AUTOCOMMIT, ¶m, 0); // Enable auto_commit by default. 51 | } 52 | 53 | Napi::Object DbConn::NewInstance(Napi::Value arg) 54 | { 55 | Napi::Object dbConn = constructor.New({arg}); 56 | return dbConn; 57 | } 58 | 59 | /* 60 | * DbConn::SetConnAttr 61 | * Description: Set an attribute of a specific connection handle. 62 | * Parameters: 63 | * const Napi::CallbackInfo& info: 64 | * The information passed by Napi from the JavaScript call, including 65 | * arguments from the JavaScript function. In JavaScript, the exported 66 | * function takes two arguments, stored on the info object: 67 | * info[0] (Number): Attribute is the statement attribute to set. 68 | * Refer to the attribute table for more details. 69 | * info[1] (Number/String): Depending on the Attribute, this can be an 70 | * integer value, or a character string. 71 | * Return: Boolean True if no errors occured. Otherwise error is thrown. 72 | */ 73 | Napi::Value DbConn::SetConnAttr(const Napi::CallbackInfo &info) 74 | { 75 | Napi::Env env = info.Env(); 76 | Napi::HandleScope scope(env); 77 | int length = info.Length(); 78 | //validation 79 | CHECK_WITH_RETURN(length != 2, INVALID_PARAM_NUM, "Expected Two Parameters for SetConnAttr", env, env.Null()) 80 | CHECK_WITH_RETURN(!info[0].IsNumber(), INVALID_PARAM_TYPE, "Expected Parameter 1 to be a Number", env, env.Null()) 81 | CHECK_WITH_RETURN(!info[1].IsNumber() && !info[1].IsString(), INVALID_PARAM_TYPE, "Number or String Expected For Second Parameter of SetConnAttr", env, env.Null()) 82 | CHECK_WITH_RETURN(this->connAllocated == false, CONN_NOT_READY, "Need to call conn() to allocate connection on DbConn first", env, env.Null()) 83 | 84 | SQLINTEGER attr = Napi::Number(env, info[0]).Int32Value(); 85 | char *cValue; 86 | SQLINTEGER sLen = 0; 87 | SQLRETURN sqlReturnCode = -1; 88 | //check if the second arg was a Number or a String 89 | if (info[1].IsNumber()) 90 | { 91 | int param = Napi::Number(env, info[1]).Int32Value(); 92 | // Doc https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/cli/rzadpfnsconx.htm 93 | sqlReturnCode = SQLSetConnectAttr(this->connh, //SQLHDBC hdbc -Connection Handle 94 | attr, //SQLINTEGER fAttr -Connection Attr to Set 95 | ¶m, //SQLPOINTER vParam -Value for fAttr 96 | 0); //SQLINTEGER sLen -Length of input value (if string) 97 | DEBUG(this, "SetConnAttr() attr = %d, value = %d, rc = %d\n", (int)attr, param, (int)sqlReturnCode); 98 | } 99 | else if (info[1].IsString()) 100 | { 101 | std::string arg1 = Napi::String(env, info[1]).Utf8Value(); 102 | std::vector newCString(arg1.begin(), arg1.end()); 103 | newCString.push_back('\0'); 104 | cValue = &newCString[0]; 105 | sLen = strlen(cValue); 106 | sqlReturnCode = SQLSetConnectAttr(this->connh, attr, cValue, sLen); 107 | DEBUG(this, "SetConnAttr() attr = %d, value = %s, return code = %d\n", (int)attr, cValue, (int)sqlReturnCode); 108 | } 109 | if (sqlReturnCode != SQL_SUCCESS) 110 | { 111 | throwErrMsg(SQL_HANDLE_DBC, connh, env); 112 | return env.Null(); 113 | } 114 | return Napi::Boolean::New(env, 1); 115 | } 116 | 117 | /* 118 | * DbConn::GetConnAttr 119 | * Description: Returns the current settings for the specified connection option 120 | * Parameters: 121 | * const Napi::CallbackInfo& info: 122 | * The information passed by Napi from the JavaScript call, including 123 | * arguments from the JavaScript function. In JavaScript, the exported 124 | * function takes one argument, stored on the info object: 125 | * info[0] (Number): Attribute is the connection attribute to set. 126 | * Return: The attribute option in the format of a Number or a String. 127 | * 128 | */ 129 | Napi::Value DbConn::GetConnAttr(const Napi::CallbackInfo &info) 130 | { 131 | Napi::Env env = info.Env(); 132 | Napi::HandleScope scope(env); 133 | int length = info.Length(); 134 | //validation 135 | CHECK_WITH_RETURN(length != 1, INVALID_PARAM_NUM, "Expected One Parameter for getConnAttr", env, env.Null()) 136 | CHECK_WITH_RETURN(!info[0].IsNumber(), INVALID_PARAM_TYPE, "Expected Parameter 1 to be a Number", env, env.Null()) 137 | CHECK_WITH_RETURN(this->connAllocated == false, CONN_NOT_READY, "Need to call conn() to allocate connection on first", env, env.Null()) 138 | 139 | SQLINTEGER attr = Napi::Number(env, info[0]).Int32Value(); 140 | 141 | char buf[1024]; 142 | int retVal = 0; 143 | SQLINTEGER sLen = 0; 144 | void *pValue = (char *)&buf; 145 | //Doc https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/cli/rzadpfngcona.htm 146 | SQLRETURN sqlReturnCode = SQLGetConnectAttr(this->connh, //SQLHDBC hdbc -Connection handle 147 | attr, //SQLINTEGER fAttr -Attribute to retrieve 148 | pValue, //SQLPOINTER pvParam -Value for fAttr 149 | sizeof(buf), //SQLINTEGER bLen -Max # of bytes store in pvParam 150 | &sLen); //SQLINTEGER* sLen -Length of the output data.(if string) 151 | 152 | if (!sLen) 153 | { //If the returned value is a number. 154 | pValue = &retVal; 155 | sqlReturnCode = SQLGetConnectAttr(this->connh, attr, pValue, 0, &sLen); 156 | DEBUG(this, "GetConnAttr() attr = %d, value = %d, return code = %d\n", (int)attr, *(int *)pValue, (int)sqlReturnCode); 157 | if (sqlReturnCode == SQL_SUCCESS) 158 | { 159 | return Napi::Number::New(env, *(int *)pValue); 160 | } 161 | } 162 | else 163 | { //If the returned value is a string. 164 | DEBUG(this, "GetConnAttr() attr = %d, value = %s, return code = %d\n", (int)attr, (char *)pValue, (int)sqlReturnCode); 165 | if (sqlReturnCode == SQL_SUCCESS) 166 | { 167 | return Napi::String::New(env, buf); 168 | } 169 | } 170 | if (sqlReturnCode != SQL_SUCCESS) 171 | { 172 | throwErrMsg(SQL_HANDLE_DBC, connh, env); 173 | } 174 | return env.Null(); 175 | } 176 | 177 | /* 178 | * DbConn::Conn 179 | * Description: Establishes a connection to the target database. 180 | * Parameters: 181 | * const Napi::CallbackInfo& info: 182 | * The information passed by Napi from the JavaScript call, including 183 | * arguments from the JavaScript function. In JavaScript, the exported 184 | * function takes either 1-4 arguments, stored on the info object depending on the syntex: 185 | * Syntex 1: conn(string Database) 186 | * Syntex 2: conn(string Database, function Callback) 187 | * Syntex 3: conn(string Database, string User, string Password) 188 | * Syntex 4: conn(string Database, string User, string Password, function Callback) 189 | * 190 | */ 191 | void DbConn::Conn(const Napi::CallbackInfo &info) 192 | { 193 | Napi::Env env = info.Env(); 194 | Napi::HandleScope scope(env); 195 | int length = info.Length(); 196 | SQLRETURN sqlReturnCode; 197 | 198 | //validation 199 | if (this->connected == true || this->connAllocated == false) 200 | { 201 | return; 202 | } 203 | 204 | switch (length) 205 | { 206 | case 1: //conn(string Database) 207 | if (!info[0].IsString()) 208 | { 209 | Napi::TypeError::New(env, "First Parameter Must be a String").ThrowAsJavaScriptException(); 210 | return; 211 | } 212 | break; 213 | case 2: //conn(string Database, function Callback) 214 | if (!(info[0].IsString() && info[1].IsFunction())) 215 | { 216 | Napi::TypeError::New(env, "First Parameter Must be a String, Second Parameter must be a Function").ThrowAsJavaScriptException(); 217 | return; 218 | } 219 | break; 220 | case 3: //conn(string Database, string User, string Password) 221 | if (!(info[0].IsString() && info[1].IsString() && info[2].IsString())) 222 | { 223 | Napi::TypeError::New(env, "All three Parameters Must be a String").ThrowAsJavaScriptException(); 224 | return; 225 | } 226 | break; 227 | case 4: //conn(string Database, string User, string Password, function Callback) 228 | if (!(info[0].IsString() && info[1].IsString() && info[2].IsString() && info[3].IsFunction())) 229 | { 230 | Napi::TypeError::New(env, "First three Parameters Must be a String, last Parameter must be a Function").ThrowAsJavaScriptException(); 231 | return; 232 | } 233 | break; 234 | default: 235 | Napi::Error::New(env, "conn() takes either 1,2,3, or 4 parameters").ThrowAsJavaScriptException(); 236 | return; 237 | } 238 | SQLCHAR *datasource = strdup(Napi::String(env, info[0]).Utf8Value().c_str()); 239 | SQLCHAR *loginuser = NULL; 240 | SQLCHAR *password = NULL; 241 | if (length >= 3) 242 | { 243 | loginuser = strdup(Napi::String(env, info[1]).Utf8Value().c_str()); 244 | password = strdup(Napi::String(env, info[2]).Utf8Value().c_str()); 245 | } 246 | // Doc https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/cli/rzadpfnconn.htm 247 | sqlReturnCode = SQLConnect(this->connh, //SQLHDBC Connection Handle 248 | datasource, //SQLCHAR* szDSN -Name or alias name of the database 249 | SQL_NTS, //SQLSMALLINT cbDSN -Length of contents of szDSN 250 | loginuser, //SQLCHAR* szUID -Auth Name (UID) 251 | SQL_NTS, //SQLSMALLINT cbUID -Length of contents of szUID 252 | password, //SQLCHAR* szAuthStr -Auth String (password) 253 | SQL_NTS); //SQLSMALLINT cbAuthStr Length of Contents of szAuthStr 254 | DEBUG(this, "SQLConnect(%d): conn obj [%p] handler [%d]\n", sqlReturnCode, this, this->connh); 255 | 256 | free(datasource); 257 | if (length >= 3) 258 | { 259 | free(loginuser); 260 | free(password); 261 | } 262 | if (sqlReturnCode != SQL_SUCCESS) 263 | { 264 | throwErrMsg(SQL_HANDLE_DBC, connh, env); 265 | SQLFreeConnect(this->connh); 266 | return; 267 | } 268 | this->connected = true; 269 | //when length is 2 or 4 need to Make Callbacks 270 | if (length == 2 || length == 4) 271 | { 272 | Napi::Function cb = info[length - 1].As(); 273 | cb.MakeCallback(env.Global(), {env.Null()}); 274 | } 275 | } 276 | 277 | /* 278 | * DbConn::Disconnect 279 | * Description: Ends the connection associated with the database connection handle. 280 | * Parameters: 281 | * const Napi::CallbackInfo& info: 282 | * The information passed by Napi from the JavaScript call, including 283 | * arguments from the JavaScript function. In JavaScript, the exported 284 | * function takes 0 arguments. 285 | * Returns: boolean true indicating success or throws an error. 286 | * 287 | */ 288 | Napi::Value DbConn::Disconnect(const Napi::CallbackInfo &info) 289 | { 290 | Napi::Env env = info.Env(); 291 | Napi::HandleScope scope(env); 292 | SQLRETURN sqlReturnCode; 293 | 294 | if (this->connected) 295 | { 296 | SQLINTEGER auto_commit = 0; 297 | sqlReturnCode = SQLGetConnectAttr(this->connh, SQL_ATTR_AUTOCOMMIT, &auto_commit, 0, NULL); 298 | if (auto_commit != SQL_TRUE) 299 | { // If Auto_Commit is disabled, Rollback all transactions before exit. 300 | //Doc https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/cli/rzadpfnendtr.htm 301 | sqlReturnCode = SQLEndTran(SQL_HANDLE_DBC, // SQLSMALLINT htype -Type of Handle 302 | this->connh, //SQLHENV handle -Handle to use 303 | SQL_ROLLBACK); //SQLSMALLINT fType -Wanted Action for the transaction 304 | } 305 | DEBUG(this, "SQLDisconnect: conn obj [%p] handler [%d]\n", this, this->connh); 306 | //Doc https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/cli/rzadpfndconn.htm 307 | sqlReturnCode = SQLDisconnect(this->connh); //SQLHDBC hdbc -Connection Handle 308 | CHECK_WITH_RETURN(sqlReturnCode != SQL_SUCCESS, SQL_ERROR, "SQLDisconnect Failed", env, Napi::Boolean::New(env, 0)) 309 | this->connected = false; 310 | } 311 | return Napi::Boolean::New(env, 1); 312 | } 313 | 314 | /* 315 | * DbConn::Close 316 | * Description: Frees the connection object. 317 | * Parameters: 318 | * const Napi::CallbackInfo& info: 319 | * The information passed by Napi from the JavaScript call, including 320 | * arguments from the JavaScript function. In JavaScript, the exported 321 | * function takes 0 arguments. 322 | * Returns: boolean true indicating success or throws an error. 323 | * 324 | */ 325 | Napi::Value DbConn::Close(const Napi::CallbackInfo &info) 326 | { 327 | Napi::Env env = info.Env(); 328 | Napi::HandleScope scope(env); 329 | SQLRETURN sqlReturnCode = -1; 330 | 331 | if (this->connAllocated) 332 | { 333 | if (this->connected) { 334 | this->Disconnect(info); 335 | } 336 | DEBUG(this, "SQLFreeConnect: conn obj [%p] handler [%d]\n", this, this->connh); 337 | //Doc https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/cli/rzadpfnfconn.htm 338 | sqlReturnCode = SQLFreeConnect(this->connh); //SQLHDBC hdbc -Connection Handle 339 | DEBUG(this, "SQLFreeConnect[%d]\n", sqlReturnCode); 340 | CHECK_WITH_RETURN(sqlReturnCode != SQL_SUCCESS, SQL_ERROR, "SQLFreeConnect Failed", env, Napi::Boolean::New(env, 0)); 341 | this->connAllocated = false; 342 | } 343 | return Napi::Boolean::New(env, 1); 344 | } 345 | 346 | /* 347 | * DbConn::ValidSmt 348 | * Description: Checks if the SQL string is valid and interprets vendor escape clauses. 349 | * Parameters: 350 | * const Napi::CallbackInfo& info: 351 | * The information passed by Napi from the JavaScript call, including 352 | * arguments from the JavaScript function. In JavaScript, the exported 353 | * function takes 1 argument. 354 | * info[0] (String): The SQL String to validate. 355 | * Returns: boolean true indicating success or throws an error. 356 | * 357 | */ 358 | Napi::Value DbConn::ValidStmt(const Napi::CallbackInfo &info) 359 | { 360 | Napi::Env env = info.Env(); 361 | Napi::HandleScope scope(env); 362 | int length = info.Length(); 363 | 364 | CHECK_WITH_RETURN(length != 1, INVALID_PARAM_NUM, "Expected 1st Parameter to be a String", env, env.Null()) 365 | CHECK_WITH_RETURN(this->connAllocated == false, CONN_NOT_READY, "Expected Connection to be allocated first, you can do so by using conn()", env, env.Null()) 366 | 367 | std::string argStr = Napi::String(env, info[0]).Utf8Value(); 368 | std::vector newCString(argStr.begin(), argStr.end()); 369 | newCString.push_back('\0'); 370 | 371 | SQLCHAR *tmpSqlSt = &newCString[0]; 372 | SQLINTEGER outLen = 0; 373 | SQLCHAR outSqlSt[2048]; 374 | // Doc https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/cli/rzadpfnnsql.htm 375 | SQLRETURN sqlReturnCode = SQLNativeSql(this->connh, //SQL HDBC Connection Handle 376 | tmpSqlSt, //SQLCHAR* InStatementText -Input SQL String 377 | strlen(tmpSqlSt), //SQLINTEGER TextLength1 -Length of InStatementText 378 | outSqlSt, //SQLCHAR* OutStatementText -Point to transformed String buffer 379 | sizeof(outSqlSt), //SQLINTEGER BufferLength -Size of buffer -> OutStatementText 380 | &outLen); //SQLINTGER* TextLength2 -number of bytes available to return in OutStatementText 381 | if (sqlReturnCode != SQL_SUCCESS) 382 | { 383 | throwErrMsg(SQL_HANDLE_DBC, connh, env); 384 | return env.Null(); 385 | } 386 | if ((unsigned int)outLen < sizeof(outSqlSt)) 387 | { 388 | outSqlSt[outLen] = '\0'; 389 | return Napi::String::New(env, outSqlSt); 390 | } 391 | return env.Null(); 392 | } 393 | 394 | /* 395 | * DbConn::Debug 396 | * Description: Turn on or Off Verbose Output: 397 | * Parameters: 398 | * const Napi::CallbackInfo& info: 399 | * The information passed by Napi from the JavaScript call, including 400 | * arguments from the JavaScript function. In JavaScript, the exported 401 | * function takes 1 argument. 402 | * info[0] (Boolean): true for ON false for OFF. 403 | * Returns: boolean true/false indicating the state of the debug switch. 404 | * 405 | */ 406 | Napi::Value DbConn::Debug(const Napi::CallbackInfo &info) 407 | { 408 | Napi::Env env = info.Env(); 409 | Napi::HandleScope scope(env); 410 | int length = info.Length(); 411 | //validation 412 | CHECK_WITH_RETURN(length != 1, INVALID_PARAM_NUM, "debug() Expected One Parameter", env, env.Null()) 413 | CHECK_WITH_RETURN(!info[0].IsBoolean(), INVALID_PARAM_TYPE, "debug() Expected 1st Parameter to be a Boolean ", env, env.Null()) 414 | 415 | this->isDebug = Napi::Boolean(env, info[0]).Value(); 416 | return Napi::Boolean(env, info[0]); 417 | } 418 | 419 | /* 420 | * DbConn::IsConnected 421 | * Description: Determine if the dbconn has a connection to the db. 422 | * Parameters: 423 | * const Napi::CallbackInfo& info: 424 | * The information passed by Napi from the JavaScript call, including 425 | * arguments from the JavaScript function. In JavaScript, the exported 426 | * function takes 0 arguments. 427 | * Returns: boolean true/false indicating if connected. 428 | * 429 | */ 430 | Napi::Value DbConn::IsConnected(const Napi::CallbackInfo &info) 431 | { 432 | Napi::Env env = info.Env(); 433 | Napi::HandleScope scope(env); 434 | 435 | return Napi::Boolean::New(env, this->connected); 436 | } -------------------------------------------------------------------------------- /src/db2ia/dbconn.h: -------------------------------------------------------------------------------- 1 | // Copyright contributors to the nodejs-idb-connector project 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "sqlcli.h" 12 | #include // For SQLOverrideCCSID400() 13 | #include "napi.h" 14 | 15 | #include "dberror.h" 16 | 17 | #define MAX_COLNAME_WIDTH 256 18 | #define MAX_COL_WIDTH 32766 19 | #define SP_PARAM_MAX 128 20 | 21 | #define INVALID_PARAM_NUM 8001 22 | #define INVALID_PARAM_TYPE 8002 23 | #define INVALID_PARAM_RANGE 8003 24 | 25 | #define ENV_NOT_READY 8011 26 | #define CONN_NOT_READY 8012 27 | #define STMT_NOT_READY 8013 28 | #define RSSET_NOT_READY 8014 29 | 30 | class DbConn : public Napi::ObjectWrap 31 | { 32 | friend class DbStmt; 33 | 34 | public: 35 | DbConn(const Napi::CallbackInfo &info); 36 | static Napi::Object Init(Napi::Env env, Napi::Object exports, SQLHENV envh); 37 | static Napi::Object NewInstance(Napi::Value arg); 38 | 39 | private: 40 | bool connAllocated = false; 41 | bool connected = false; 42 | bool isDebug = false; 43 | Napi::Value SetConnAttr(const Napi::CallbackInfo &info); 44 | Napi::Value GetConnAttr(const Napi::CallbackInfo &info); 45 | void Conn(const Napi::CallbackInfo &info); 46 | Napi::Value Disconnect(const Napi::CallbackInfo &info); 47 | Napi::Value Close(const Napi::CallbackInfo &info); 48 | Napi::Value ValidStmt(const Napi::CallbackInfo &info); 49 | Napi::Value Debug(const Napi::CallbackInfo &info); 50 | Napi::Value IsConnected(const Napi::CallbackInfo &info); 51 | 52 | static Napi::FunctionReference constructor; 53 | static SQLHENV envh; 54 | SQLHDBC connh; 55 | }; -------------------------------------------------------------------------------- /src/db2ia/dberror.h: -------------------------------------------------------------------------------- 1 | // Copyright contributors to the nodejs-idb-connector project 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "sqlcli.h" 12 | #include "napi.h" 13 | 14 | #define DEBUG(object, f_, ...) \ 15 | if (object->isDebug) \ 16 | { \ 17 | printf((f_), ##__VA_ARGS__); \ 18 | } 19 | 20 | #define CHECK(condition, errorCode, errorMessage, env) \ 21 | if ((condition)) \ 22 | { \ 23 | throwCustomMsg((errorCode), (errorMessage), (env)); \ 24 | return; \ 25 | } 26 | 27 | #define CHECK_WITH_RETURN(condition, errorCode, errorMessage, env, returnValue) \ 28 | if ((condition)) \ 29 | { \ 30 | throwCustomMsg((errorCode), (errorMessage), (env)); \ 31 | return (returnValue); \ 32 | } 33 | 34 | typedef struct _sqlError 35 | { 36 | SQLCHAR sqlState[SQL_SQLSTATE_SIZE + 1]; 37 | SQLINTEGER sqlCode; 38 | SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1]; 39 | int sqlReturnCode; 40 | } sqlError; 41 | 42 | static sqlError returnErrObj(int handleType, SQLINTEGER handle) 43 | { 44 | sqlError errObj; 45 | SQLSMALLINT length = 0; 46 | 47 | errObj.sqlReturnCode = SQLGetDiagRec(handleType, //SQLSMALLINT handleType -Handle Type 48 | handle, //SQLINTEGER handle -hadnle for info is wanted 49 | 1, //SQLSMALLINT recNUM -Indicates which error record to return (if multiple) 50 | errObj.sqlState, //SQLCHAR* szSQLSTATE -SQLSTATE as a string of 5 characters terminated by a null character. (Output) 51 | &errObj.sqlCode, //SQLINTEGER* pfNativeError -Error Code (Output) 52 | errObj.message, //SQLCHAR* szErrorMsg -Pointer to buffer msg text (Output) 53 | SQL_MAX_MESSAGE_LENGTH + 1, //SQLSMALLINT cbErorMsgMax -Max length of the buffer szErrorMsg 54 | &length); //SQLSMALLINT* pcbErrorMsg -Pointer total # bytes to return to szErrorMsg (Output) 55 | 56 | if (errObj.sqlReturnCode == SQL_SUCCESS) 57 | if (errObj.message[length - 1] == '\n') 58 | errObj.message[length - 1] = '\0'; 59 | 60 | return errObj; 61 | } 62 | 63 | // In async threads, we cannot access Napi:Env to throw exceptions, but only print the error to the member string `msg`. 64 | static void printErrorToLog(SQLINTEGER handle, char msg[]) 65 | { 66 | sqlError errObj = returnErrObj(SQL_HANDLE_STMT, handle); 67 | if (errObj.sqlReturnCode == SQL_SUCCESS) 68 | sprintf((char *)msg, "SQLSTATE=%s SQLCODE=%d %s", errObj.sqlState, (int)errObj.sqlCode, errObj.message); 69 | } 70 | 71 | //experimental way to actually return error messages in callbacks 72 | static std::string returnErrMsg(int handleType, SQLINTEGER handle) 73 | { 74 | SQLCHAR errMsg[SQL_MAX_MESSAGE_LENGTH + SQL_SQLSTATE_SIZE + 10]; 75 | std::string error; 76 | sqlError errObj = returnErrObj(handleType, handle); 77 | if (errObj.sqlReturnCode == SQL_SUCCESS) 78 | sprintf((char *)errMsg, "SQLSTATE=%s SQLCODE=%d %s", errObj.sqlState, (int)errObj.sqlCode, errObj.message); 79 | 80 | // return Napi::String::New(env, errMsg).Utf8Value(); 81 | error = errMsg; 82 | return error; 83 | } 84 | 85 | // In the main thread, we can throw the error message as a Javascript exeception. 86 | static void throwErrMsg(int handleType, SQLINTEGER handle, Napi::Env env) 87 | { 88 | std::string errorMessage = returnErrMsg(handleType, handle); 89 | Napi::Error::New(env, Napi::String::New(env, errorMessage)).ThrowAsJavaScriptException(); 90 | } 91 | 92 | // Throw a customized Javascript exeception. 93 | static void throwCustomMsg(int code, const char *msg, Napi::Env env) 94 | { 95 | SQLCHAR errMsg[SQL_MAX_MESSAGE_LENGTH + SQL_SQLSTATE_SIZE + 10]; 96 | sprintf((char *)errMsg, "SQLSTATE=PAERR SQLCODE=%d %s", code, msg); 97 | Napi::Error::New(env, Napi::String::New(env, errMsg)).ThrowAsJavaScriptException(); 98 | } 99 | 100 | static bool SQLErrorEquals(int handleType, SQLINTEGER handle, const char *sqlState, SQLINTEGER sqlCode) 101 | { 102 | sqlError errObj = returnErrObj(handleType, handle); 103 | return errObj.sqlCode == sqlCode && !strncmp(errObj.sqlState, sqlState, SQL_SQLSTATE_SIZE); 104 | } 105 | 106 | static const char* getSQLType(int sqlType) 107 | { 108 | switch (sqlType) 109 | { 110 | case SQL_CHAR: // SQL_CHAR(SQL_CODE_DATE) = 1 111 | return "CHAR"; 112 | case SQL_NUMERIC: // SQL_NUMERIC(SQL_CODE_TIME) = 2 113 | return "NUMERIC"; 114 | case SQL_DECIMAL: // SQL_DECIMAL(SQL_CODE_TIMESTAMP) = 3 115 | return "DECIMAL"; 116 | case SQL_INTEGER: // SQL_INTEGER = 4 117 | return "INTEGER"; 118 | case SQL_SMALLINT: // SQL_SMALLINT = 5 119 | return "SMALLINT"; 120 | case SQL_FLOAT: // SQL_FLOAT = 6 121 | return "FLOAT"; 122 | case SQL_REAL: // SQL_REAL = 7 123 | return "REAL"; 124 | case SQL_DOUBLE: // SQL_DOUBLE = 8 125 | return "DOUBLE"; 126 | case SQL_DATETIME: // SQL_DATETIME = 9 127 | return "DATETIME"; 128 | case SQL_VARCHAR: // SQL_VARCHAR(SQL_LONGVARCHAR) = 12 129 | return "VARCHAR"; 130 | case SQL_BLOB: // SQL_BLOB = 13 131 | return "BLOB"; 132 | case SQL_CLOB: // SQL_CLOB = 14 133 | return "CLOB"; 134 | case SQL_DBCLOB: // SQL_DBCLOB = 15 135 | return "DBCLOB"; 136 | case SQL_DATALINK: // SQL_DATALINK = 16 137 | return "DATALINK"; 138 | case SQL_WCHAR: // SQL_WCHAR = 17 139 | return "WCHAR"; 140 | case SQL_WVARCHAR: // SQL_WVARCHAR(SQL_WLONGVARCHAR) = 18 141 | return "WVARCHAR"; 142 | case SQL_BIGINT: // SQL_BIGINT = 19 143 | return "BIGINT"; 144 | case SQL_BLOB_LOCATOR: // SQL_BLOB_LOCATOR = 20 145 | return "BLOB_LOCATOR"; 146 | case SQL_CLOB_LOCATOR: // SQL_CLOB_LOCATOR = 21 147 | return "CLOB_LOCATOR"; 148 | case SQL_DBCLOB_LOCATOR:// SQL_DBCLOB_LOCATOR = 22 149 | return "DBCLOB_LOCATOR"; 150 | case SQL_UTF8_CHAR: // SQL_UTF8_CHAR = 23 151 | return "UTF8_CHAR"; 152 | case SQL_GRAPHIC: // SQL_GRAPHIC = 95 153 | return "GRAPHIC"; 154 | case SQL_VARGRAPHIC: // SQL_VARGRAPHIC(SQL_LONGVARGRAPHIC) = 96 155 | return "VARGRAPHIC"; 156 | case SQL_BINARY: // SQL_BINARY = -2 157 | return "BINARY"; 158 | case SQL_VARBINARY: // SQL_VARBINARY(SQL_LONGVARBINARY) = -3 159 | return "VARBINARY"; 160 | case SQL_DATE: // SQL_DATE(SQL_TYPE_DATE) = 91 161 | return "DATE"; 162 | case SQL_TIME: // SQL_TIME(SQL_TYPE_TIME) = 92 163 | return "TIME"; 164 | case SQL_TIMESTAMP: // SQL_TIMESTAMP(SQL_TYPE_TIMESTAMP) = 93 165 | return "TIMESTAMP"; 166 | case SQL_ALL_TYPES: // SQL_ALL_TYPES = 0 167 | return "ALL_TYPES"; 168 | case SQL_DECFLOAT: // SQL_DECFLOAT = -360 169 | return "DECFLOAT"; 170 | case SQL_XML: // SQL_XML = -370 171 | return "XML"; 172 | default: 173 | return "UNKNOWN"; 174 | } 175 | } -------------------------------------------------------------------------------- /src/db2ia/dbstmt.h: -------------------------------------------------------------------------------- 1 | // Copyright contributors to the nodejs-idb-connector project 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "dbconn.h" 7 | #include 8 | 9 | 10 | struct db2ColumnDescription 11 | { 12 | SQLCHAR *name; 13 | SQLSMALLINT nameLength; 14 | SQLSMALLINT sqlType; 15 | SQLINTEGER colPrecise; 16 | SQLSMALLINT colScale; 17 | SQLSMALLINT colNull; 18 | SQLINTEGER rlength; 19 | SQLINTEGER clobLoc; 20 | }; 21 | 22 | struct db2ParameterDescription 23 | { 24 | SQLSMALLINT valueType; 25 | SQLSMALLINT paramType; 26 | SQLINTEGER paramSize; 27 | SQLSMALLINT decDigits; 28 | SQLSMALLINT nullable; 29 | int io; 30 | SQLINTEGER ind; 31 | void *buf; 32 | }; 33 | 34 | struct resultSetItem 35 | { 36 | SQLCHAR *data; 37 | SQLINTEGER rlength; 38 | }; 39 | 40 | class DbStmt : public Napi::ObjectWrap 41 | { 42 | // classes that do the async versions of the workflows 43 | friend class ExecAsyncWorker; 44 | friend class PrepareAsyncWorker; 45 | friend class BindParamAsyncWorker; 46 | friend class BindParametersAsyncWorker; 47 | friend class ExecuteAsyncWorker; 48 | friend class FetchAsyncWorker; 49 | friend class FetchAllAsyncWorker; 50 | 51 | public: 52 | static Napi::Object Init(Napi::Env env, Napi::Object exports); 53 | DbStmt(const Napi::CallbackInfo &info); 54 | 55 | private: 56 | static Napi::FunctionReference constructor; 57 | 58 | Napi::Value SetStmtAttr(const Napi::CallbackInfo &info); 59 | Napi::Value GetStmtAttr(const Napi::CallbackInfo &info); 60 | 61 | void Exec(const Napi::CallbackInfo &info); 62 | Napi::Value ExecSync(const Napi::CallbackInfo &info); 63 | 64 | void Prepare(const Napi::CallbackInfo &info); 65 | void PrepareSync(const Napi::CallbackInfo &info); 66 | 67 | void BindParam(const Napi::CallbackInfo &info); 68 | void BindParamSync(const Napi::CallbackInfo &info); 69 | 70 | void BindParameters(const Napi::CallbackInfo &info); 71 | void BindParametersSync(const Napi::CallbackInfo &info); 72 | 73 | void Execute(const Napi::CallbackInfo &info); 74 | Napi::Value ExecuteSync(const Napi::CallbackInfo &info); 75 | 76 | Napi::Value NextResult(const Napi::CallbackInfo &info); 77 | 78 | void Fetch(const Napi::CallbackInfo &info); 79 | Napi::Value FetchSync(const Napi::CallbackInfo &info); 80 | 81 | void FetchAll(const Napi::CallbackInfo &info); 82 | Napi::Value FetchAllSync(const Napi::CallbackInfo &info); 83 | 84 | Napi::Value CloseCursor(const Napi::CallbackInfo &info); 85 | void Reset(const Napi::CallbackInfo &info); 86 | Napi::Value Commit(const Napi::CallbackInfo &info); 87 | Napi::Value Rollback(const Napi::CallbackInfo &info); 88 | Napi::Value Close(const Napi::CallbackInfo &info); 89 | 90 | Napi::Value NumFields(const Napi::CallbackInfo &info); 91 | Napi::Value NumRows(const Napi::CallbackInfo &info); 92 | Napi::Value FieldType(const Napi::CallbackInfo &info); 93 | Napi::Value FieldWidth(const Napi::CallbackInfo &info); 94 | Napi::Value FieldName(const Napi::CallbackInfo &info); 95 | Napi::Value FieldPrecise(const Napi::CallbackInfo &info); 96 | Napi::Value FieldScale(const Napi::CallbackInfo &info); 97 | Napi::Value FieldNullable(const Napi::CallbackInfo &info); 98 | Napi::Value FieldInfo(const Napi::CallbackInfo &info); 99 | 100 | Napi::Value AsNumber(const Napi::CallbackInfo &info); 101 | 102 | void StmtError(const Napi::CallbackInfo &info); 103 | 104 | // unexposed helper functions 105 | int populateColumnDescriptions(Napi::Env env); 106 | void freeColumnDescriptions(); 107 | int bindColData(Napi::Env env); 108 | int fetchData(); 109 | int buildJsObject(Napi::Env env, Napi::Array *array); 110 | void freeBindingRow(); 111 | void freeColumns(); 112 | void freeSp(); 113 | int bindParams(Napi::Env env, Napi::Array *params, std::string &error); 114 | int fetchSp(Napi::Env env, Napi::Array *array); 115 | int fetch(Napi::Env env, Napi::Object *row); 116 | 117 | bool stmtAllocated = false; 118 | bool resultSetAvailable = false; 119 | bool colDescAllocated = false; 120 | bool bindingRowAllocated = false; 121 | 122 | bool isDebug = false; 123 | bool asNumber = false; 124 | 125 | static SQLHENV envh; 126 | SQLHDBC connh; 127 | SQLHSTMT stmth; 128 | DbConn *myDbConn; 129 | 130 | char *xmlOut; 131 | char *spIn[SP_PARAM_MAX]; 132 | char *spOut[SP_PARAM_MAX]; 133 | int spInNum[SP_PARAM_MAX]; 134 | int spOutNum[SP_PARAM_MAX]; 135 | SQLINTEGER indicator[SP_PARAM_MAX]; 136 | int spInCount = 0; 137 | int spOutCount = 0; 138 | int spInNumCount; 139 | int spOutNumCount; 140 | 141 | SQLSMALLINT colCount = 0; 142 | db2ColumnDescription *dbColumn; 143 | SQLCHAR **bindingRowInC; 144 | std::vector resultSetInC; 145 | 146 | db2ParameterDescription *param = NULL; 147 | int paramCount = 0; 148 | 149 | char msg[SQL_MAX_MESSAGE_LENGTH + SQL_SQLSTATE_SIZE + 10]; 150 | }; 151 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # How to Test 2 | 3 | Mocha & Chai were used to create unit tests. Both are part of dev-deps within the package.json so be sure to run `npm install` first. 4 | 5 | Specific Tests can be run by `npm test path_to_test`. 6 | For example when inside the test directory you can run `npm test connectionTest`. 7 | All Tests can be run by `npm test`. 8 | Test connection with user name & password by `DBNAME=yourname DBPWD=password npm test`. -------------------------------------------------------------------------------- /test/asyncStatementTest.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const db2a = require('../lib/db2a'); 3 | 4 | const { 5 | OUT, IN, CHAR, CLOB, NUMERIC, dbstmt, dbconn, 6 | } = db2a; 7 | 8 | // Test Statement Class Async Methods 9 | describe('Statement Async Test', () => { 10 | var dbConn, dbStmt; 11 | 12 | before(() => { 13 | dbConn = new dbconn(); 14 | dbConn.conn('*LOCAL'); 15 | }); 16 | 17 | after(() => { 18 | dbConn.disconn(); 19 | dbConn.close(); 20 | }); 21 | 22 | beforeEach(() => { 23 | dbStmt = new dbstmt(dbConn); 24 | }); 25 | 26 | afterEach(() => { 27 | dbStmt.close(); 28 | }); 29 | 30 | describe('async prepare', () => { 31 | it('Prepares valid SQL and sends it to the DBMS, if fail, error is returned. ', (done) => { 32 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 33 | dbStmt.prepare(sql, (error) => { 34 | if (error) { 35 | throw error; 36 | } 37 | expect(error).to.be.null; 38 | done(); 39 | }); 40 | }); 41 | }); 42 | 43 | describe('async bindParams (2-D array)', () => { 44 | it('associate parameter markers in an SQL statement to app variables', (done) => { 45 | const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 46 | const dbConn2 = new dbconn(); 47 | dbConn2.conn('*LOCAL'); 48 | const dbStmt2 = new dbstmt(dbConn2); 49 | 50 | const params = [ 51 | [9997, IN, NUMERIC], // CUSNUM 52 | ['Doe', IN, CHAR], // LASTNAME 53 | ['J D', IN, CHAR], // INITIAL 54 | ['123 Broadway', IN, CHAR], // ADDRESS 55 | ['Hope', IN, CHAR], // CITY 56 | ['WA', IN, CHAR], // STATE 57 | [98101, IN, NUMERIC], // ZIP 58 | [2000, IN, NUMERIC], // CREDIT LIMIT 59 | [1, IN, NUMERIC], // change 60 | [250, IN, NUMERIC], // BAL DUE 61 | [0.00, IN, NUMERIC], // CREDIT DUE 62 | ]; 63 | 64 | dbStmt.exec('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT', (result, error) => { 65 | if (error) { 66 | throw error; 67 | } 68 | let rowsBefore = result[0]['00001']; 69 | rowsBefore = Number(rowsBefore); 70 | dbStmt.close(); 71 | 72 | dbStmt2.prepare(sql, (error) => { 73 | if (error) { 74 | throw error; 75 | } 76 | dbStmt2.bindParam(params, (error) => { 77 | if (error) { 78 | throw error; 79 | } 80 | expect(error).to.be.null; 81 | dbStmt2.execute((out, error) => { 82 | if (error) { 83 | throw error; 84 | } 85 | expect(error).to.be.null; 86 | dbStmt2.close(); 87 | dbStmt = new dbstmt(dbConn); 88 | dbStmt.exec('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT', 89 | (result, error) => { 90 | if (error) { 91 | throw error; 92 | } 93 | let rowsAfter = result[0]['00001']; 94 | rowsAfter = Number(rowsAfter); 95 | expect(rowsAfter).to.equal(rowsBefore + 1); 96 | done(); 97 | }); 98 | }); 99 | }); 100 | }); 101 | }); 102 | }); 103 | }); 104 | 105 | describe('async bindParams (1-D array)', () => { 106 | it('associate parameter markers in an SQL statement to app variables', (done) => { 107 | const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 108 | const dbConn2 = new dbconn(); 109 | dbConn2.conn('*LOCAL'); 110 | const dbStmt2 = new dbstmt(dbConn2); 111 | 112 | const params = [9997, 'Doe', 'J D', '123 Broadway', 'Hope', 'WA', 98101, 2000, 1, 250, 0.00]; 113 | 114 | dbStmt.exec('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT', (result, error) => { 115 | if (error) { 116 | throw error; 117 | } 118 | let rowsBefore = result[0]['00001']; 119 | rowsBefore = Number(rowsBefore); 120 | dbStmt.close(); 121 | 122 | dbStmt2.prepare(sql, (error) => { 123 | if (error) { 124 | throw error; 125 | } 126 | dbStmt2.bindParam(params, (error) => { 127 | if (error) { 128 | throw error; 129 | } 130 | expect(error).to.be.null; 131 | dbStmt2.execute((out, error) => { 132 | if (error) { 133 | throw error; 134 | } 135 | expect(error).to.be.null; 136 | dbStmt2.close(); 137 | dbStmt = new dbstmt(dbConn); 138 | dbStmt.exec('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT', 139 | (result, error) => { 140 | if (error) { 141 | throw error; 142 | } 143 | let rowsAfter = result[0]['00001']; 144 | rowsAfter = Number(rowsAfter); 145 | expect(rowsAfter).to.equal(rowsBefore + 1); 146 | done(); 147 | }); 148 | }); 149 | }); 150 | }) 151 | }); 152 | }); 153 | it("should insert spaces into CHAR columns when given a Javascript empty-string.", done => { 154 | const sql = ` 155 | INSERT INTO QIWS.QCUSTCDT ( 156 | CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE 157 | ) 158 | VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE `; 159 | const dbConn2 = new dbconn(); 160 | dbConn2.conn("*LOCAL"); 161 | const dbStmt2 = new dbstmt(dbConn2); 162 | const params = [ 163 | 9998, 164 | "", 165 | "", 166 | "123 Broadway", 167 | "Hope", 168 | "WA", 169 | 98101, 170 | 2000, 171 | 1, 172 | 250, 173 | 0.0, 174 | ]; 175 | dbStmt2.prepare(sql, error => { 176 | if (error) { 177 | throw error; 178 | } 179 | dbStmt2.bindParameters(params, error => { 180 | if (error) { 181 | throw error; 182 | } 183 | expect(error).to.be.null; 184 | dbStmt2.execute((out, error) => { 185 | if (error) { 186 | throw error; 187 | } 188 | expect(error).to.be.null; 189 | dbStmt2.close(); 190 | dbStmt = new dbstmt(dbConn); 191 | dbStmt.exec( 192 | "SELECT q.*, hex(lstnam) AS hexlstnam FROM QIWS.QCUSTCDT q where q.lstnam = ' '", 193 | (result, error) => { 194 | if (error) { 195 | throw error; 196 | } 197 | const rowsSelected = Number(result.length); 198 | 199 | expect(rowsSelected).to.equal(1); 200 | done(); 201 | } // }4040404040404040 202 | ); 203 | }); 204 | }); 205 | }); 206 | }); 207 | }); 208 | 209 | describe('async bindParameters (1-D array)', () => { 210 | it('associate parameter markers in an SQL statement to app variables', (done) => { 211 | const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 212 | const dbConn2 = new dbconn(); 213 | dbConn2.conn('*LOCAL'); 214 | const dbStmt2 = new dbstmt(dbConn2); 215 | 216 | const params = [9997, 'Doe', 'J D', '123 Broadway', 'Hope', 'WA', 98101, 2000, 1, 250, 0.00]; 217 | 218 | dbStmt.exec('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT', (result, error) => { 219 | if (error) { 220 | throw error; 221 | } 222 | let rowsBefore = result[0]['00001']; 223 | rowsBefore = Number(rowsBefore); 224 | dbStmt.close(); 225 | 226 | dbStmt2.prepare(sql, (error) => { 227 | if (error) { 228 | throw error; 229 | } 230 | dbStmt2.bindParameters(params, (error) => { 231 | if (error) { 232 | throw error; 233 | } 234 | expect(error).to.be.null; 235 | dbStmt2.execute((out, error) => { 236 | if (error) { 237 | throw error; 238 | } 239 | expect(error).to.be.null; 240 | dbStmt2.close(); 241 | dbStmt = new dbstmt(dbConn); 242 | dbStmt.exec('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT', 243 | (result, error) => { 244 | if (error) { 245 | throw error; 246 | } 247 | let rowsAfter = result[0]['00001']; 248 | rowsAfter = Number(rowsAfter); 249 | expect(rowsAfter).to.equal(rowsBefore + 1); 250 | done(); 251 | }); 252 | }); 253 | }); 254 | }); 255 | }); 256 | }); 257 | }); 258 | 259 | describe('async execute', () => { 260 | it('retrieves output params from stored proc', (done) => { 261 | const sql = 'call QXMLSERV.iPLUG512K(?,?,?,?)'; 262 | const ipc = '*NA'; 263 | const ctl = '*here'; 264 | const xmlIn = `system 'wrksbs'`; 265 | const xmlOut = ''; 266 | const params = [ 267 | [ipc, IN, CHAR], 268 | [ctl, IN, CHAR], 269 | [xmlIn, IN, CLOB], 270 | [xmlOut, OUT, CLOB], 271 | ]; 272 | 273 | dbStmt.prepare(sql, (error) => { 274 | if (error) { 275 | throw error; 276 | } 277 | dbStmt.bindParam(params, (error) => { 278 | if (error) { 279 | throw error; 280 | } 281 | dbStmt.execute((out, error) => { 282 | if (error) { 283 | throw error; 284 | } 285 | expect(error).to.be.null; 286 | expect(out).to.be.a('array'); 287 | expect(out.length).to.be.eq(1); 288 | done(); 289 | }); 290 | }); 291 | }); 292 | }); 293 | 294 | it('executes prepared statement returns null because no output params are available', (done) => { 295 | const sql = 'SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'; 296 | const params = [ 297 | [10.00, IN, NUMERIC], 298 | ]; 299 | 300 | dbStmt.prepare(sql, (error) => { 301 | if (error) { 302 | throw error; 303 | } 304 | dbStmt.bindParam(params, (error) => { 305 | if (error) { 306 | throw error; 307 | } 308 | dbStmt.execute((out, error) => { 309 | if (error) { 310 | throw error; 311 | } 312 | expect(error).to.be.null; 313 | expect(out).to.be.null; 314 | done(); 315 | }); 316 | }); 317 | }); 318 | }); 319 | }); 320 | 321 | describe('async execute (1-D param array)', () => { 322 | it('retrieves output params from stored proc', (done) => { 323 | const sql = 'call QXMLSERV.iPLUG512K(?,?,?,?)'; 324 | const ipc = '*NA'; 325 | const ctl = '*here'; 326 | const xmlIn = `system 'wrksbs'`; 327 | const xmlOut = ''; 328 | const params = [ipc, ctl, xmlIn, xmlOut]; 329 | 330 | dbStmt.prepare(sql, (error) => { 331 | if (error) { 332 | throw error; 333 | } 334 | dbStmt.bindParam(params, (error) => { 335 | if (error) { 336 | throw error; 337 | } 338 | dbStmt.execute((out, error) => { 339 | if (error) { 340 | throw error; 341 | } 342 | expect(error).to.be.null; 343 | expect(out).to.be.a('array'); 344 | expect(out.length).to.be.eq(params.length); 345 | done(); 346 | }); 347 | }); 348 | }); 349 | }); 350 | 351 | it('executes prepared statement returns null because no output params are available', (done) => { 352 | const sql = 'SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'; 353 | const params = [ 10.00 ]; 354 | 355 | dbStmt.prepare(sql, (error) => { 356 | if (error) { 357 | throw error; 358 | } 359 | dbStmt.bindParam(params, (error) => { 360 | if (error) { 361 | throw error; 362 | } 363 | dbStmt.execute((out, error) => { 364 | if (error) { 365 | throw error; 366 | } 367 | expect(error).to.be.null; 368 | expect(out).to.be.a('array'); 369 | expect(out.length).to.be.eq(params.length); 370 | done(); 371 | }); 372 | }); 373 | }); 374 | }); 375 | }); 376 | 377 | describe('async execute (bindParameters)', () => { 378 | it('retrieves output params from stored proc', (done) => { 379 | const sql = 'call QXMLSERV.iPLUG512K(?,?,?,?)'; 380 | const ipc = '*NA'; 381 | const ctl = '*here'; 382 | const xmlIn = `system 'wrksbs'`; 383 | const xmlOut = ''; 384 | const params = [ipc, ctl, xmlIn, xmlOut]; 385 | 386 | dbStmt.prepare(sql, (error) => { 387 | if (error) { 388 | throw error; 389 | } 390 | dbStmt.bindParameters(params, (error) => { 391 | if (error) { 392 | throw error; 393 | } 394 | dbStmt.execute((out, error) => { 395 | if (error) { 396 | throw error; 397 | } 398 | expect(error).to.be.null; 399 | expect(out).to.be.a('array'); 400 | expect(out.length).to.be.eq(params.length); 401 | done(); 402 | }); 403 | }); 404 | }); 405 | }); 406 | 407 | it('executes prepared statement returns null because no output params are available', (done) => { 408 | const sql = 'SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'; 409 | const params = [ 10.00 ]; 410 | 411 | dbStmt.prepare(sql, (error) => { 412 | if (error) { 413 | throw error; 414 | } 415 | dbStmt.bindParameters(params, (error) => { 416 | if (error) { 417 | throw error; 418 | } 419 | dbStmt.execute((out, error) => { 420 | if (error) { 421 | throw error; 422 | } 423 | expect(error).to.be.null; 424 | expect(out).to.be.a('array'); 425 | expect(out.length).to.be.eq(params.length); 426 | done(); 427 | }); 428 | }); 429 | }); 430 | }); 431 | }); 432 | 433 | describe('async exec', () => { 434 | it('performs action of given SQL String', (done) => { 435 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 436 | dbStmt.exec(sql, (result, error) => { 437 | if (error) { 438 | throw error; 439 | } 440 | expect(error).to.be.null; 441 | expect(result).to.be.an('array'); 442 | expect(result.length).to.be.greaterThan(0); 443 | done(); 444 | }); 445 | }); 446 | }); 447 | 448 | describe('async fetchAll', () => { 449 | it('retrieves all rows from execute function:', (done) => { 450 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 451 | dbStmt.prepare(sql, (error) => { 452 | if (error) { 453 | throw error; 454 | } 455 | dbStmt.execute((out, error) => { 456 | if (error) { 457 | throw error; 458 | } 459 | dbStmt.fetchAll((result, error) => { 460 | if (error) { 461 | throw error; 462 | } 463 | expect(error).to.be.null; 464 | expect(result).to.be.a('array'); 465 | expect(result.length).to.be.greaterThan(0); 466 | done(); 467 | }); 468 | }); 469 | }); 470 | }); 471 | }); 472 | 473 | 474 | describe('async fetch', () => { 475 | it('retrieves one row from result set:', (done) => { 476 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 477 | dbStmt.prepare(sql, (error) => { 478 | if (error) { 479 | throw error; 480 | } 481 | dbStmt.execute((out, error) => { 482 | if (error) { 483 | throw error; 484 | } 485 | dbStmt.fetch((row, returnCode) => { 486 | if (returnCode !== 0) { // SQL_SUCCESS 487 | throw new Error('Rreturn Code was Not SQL SUCESS'); 488 | } 489 | expect(returnCode).to.equal(0); 490 | expect(row).to.be.a('object'); 491 | done(); 492 | }); 493 | }); 494 | }); 495 | }); 496 | }); 497 | }); 498 | -------------------------------------------------------------------------------- /test/connectionTest.js: -------------------------------------------------------------------------------- 1 | const {expect} = require('chai'); 2 | const db2a = require('../lib/db2a'); 3 | 4 | const {dbconn} = db2a; 5 | const username = process.env.DBNAME; 6 | const password = process.env.DBPWD; 7 | 8 | // Test connection Class 9 | describe('Connection Test', () => { 10 | // if successful returns undefined 11 | describe('conn & disconn & isConnected', () => { 12 | it('disconnects an exsisting connection to the datbase.', () => { 13 | const connection = new dbconn(); 14 | let result = connection.conn('*LOCAL'); 15 | result = connection.isConnected(); 16 | expect(result).to.be.true; 17 | 18 | result = connection.disconn(); 19 | expect(result).to.be.true; 20 | 21 | result = connection.isConnected(); 22 | expect(result).to.be.false; 23 | 24 | // Test the callback style 25 | connection.conn('*LOCAL', () => { 26 | result = connection.isConnected(); 27 | expect(result).to.be.true; 28 | 29 | result = connection.disconn(); 30 | expect(result).to.be.true; 31 | 32 | result = connection.isConnected(); 33 | expect(result).to.be.false; 34 | }); 35 | }); 36 | if (username && password) { 37 | it('disconnects an exsisting connection to the datbase with user/pwd.', () => { 38 | // Test with username/password 39 | const connection = new dbconn(); 40 | let result = connection.conn('*LOCAL', username, password); 41 | result = connection.isConnected(); 42 | expect(result).to.be.true; 43 | 44 | result = connection.disconn(); 45 | expect(result).to.be.true; 46 | 47 | result = connection.isConnected(); 48 | expect(result).to.be.false; 49 | 50 | // Test the callback style 51 | connection.conn('*LOCAL', username, password, () => { 52 | result = connection.isConnected(); 53 | expect(result).to.be.true; 54 | 55 | result = connection.disconn(); 56 | expect(result).to.be.true; 57 | 58 | result = connection.isConnected(); 59 | expect(result).to.be.false; 60 | }); 61 | }); 62 | } 63 | }); 64 | 65 | // if successful returns String or Int depending on attribute 66 | describe('getConnAttr', () => { 67 | it('getConnAttr(SQL_ATTR_AUTOCOMMIT) should return type Int', () => { 68 | const attr = db2a.SQL_ATTR_AUTOCOMMIT; 69 | const connection = new dbconn(); 70 | const result = connection.getConnAttr(attr); 71 | connection.close(); 72 | expect(result).to.be.a('number'); 73 | }); 74 | 75 | it('getConnAttr(SQL_ATTR_DBC_DEFAULT_LIB) should return type String', () => { 76 | const attr = db2a.SQL_ATTR_DBC_DEFAULT_LIB; 77 | const connection = new dbconn(); 78 | const result = connection.getConnAttr(attr); 79 | connection.close(); 80 | expect(result).to.be.a('string'); 81 | }); 82 | }); 83 | 84 | 85 | // if successful returns undefined 86 | describe('setConnAttr', () => { 87 | it('setConnAttr(SQL_ATTR_AUTOCOMMIT, SQL_TRUE) should return true', () => { 88 | const attr = db2a.SQL_ATTR_AUTOCOMMIT; 89 | let value = db2a.SQL_TRUE; 90 | const connection = new dbconn(); 91 | let result = connection.setConnAttr(attr, value); 92 | 93 | expect(result).to.be.true; 94 | 95 | result = connection.getConnAttr(attr); 96 | expect(result).to.equal(db2a.SQL_TRUE); 97 | 98 | value = db2a.SQL_FALSE; 99 | result = connection.setConnAttr(attr, value); 100 | expect(result).to.be.true; 101 | 102 | result = connection.getConnAttr(attr); 103 | expect(result).to.equal(db2a.SQL_FALSE); 104 | 105 | connection.close(); 106 | }); 107 | 108 | it('setConnAttr(SQL_ATTR_INFO_APPLNAME, "NODEJSTEST") should return true', () => { 109 | const attr = db2a.SQL_ATTR_INFO_APPLNAME; 110 | const value = 'NODEJSTEST'; 111 | const connection = new dbconn(); 112 | const result = connection.setConnAttr(attr, value); 113 | connection.close(); 114 | expect(result).to.be.true; 115 | }); 116 | }); 117 | 118 | // if successful returns undefined 119 | describe('debug', () => { 120 | it('prints more detailed info if choice = true. Turned off by setting choice = false.', () => { 121 | let choice = true; 122 | const connection = new dbconn(); 123 | let result = connection.debug(choice); 124 | 125 | expect(result).to.equal(choice); 126 | choice = !choice; 127 | result = connection.debug(choice); 128 | connection.close(); 129 | expect(result).to.equal(choice); 130 | }); 131 | }); 132 | 133 | // if successful returns String 134 | describe('validStmt', () => { 135 | it('if the SQL is valid, validStmt() should return the SQL', () => { 136 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 137 | const connection = new dbconn(); 138 | const result = connection.validStmt(sql); 139 | connection.close(); 140 | expect(result).to.equal(sql); 141 | }); 142 | 143 | it('if the SQL is invalid, validStmt() should return null', () => { 144 | try { 145 | const sql = 'SELECT * FORM QIWS.QCUSTCDT'; 146 | const connection = new dbconn(); 147 | const result = connection.validStmt(sql); 148 | connection.close(); 149 | expect(result).to.equal(null); 150 | } catch (e) { } 151 | }); 152 | }); 153 | 154 | // if successful returns undefined 155 | describe('close', () => { 156 | it('frees the connection object. ', () => { 157 | const connection = new dbconn(); 158 | const result = connection.close(); 159 | 160 | expect(result).to.be.true; 161 | }); 162 | it('disconnects if connected and frees the connection object ', () => { 163 | const connection = new dbconn(); 164 | connection.conn('*LOCAL'); 165 | const result = connection.close(); 166 | expect(result).to.be.true; 167 | }); 168 | }); 169 | }); 170 | -------------------------------------------------------------------------------- /test/dataTypes.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const util = require('util'); 3 | const fs = require('fs'); 4 | const db2a = require('../lib/db2a'); 5 | 6 | const { 7 | BLOB, BINARY, IN, dbstmt, dbconn, 8 | } = db2a; 9 | 10 | describe('Data Type Test', () => { 11 | let dbConn, dbStmt; 12 | 13 | before(() => { 14 | dbConn = new dbconn(); 15 | dbConn.conn('*LOCAL'); 16 | }); 17 | 18 | after(() => { 19 | dbConn.disconn(); 20 | dbConn.close(); 21 | }); 22 | 23 | beforeEach(() => { 24 | dbStmt = new dbstmt(dbConn); 25 | }); 26 | 27 | afterEach(() => { 28 | dbStmt.close(); 29 | }); 30 | 31 | describe('select number types', () => { 32 | it('smallint', (done) => { 33 | const sql = 'select * from (values smallint( -32768 )) as x (smallint_val)'; 34 | dbStmt.exec(sql, (result, error) => { 35 | expect(error).to.be.null; 36 | expect(result).to.be.an('array'); 37 | expect(result.length).to.be.greaterThan(0); 38 | expect(Object.values(result[0])[0]).to.equal('-32768'); 39 | done(); 40 | }); 41 | }); 42 | 43 | 44 | it('int', (done) => { 45 | const sql = 'select * from (values int( -2147483648 )) as x (int_val)'; 46 | dbStmt.exec(sql, (result, error) => { 47 | expect(error).to.be.null; 48 | expect(result).to.be.an('array'); 49 | expect(result.length).to.be.greaterThan(0); 50 | expect(Object.values(result[0])[0]).to.equal('-2147483648'); 51 | done(); 52 | }); 53 | }); 54 | 55 | 56 | it('bigint', (done) => { 57 | const sql = 'select * from (values bigint( -9223372036854775808 )) as x (bigint_val)'; 58 | dbStmt.exec(sql, (result, error) => { 59 | expect(error).to.be.null; 60 | expect(result).to.be.an('array'); 61 | expect(result.length).to.be.greaterThan(0); 62 | expect(Object.values(result[0])[0]).to.equal('-9223372036854775808'); 63 | done(); 64 | }); 65 | }); 66 | 67 | 68 | // it('real', (done) => { 69 | // let sql = 'select * from (values real( -12345.54321 )) as x (real_val)', 70 | // dbConn = new dbconn(); 71 | 72 | // dbConn.conn('*LOCAL'); 73 | 74 | // let dbStmt = new dbstmt(dbConn); 75 | 76 | // dbStmt.exec(sql, (result, error) => { 77 | // expect(error).to.be.null; 78 | // expect(result).to.be.an('array'); 79 | // expect(result.length).to.be.greaterThan(0); 80 | // expect(Object.values(result[0])[0] ).to.equal("-12345.54321"); 81 | // done(); 82 | // }); 83 | // }); 84 | }); 85 | 86 | 87 | // describe('bind parameters blob/binary/varbinary', () => { 88 | // it('create tables for test', (done) => { 89 | // const user = (process.env.USER).toUpperCase(); 90 | // const sql = [ 91 | // `CREATE SCHEMA IF NOT EXISTS ${user}`, 92 | // `CREATE OR REPLACE TABLE ${user}.BLOBTEST(BLOB_COLUMN BLOB(512k))`, 93 | // `CREATE OR REPLACE TABLE ${user}.BINARYTEST(BINARY_COLUMN BINARY(5000))`, 94 | // `CREATE OR REPLACE TABLE ${user}.VARBINTEST(VARBINARY_COLUMN VARBINARY(5000))` 95 | // ]; 96 | // for (let i = 0; i < sql.length; i++) { 97 | // dbStmt.execSync(sql[i], (result, err) => { }); 98 | // dbStmt.closeCursor(); 99 | // } 100 | // done(); 101 | // }); 102 | 103 | // it('runs SQLExecute and to bind blob', (done) => { 104 | // const user = (process.env.USER).toUpperCase(); 105 | // // Table which only contains one BLOB(512k) Field 106 | // const sql = `INSERT INTO ${user}.BLOBTEST(BLOB_COLUMN) VALUES(?)`; 107 | // fs.readFile(`${__dirname}/../README.md`, (error, buffer) => { 108 | // if (error) { 109 | // throw error; 110 | // } 111 | // dbStmt.prepare(sql, (error) => { 112 | // if (error) { 113 | // throw error; 114 | // } 115 | // dbStmt.bindParam([[buffer, IN, BLOB]], (error) => { 116 | // if (error) { 117 | // throw error; 118 | // } 119 | // dbStmt.execute((result, error) => { 120 | // if (error) { 121 | // console.log(util.inspect(error)); 122 | // throw error; 123 | // } 124 | // expect(error).to.be.null; 125 | // done(); 126 | // }); 127 | // }); 128 | // }); 129 | // }); 130 | // }); 131 | 132 | 133 | // it('runs SQLExecute and to bind blob (1-D array)', (done) => { 134 | // const user = (process.env.USER).toUpperCase(); 135 | // // Table which only contains one BLOB(512k) Field 136 | // const sql = `INSERT INTO ${user}.BLOBTEST(BLOB_COLUMN) VALUES(?)`; 137 | // fs.readFile(`${__dirname}/../README.md`, (error, buffer) => { 138 | // if (error) { 139 | // throw error; 140 | // } 141 | // dbStmt.prepare(sql, (error) => { 142 | // if (error) { 143 | // throw error; 144 | // } 145 | // dbStmt.bindParam([buffer], (error) => { 146 | // if (error) { 147 | // throw error; 148 | // } 149 | // dbStmt.execute((result, error) => { 150 | // if (error) { 151 | // console.log(util.inspect(error)); 152 | // throw error; 153 | // } 154 | // expect(error).to.be.null; 155 | // done(); 156 | // }); 157 | // }); 158 | // }); 159 | // }); 160 | // }); 161 | 162 | 163 | // it('runs SQLExecute and to bind blob (bindParameters)', (done) => { 164 | // const user = (process.env.USER).toUpperCase(); 165 | // // Table which only contains one BLOB(512k) Field 166 | // const sql = `INSERT INTO ${user}.BLOBTEST(BLOB_COLUMN) VALUES(?)`; 167 | // fs.readFile(`${__dirname}/../README.md`, (error, buffer) => { 168 | // if (error) { 169 | // throw error; 170 | // } 171 | // dbStmt.prepare(sql, (error) => { 172 | // if (error) { 173 | // throw error; 174 | // } 175 | // dbStmt.bindParameters([buffer], (error) => { 176 | // if (error) { 177 | // throw error; 178 | // } 179 | // dbStmt.execute((result, error) => { 180 | // if (error) { 181 | // console.log(util.inspect(error)); 182 | // throw error; 183 | // } 184 | // expect(error).to.be.null; 185 | // done(); 186 | // }); 187 | // }); 188 | // }); 189 | // }); 190 | // }); 191 | 192 | 193 | // it('runs SQLExecute and to bind binary', (done) => { 194 | // const user = (process.env.USER).toUpperCase(); 195 | // // Table which only contains one BLOB(10) Field 196 | // const sql = `INSERT INTO ${user}.BINARYTEST(BINARY_COLUMN) VALUES(?)`; 197 | // fs.readFile(`${__dirname}/../README.md`, (error, buffer) => { 198 | // if (error) { 199 | // throw error; 200 | // } 201 | // dbStmt.prepare(sql, (error) => { 202 | // if (error) { 203 | // throw error; 204 | // } 205 | // dbStmt.bindParam([[buffer, IN, BINARY]], (error) => { 206 | // if (error) { 207 | // throw error; 208 | // } 209 | // dbStmt.execute((result, error) => { 210 | // if (error) { 211 | // throw error; 212 | // } 213 | // expect(error).to.be.null; 214 | // done(); 215 | // }); 216 | // }); 217 | // }); 218 | // }); 219 | // }); 220 | 221 | 222 | // it('runs SQLExecute and to bind binary (1-D array)', (done) => { 223 | // const user = (process.env.USER).toUpperCase(); 224 | // // Table which only contains one BLOB(10) Field 225 | // const sql = `INSERT INTO ${user}.BINARYTEST(BINARY_COLUMN) VALUES(?)`; 226 | // fs.readFile(`${__dirname}/../README.md`, (error, buffer) => { 227 | // if (error) { 228 | // throw error; 229 | // } 230 | // dbStmt.prepare(sql, (error) => { 231 | // if (error) { 232 | // throw error; 233 | // } 234 | // dbStmt.bindParam([buffer], (error) => { 235 | // if (error) { 236 | // throw error; 237 | // } 238 | // dbStmt.execute((result, error) => { 239 | // if (error) { 240 | // throw error; 241 | // } 242 | // expect(error).to.be.null; 243 | // done(); 244 | // }); 245 | // }); 246 | // }); 247 | // }); 248 | // }); 249 | 250 | 251 | // it('runs SQLExecute and to bind binary (bindParameters)', (done) => { 252 | // const user = (process.env.USER).toUpperCase(); 253 | // // Table which only contains one BLOB(10) Field 254 | // const sql = `INSERT INTO ${user}.BINARYTEST(BINARY_COLUMN) VALUES(?)`; 255 | // fs.readFile(`${__dirname}/../README.md`, (error, buffer) => { 256 | // if (error) { 257 | // throw error; 258 | // } 259 | // dbStmt.prepare(sql, (error) => { 260 | // if (error) { 261 | // throw error; 262 | // } 263 | // dbStmt.bindParameters([buffer], (error) => { 264 | // if (error) { 265 | // throw error; 266 | // } 267 | // dbStmt.execute((result, error) => { 268 | // if (error) { 269 | // throw error; 270 | // } 271 | // expect(error).to.be.null; 272 | // done(); 273 | // }); 274 | // }); 275 | // }); 276 | // }); 277 | // }); 278 | 279 | 280 | // it('runs SQLExecute and to bind varbinary', (done) => { 281 | // const user = (process.env.USER).toUpperCase(); 282 | // // Table which only contains one VARBINARY(10) Field 283 | // const sql = `INSERT INTO ${user}.VARBINTEST(VARBINARY_COLUMN) VALUES(?)`; 284 | // fs.readFile(`${__dirname}/../README.md`, (error, buffer) => { 285 | // if (error) { 286 | // throw error; 287 | // } 288 | // dbStmt.prepare(sql, (error) => { 289 | // if (error) { 290 | // throw error; 291 | // } 292 | // dbStmt.bindParam([[buffer, IN, BLOB]], (error) => { 293 | // if (error) { 294 | // throw error; 295 | // } 296 | // dbStmt.execute((result, error) => { 297 | // if (error) { 298 | // console.log(util.inspect(error)); 299 | // throw error; 300 | // } 301 | // expect(error).to.be.null; 302 | // done(); 303 | // }); 304 | // }); 305 | // }); 306 | // }); 307 | // }); 308 | // }); 309 | 310 | 311 | // it('runs SQLExecute and to bind varbinary (1-D array)', (done) => { 312 | // const user = (process.env.USER).toUpperCase(); 313 | // // Table which only contains one VARBINARY(10) Field 314 | // const sql = `INSERT INTO ${user}.VARBINTEST(VARBINARY_COLUMN) VALUES(?)`; 315 | // fs.readFile(`${__dirname}/../README.md`, (error, buffer) => { 316 | // if (error) { 317 | // throw error; 318 | // } 319 | // dbStmt.prepare(sql, (error) => { 320 | // if (error) { 321 | // throw error; 322 | // } 323 | // dbStmt.bindParam([buffer], (error) => { 324 | // if (error) { 325 | // throw error; 326 | // } 327 | // dbStmt.execute((result, error) => { 328 | // if (error) { 329 | // console.log(util.inspect(error)); 330 | // throw error; 331 | // } 332 | // expect(error).to.be.null; 333 | // done(); 334 | // }); 335 | // }); 336 | // }); 337 | // }); 338 | // }); 339 | 340 | // it('runs SQLExecute and to bind varbinary (bindParameters)', (done) => { 341 | // const user = (process.env.USER).toUpperCase(); 342 | // // Table which only contains one VARBINARY(10) Field 343 | // const sql = `INSERT INTO ${user}.VARBINTEST(VARBINARY_COLUMN) VALUES(?)`; 344 | // fs.readFile(`${__dirname}/../README.md`, (error, buffer) => { 345 | // if (error) { 346 | // throw error; 347 | // } 348 | // dbStmt.prepare(sql, (error) => { 349 | // if (error) { 350 | // throw error; 351 | // } 352 | // dbStmt.bindParameters([buffer], (error) => { 353 | // if (error) { 354 | // throw error; 355 | // } 356 | // dbStmt.execute((result, error) => { 357 | // if (error) { 358 | // console.log(util.inspect(error)); 359 | // throw error; 360 | // } 361 | // expect(error).to.be.null; 362 | // done(); 363 | // }); 364 | // }); 365 | // }); 366 | // }); 367 | // }); 368 | 369 | 370 | describe('exec read blob test', () => { 371 | it('performs action of given SQL String', (done) => { 372 | const sql = 'SELECT CAST(\'test\' AS BLOB(10k)) FROM SYSIBM.SYSDUMMY1'; 373 | dbStmt.exec(sql, (result, error) => { 374 | if (error) { 375 | console.log(util.inspect(error)); 376 | throw error; 377 | } 378 | expect(error).to.be.null; 379 | expect(result).to.be.an('array'); 380 | expect(result.length).to.be.greaterThan(0); 381 | expect(Object.values(result[0])[0]).to.be.instanceOf(Buffer); 382 | done(); 383 | }); 384 | }); 385 | }); 386 | 387 | 388 | describe('exec read binary test', () => { 389 | it('performs action of given SQL String', (done) => { 390 | const sql = 'SELECT CAST(\'test\' AS BINARY(10)) FROM SYSIBM.SYSDUMMY1'; 391 | dbStmt.exec(sql, (result, error) => { 392 | if (error) { 393 | console.log(util.inspect(error)); 394 | throw error; 395 | } 396 | expect(error).to.be.null; 397 | expect(result).to.be.an('array'); 398 | expect(result.length).to.be.greaterThan(0); 399 | expect(Object.values(result[0])[0]).to.be.instanceOf(Buffer); 400 | done(); 401 | }); 402 | }); 403 | }); 404 | 405 | 406 | describe('exec read varbinary test', () => { 407 | it('performs action of given SQL String', (done) => { 408 | const sql = 'SELECT CAST(\'test\' AS VARBINARY(10)) FROM SYSIBM.SYSDUMMY1'; 409 | dbStmt.exec(sql, (result, error) => { 410 | if (error) { 411 | console.log(util.inspect(error)); 412 | throw error; 413 | } 414 | expect(error).to.be.null; 415 | expect(result).to.be.an('array'); 416 | expect(result.length).to.be.greaterThan(0); 417 | expect(Object.values(result[0])[0]).to.be.instanceOf(Buffer); 418 | done(); 419 | }); 420 | }); 421 | }); 422 | 423 | describe('inconsitent data', () => { 424 | it('handle ABC/10 error in exec', (done) => { 425 | const sql = `SELECT 'ABC'/10 AS DIVERR from sysibm.sysdummy1`; 426 | dbStmt.exec(sql, (result, error) => { 427 | if (error) { 428 | console.log(util.inspect(error)); 429 | throw error; 430 | } 431 | expect(error).to.be.null; 432 | expect(result).to.be.an('array'); 433 | expect(result[0].DIVERR).to.equal('-'); 434 | done(); 435 | }); 436 | }); 437 | 438 | it('handle ABC/10 error in fetch', (done) => { 439 | const sql = `SELECT 'ABC'/10 AS DIVERR from sysibm.sysdummy1`; 440 | dbStmt.prepare(sql, (error) => { 441 | dbStmt.execute((outParams, error) => { 442 | dbStmt.fetch((result, error) => { 443 | expect(error).to.equal(1); 444 | expect(result).to.be.an('object'); 445 | expect(result.DIVERR).to.equal('-'); 446 | done(); 447 | }); 448 | }); 449 | }); 450 | }); 451 | 452 | it('handle ABC/10 error in fetchAll', (done) => { 453 | const sql = `SELECT 'ABC'/10 AS DIVERR from sysibm.sysdummy1`; 454 | dbStmt.prepare(sql, (error) => { 455 | dbStmt.execute((outParams, error) => { 456 | dbStmt.fetchAll((result, error) => { 457 | if (error) { 458 | console.log(util.inspect(error)); 459 | throw error; 460 | } 461 | expect(error).to.be.null; 462 | expect(result).to.be.an('array'); 463 | expect(result[0].DIVERR).to.equal('-'); 464 | done(); 465 | }); 466 | }); 467 | }); 468 | }); 469 | 470 | it('handle ABC/10 error in execSync', (done) => { 471 | const sql = `SELECT 'ABC'/10 AS DIVERR from sysibm.sysdummy1`; 472 | dbStmt.execSync(sql, (result, error) => { 473 | if (error) { 474 | console.log(util.inspect(error)); 475 | throw error; 476 | } 477 | expect(error).to.be.null; 478 | expect(result).to.be.an('array'); 479 | expect(result[0].DIVERR).to.equal('-'); 480 | done(); 481 | }); 482 | }); 483 | 484 | it('handle ABC/10 error in fetchSync', (done) => { 485 | const sql = `SELECT 'ABC'/10 AS DIVERR from sysibm.sysdummy1`; 486 | const dbConn = new dbconn(); 487 | dbConn.conn('*LOCAL'); 488 | 489 | const dbStmt = new dbstmt(dbConn); 490 | dbStmt.prepareSync(sql, (error) => { 491 | dbStmt.executeSync((out, error) => { 492 | dbStmt.fetchSync((result, error) => { 493 | expect(error).to.equal(1); 494 | expect(result).to.be.an('object'); 495 | expect(result.DIVERR).to.equal('-'); 496 | done(); 497 | }); 498 | }); 499 | }); 500 | }); 501 | 502 | it('handle ABC/10 error in fetchAllSync', (done) => { 503 | const sql = `SELECT 'ABC'/10 AS DIVERR from sysibm.sysdummy1`; 504 | dbStmt.prepareSync(sql, (error) => { 505 | dbStmt.executeSync((outParams, error) => { 506 | dbStmt.fetchAllSync((result, error) => { 507 | if (error) { 508 | console.log(util.inspect(error)); 509 | throw error; 510 | } 511 | expect(error).to.be.null; 512 | expect(result).to.be.an('array'); 513 | expect(result[0].DIVERR).to.equal('-'); 514 | done(); 515 | }); 516 | }); 517 | }); 518 | }); 519 | }); 520 | }); 521 | -------------------------------------------------------------------------------- /test/failTests.js: -------------------------------------------------------------------------------- 1 | const {assert} = require('chai'); 2 | const {expect} = require('chai'); 3 | const addon = require('bindings')('db2ia'); 4 | const util = require('util'); 5 | const db2a = require('../lib/db2a'); 6 | 7 | // // Test Connection Class 8 | 9 | // describe('validStmtFail', () => { 10 | // it('error caused by providing invalid SQL as a param', () => { 11 | // const sql = 'garbageInput'; 12 | // const dbConn = new addon.DbConn(); 13 | // const result = dbConn.validStmt(); 14 | // console.log(`Valid Stmt output: ${result}`); 15 | // expect(result).to.be.a('string'); 16 | // }); 17 | // }); 18 | 19 | // describe('getConnAttrFail', () => { 20 | // it('error caused by providing invalid attr as a param', () => { 21 | // const attr = 50; 22 | // const dbConn = new addon.DbConn(); 23 | // const result = dbConn.getConnAttr(attr); 24 | // console.log(`Attrubte:: ${result}`); 25 | // expect(result).to.satisfy((result) => { 26 | // return result === 'string' || typeof result === 'number'; 27 | // }); 28 | // }); 29 | // }); 30 | 31 | // describe('setConnAttrFail', () => { 32 | // it('error caused by providing invlaid attr and value params', () => { 33 | // const attr = ''; 34 | // const value = -5; 35 | // const dbConn = new addon.DbConn(); 36 | // const result = dbConn.setConnAttr(attr, value); 37 | // expect(result).to.be.a('undefined'); 38 | // }); 39 | // }); 40 | 41 | // describe('debugFail', () => { 42 | // it('error caused by using invalid param type instead of a boolean', () => { 43 | // const choice = 1; 44 | // const dbConn = new addon.DbConn(); 45 | // const result = dbConn.debug('choice'); 46 | // expect(result).to.be.a('undefined'); 47 | // }); 48 | // }); 49 | 50 | // // need to create a Failure Case for disconn() 51 | // describe('disconnFail', () => { 52 | // it('error caused by calling disconn before Conn was established ', () => { 53 | // const dbConn = new addon.Connection().constructor(); 54 | // const result = dbConn.disconn(); 55 | // expect(result).to.be.a('undefined'); 56 | // }); 57 | // }); 58 | 59 | // // need to create a Failure Case for close() 60 | // describe('closeFail' , () => { 61 | // it('error caused by calling close before Conn was established. ', () => { 62 | // // const dbConn = new addon.DbConn(); 63 | // const result = dbConn.close(); 64 | // expect(result).to.be.a('undefined'); 65 | // }); 66 | // }); 67 | 68 | // // need to test the conn method 69 | 70 | // // Test Statement Class 71 | 72 | // describe('prepareFail', () => { 73 | // it('error caused by preparing invalid SQL as a param', () => { 74 | // const sql = 'SELECT * '; 75 | // const dbConn = new addon.DbConn(); 76 | // dbConn.conn('*LOCAL'); 77 | // const dbStmt = new addon.DbStmt(dbConn); 78 | // const result = dbStmt.prepare(sql); 79 | // expect(result).to.be.a('undefined'); 80 | // }); 81 | // }); 82 | 83 | // // if successful returns undefined. 84 | 85 | 86 | // describe('bindParamsFail', () => { 87 | // it('error caused by not providing correct params within the params[]', () => { 88 | // const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 89 | // const dbConn = new addon.DbConn(); 90 | // dbConn.conn('*LOCAL'); 91 | // const dbStmt = new addon.DbStmt(dbConn); 92 | // dbStmt.prepare(sql); 93 | // const result = dbStmt.bindParam([ 94 | // [1, addon.SQL_PARAM_INPUT, addon.SQL_NUMERIC], // change 95 | // [250, addon.SQL_PARAM_INPUT, addon.SQL_NUMERIC], // BAL DUE 96 | // ]); 97 | // dbStmt.execute(); 98 | // expect(result).to.be.a('undefined'); 99 | // }); 100 | // }); 101 | 102 | 103 | // describe('closeFail' , () => { 104 | // it('error caused by calling close before statement was executed. ', () => { 105 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 106 | // const dbStmt = new addon.Connection().connect().getStatement(); 107 | // // dbStmt.exec(sql); 108 | // const result = dbStmt.close(); 109 | // expect(result).to.be.a('undefined'); 110 | // }); 111 | // }); 112 | 113 | 114 | // describe('closeCursorFail' , () => { 115 | // it('error caused by calling closeCursor before statement was executed. ', () => { 116 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 117 | // const dbStmt = new addon.Connection().connect().getStatement(); 118 | // // dbStmt.exec(sql); 119 | // const result = dbStmt.closeCursor(); 120 | // expect(result).to.be.a('undefined'); 121 | // }); 122 | // }); 123 | 124 | 125 | // // need to create a Failure Case for commit() 126 | // describe('commitFail' , () => { 127 | // it('error caused by calling commit before statement was executed. ', () => { 128 | // const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 129 | // const dbStmt = new addon.Connection().connect().getStatement(); 130 | // dbStmt.prepare(sql); 131 | // let result = dbStmt.bindParam([ [4234,addon.PARM_TYPE_INPUT,2], ['sublime', addon.PARM_TYPE_INPUT, 1] ]); 132 | // // dbStmt.execute(); 133 | // result = dbStmt.commit(); 134 | // expect(result).to.be.a('undefined'); 135 | // }); 136 | // }); 137 | 138 | 139 | // describe('execFail', () => { 140 | // it('error caused by calling exec without params', () => { 141 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 142 | // const dbConn = new addon.DbConn(); 143 | // dbConn.conn('*LOCAL'); 144 | // const dbStmt = new addon.DbStmt(dbConn); 145 | // const result = dbStmt.exec(); 146 | // assert.isNotObject(result, 'object was not returned'); 147 | // console.log(`Type of result = ${typeof result}`); 148 | // console.log(`Select results: ${JSON.stringify(result)}`); 149 | // expect(result).to.be.an('array'); 150 | // }); 151 | // }); 152 | 153 | 154 | // describe('executeFail', () => { 155 | // it('error caused by calling execute before statement was prepared.', () => { 156 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 157 | // const dbConn = new addon.DbConn(); 158 | // dbConn.conn('*LOCAL'); 159 | // const dbStmt = new addon.DbStmt(dbConn); 160 | // // dbStmt.prepare(sql); 161 | // const result = dbStmt.execute(); 162 | // console.log(`Select results: ${JSON.stringify(result)}`); 163 | // console.log(`Size of the returned array:${result.length}`); 164 | // expect(result).to.be.a('array'); 165 | // }); 166 | // }); 167 | 168 | 169 | // describe('fetchAllFail', () => { 170 | // it('error caused by calling fetchAll before results were available', () => { 171 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 172 | // const dbConn = new addon.DbConn(); 173 | // const dbStmt = new addon.DbStmt(dbConn); 174 | // dbStmt.prepare(sql); 175 | // // dbStmt.execute(); 176 | // const result = dbStmt.fetchAll(); 177 | // console.log(`Select results: ${JSON.stringify(result)}`); 178 | // expect(result).to.be.a('array'); 179 | // }); 180 | // }); 181 | 182 | 183 | // describe('fetchFail', () => { 184 | // it('error caused by calling fetch before results were available', () => { 185 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 186 | // const dbConn = new addon.DbConn(); 187 | // const dbStmt = new addon.DbStmt(dbConn); 188 | // dbStmt.prepare(sql); 189 | // // dbStmt.execute(); 190 | // const result = dbStmt.fetch(); 191 | // console.log(`Select results: ${JSON.stringify(result)}`); 192 | // expect(result).to.be.a('object'); 193 | // }); 194 | // }); 195 | 196 | // describe('numFieldsFail', () => { 197 | // it('error caused by calling numFields before results were available.', () => { 198 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 199 | // const dbConn = new addon.DbConn(); 200 | // const dbStmt = new addon.DbStmt(dbConn); 201 | // dbStmt.prepare(sql); 202 | // // dbStmt.execute(); 203 | // const fields = dbStmt.numFields(); 204 | // console.log(`Number of Fields: ${fields}`); 205 | // expect(fields).to.be.a('number'); 206 | // }); 207 | // }); 208 | 209 | // describe('numRowsFail', () => { 210 | // it('error caused by calling numRows before results were available.', () => { 211 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 212 | // const dbConn = new addon.DbConn(); 213 | // const dbStmt = new addon.DbStmt(dbConn); 214 | // dbStmt.prepare(sql); 215 | // // dbStmt.execute(); 216 | // const rows = dbStmt.numRows(); 217 | // console.log(`Number of Rows: ${rows}`); 218 | // expect(rows).to.be.a('number'); 219 | // }); 220 | // }); 221 | 222 | // describe('fieldTypeFail', () => { 223 | // it('error caused by not providing an index as a param', () => { 224 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 225 | // const dbConn = new addon.DbConn(); 226 | // const dbStmt = new addon.DbStmt(dbConn); 227 | // dbStmt.prepare(sql); 228 | // dbStmt.execute(); 229 | // const col1 = dbStmt.fieldType(); 230 | // const col2 = dbStmt.fieldType(); 231 | // console.log(`column 1 fieldType = : ${col1}`); 232 | // console.log(`column 2 fieldType = : ${col2}`); 233 | // expect(col1).to.be.a('number'); 234 | // expect(col2).to.be.a('number'); 235 | // }); 236 | // }); 237 | 238 | // describe('fieldWidthFail', () => { 239 | // it('error caused by not providing an index as a param', () => { 240 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 241 | // const dbConn = new addon.DbConn(); 242 | // const dbStmt = new addon.DbStmt(dbConn); 243 | // dbStmt.prepare(sql); 244 | // dbStmt.execute(); 245 | // const col1 = dbStmt.fieldWidth(); 246 | // const col2 = dbStmt.fieldWidth(); 247 | // console.log(`column 1 fieldWidth = : ${col1}`); 248 | // console.log(`column 2 fieldWidth = : ${col2}`); 249 | // expect(col1).to.be.a('number'); 250 | // expect(col2).to.be.a('number'); 251 | // }); 252 | // }); 253 | 254 | // describe('fieldNullableFail', () => { 255 | // it('error caused by not providing an index as a param', () => { 256 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 257 | // const dbConn = new addon.DbConn(); 258 | // const dbStmt = new addon.DbStmt(dbConn); 259 | // dbStmt.prepare(sql); 260 | // dbStmt.execute(); 261 | // const col1 = dbStmt.fieldNullable(); 262 | // console.log(col1); 263 | // }); 264 | // }); 265 | 266 | // describe('fieldNameFail', () => { 267 | // it('error caused by providing an invalid index as a param', () => { 268 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 269 | // const dbConn = new addon.DbConn(); 270 | // const dbStmt = new addon.DbStmt(dbConn); 271 | // dbStmt.prepare(sql); 272 | // dbStmt.execute(); 273 | // const col1 = dbStmt.fieldName('garbageInput'); 274 | // const col2 = dbStmt.fieldName('fake'); 275 | // console.log(`column 1 Name = : ${col1}`); 276 | // console.log(`column 2 Name = : ${col2}`); 277 | // expect(col1).to.be.a('string'); 278 | // expect(col2).to.be.a('string'); 279 | // }); 280 | // }); 281 | 282 | // describe('fieldPreciseFail', () => { 283 | // it('error caused by not providing an index as a param', () => { 284 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 285 | // const dbConn = new addon.DbConn(); 286 | // const dbStmt = new addon.DbStmt(dbConn); 287 | // dbStmt.prepare(sql); 288 | // dbStmt.execute(); 289 | // const col1 = dbStmt.fieldPrecise(); 290 | // const col2 = dbStmt.fieldPrecise(); 291 | // console.log(`column 1 fieldPrecision = ${col1}`); 292 | // console.log(`column 2 fieldPrecision = ${col2}`); 293 | // expect(col1).to.be.a('number'); 294 | // expect(col2).to.be.a('number'); 295 | // }); 296 | // }); 297 | 298 | // describe('fieldScaleFail', () => { 299 | // it('error caused by providing an invalid index as a param', () => { 300 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 301 | // const dbConn = new addon.DbConn(); 302 | // dbConn.conn('*LOCAL'); 303 | // const dbStmt = new addon.DbStmt(dbConn); 304 | // dbStmt.prepare(sql); 305 | // dbStmt.execute(); 306 | // const col1 = dbStmt.fieldScale('c'); 307 | // const col2 = dbStmt.fieldScale('a'); 308 | // console.log(`column 1 fieldScale = ${col1}`); 309 | // console.log(`column 2 fieldScale = ${col2}`); 310 | // expect(col1).to.be.a('number'); 311 | // expect(col2).to.be.a('number'); 312 | // }); 313 | // }); 314 | 315 | // describe('setStmtAttrFail', () => { 316 | // it('error caused by providing invalid attr and value as params', () => { 317 | // // invalid attr insert 318 | // const attr = -500; 319 | // const value = 1; 320 | // const dbConn = new addon.DbConn(); 321 | // dbConn.conn('*LOCAL'); 322 | // const dbStmt = new addon.DbStmt(dbConn); 323 | // const result = dbStmt.setStmtAttr(attr, value); 324 | // expect(result).to.be.a('undefined'); 325 | // }); 326 | // }); 327 | 328 | // describe('getStmtAttrFail', () => { 329 | // it('error caused by providing invalid attr as a param.', () => { 330 | // // insert invalid attr 331 | // const attr = 2; 332 | // const dbConn = new addon.DbConn(); 333 | // dbConn.conn('*LOCAL'); 334 | // const dbStmt = new addon.DbStmt(dbConn); 335 | // const result = dbStmt.getStmtAttr(attr); 336 | // expect(result).to.satisfy((result) => { 337 | // return result === 'string' || typeof result === 'number'; 338 | // }); 339 | // }); 340 | // }); 341 | 342 | // describe('nextResultFail', () => { 343 | // it('err', () => { 344 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 345 | // const dbConn = new addon.DbConn(); 346 | // dbConn.conn('*LOCAL'); 347 | // const dbStmt = new addon.DbStmt(dbConn); 348 | // dbStmt.prepare(sql); 349 | // dbStmt.execute(); 350 | // const result = dbStmt.nextResult(); 351 | // expect(result).to.be.a('object'); 352 | // }); 353 | // }); 354 | 355 | // // need to create fail case for rollback 356 | // describe('rollbackFail', () => { 357 | // it('error caused by ', () => { 358 | // const result = dbStmt.rollback(); 359 | // const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 360 | // const dbStmt = new addon.Connection().connect().getStatement(); 361 | // dbStmt.prepare(sql); 362 | // // dbStmt.execute(); 363 | // expect(result).to.be.a('undefined'); 364 | // }); 365 | // }); 366 | 367 | // // need to create failure case for stmtErr 368 | // describe('stmtError', () => { 369 | // it('error was caused by: ', () => { 370 | // const dbStmt = new addon.Connection().connect().getStatement(); 371 | // dbStmt.stmtError(hType, recno); 372 | // }); 373 | // }); 374 | -------------------------------------------------------------------------------- /test/misc.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const db2a = require('../lib/db2a'); 3 | 4 | const { 5 | SQL_ATTR_CURSOR_TYPE, 6 | SQL_CURSOR_DYNAMIC, 7 | SQL_FETCH_RELATIVE, 8 | SQL_FETCH_NEXT, 9 | SQL_ERROR, 10 | SQL_NO_DATA_FOUND, 11 | dbconn, 12 | dbstmt, 13 | IN, 14 | CHAR, 15 | } = db2a; 16 | 17 | describe('Misc Test', () => { 18 | let dbConn, dbStmt; 19 | 20 | before(() => { 21 | dbConn = new dbconn(); 22 | dbConn.conn('*LOCAL'); 23 | }); 24 | 25 | after(() => { 26 | dbConn.disconn(); 27 | dbConn.close(); 28 | }); 29 | 30 | beforeEach(() => { 31 | dbStmt = new dbstmt(dbConn); 32 | }); 33 | 34 | afterEach(() => { 35 | dbStmt.close(); 36 | }); 37 | 38 | describe('The wide character issue', () => { 39 | it(`query some wide characters`, (done) => { 40 | const wchar = "ÇÇÇÇÇÇÇÇ"; 41 | dbStmt.exec("SELECT * FROM (VALUES CHAR('" + wchar + "')) AS X (C)", (rs) => { 42 | expect(rs[0].C).to.equal(wchar) 43 | done(); 44 | }); 45 | }); 46 | }); 47 | 48 | describe('The fetch with offset issue', () => { 49 | it(`fetch with offset`, (done) => { 50 | dbStmt.setStmtAttr(SQL_ATTR_CURSOR_TYPE, SQL_CURSOR_DYNAMIC); 51 | dbStmt.prepareSync("select schema_name from qsys2.sysschemas limit 10"); 52 | dbStmt.executeSync(); 53 | 54 | let rows = []; 55 | let orientation = SQL_FETCH_RELATIVE; 56 | do { 57 | dbStmt.fetchSync(orientation, 9, function (row, error) { 58 | if (error != SQL_ERROR && error != SQL_NO_DATA_FOUND) 59 | rows.push(row); 60 | rc = error; 61 | }); 62 | orientation = SQL_FETCH_NEXT; 63 | } while (rc != SQL_ERROR && rc != SQL_NO_DATA_FOUND) 64 | expect(rows.length).to.equal(1); 65 | done(); 66 | }); 67 | }); 68 | 69 | describe('The stored procedure with result set issue', () => { 70 | let user = (process.env.USER).toUpperCase(); 71 | let sql = `CALL ${user}.SPWITHRS`; 72 | let crtSP = `CREATE OR REPLACE PROCEDURE ${user}.SPWITHRS() 73 | LANGUAGE SQL 74 | DYNAMIC RESULT SETS 1 75 | BEGIN ATOMIC 76 | DECLARE C1 CURSOR FOR 77 | 78 | SELECT * FROM QIWS.QCUSTCDT LIMIT 5; 79 | 80 | OPEN C1 ; 81 | 82 | SET RESULT SETS WITH RETURN TO CLIENT CURSOR C1 ; 83 | 84 | END`; 85 | 86 | before((done) => { 87 | dbStmt = new dbstmt(dbConn); 88 | dbStmt.exec(crtSP, (result, err) => { 89 | if (err) throw err; 90 | dbStmt.close(); 91 | done(); 92 | }); 93 | }); 94 | 95 | it(`execSync a stored procedure with result set`, (done) => { 96 | dbStmt.execSync(sql, (result) => { 97 | expect(result.length).to.equal(5); 98 | done(); 99 | }); 100 | }); 101 | 102 | it(`exec a stored procedure with result set`, (done) => { 103 | dbStmt.exec(sql, (result, err) => { 104 | expect(result.length).to.equal(5); 105 | done(); 106 | }); 107 | }); 108 | 109 | it(`fetchAllSync a stored procedure with result set`, (done) => { 110 | dbStmt.prepareSync(sql, (err) => { 111 | dbStmt.executeSync((err) => { 112 | dbStmt.fetchAllSync((result, err) => { 113 | expect(result.length).to.equal(5); 114 | done(); 115 | }); 116 | }); 117 | }); 118 | }); 119 | 120 | it(`fetchAll a stored procedure with result set`, (done) => { 121 | dbStmt.prepareSync(sql, (err) => { 122 | dbStmt.executeSync((err) => { 123 | dbStmt.fetchAllSync((result, err) => { 124 | expect(result.length).to.equal(5); 125 | done(); 126 | }); 127 | }); 128 | }); 129 | }); 130 | }); 131 | 132 | describe('The input parameter trunction issue', () => { 133 | const sql_direct = `SELECT CITY FROM QIWS.QCUSTCDT WHERE CITY = 'Dallas2'`; 134 | const sql_bind = `SELECT * FROM QIWS.QCUSTCDT WHERE CITY = ?`; 135 | const params = [ [ 'Dallas2', IN, CHAR ] ]; 136 | 137 | it(`execSync does not truncates the too long input parameter`, (done) => { 138 | dbStmt.execSync(sql_direct, (result, error) => { 139 | expect(error).to.be.null; 140 | expect(result.length).to.equal(0); 141 | done(); 142 | }); 143 | }); 144 | 145 | it(`bindParam truncates the too long input parameter`, (done) => { 146 | dbStmt.prepareSync(sql_bind, (error) => { 147 | expect(error).to.be.null; 148 | dbStmt.bindParamSync(params, (error) => { 149 | expect(error).to.be.null; 150 | dbStmt.executeSync((out, error) => { 151 | expect(error).to.be.null; 152 | dbStmt.fetchAllSync((result, returnCode) => { 153 | expect(error).to.be.null; 154 | expect(result).to.be.a('array'); 155 | expect(result.length).to.be.greaterThan(0); 156 | done(); 157 | }); 158 | }); 159 | }); 160 | }); 161 | }); 162 | }); 163 | }); 164 | -------------------------------------------------------------------------------- /test/statementTest.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const db2a = require('../lib/db2a'); 3 | 4 | const { 5 | IN, CHAR, NUMERIC, dbstmt, dbconn, 6 | } = db2a; 7 | 8 | // Test Statement Misc 9 | describe('Statement Misc Test', () => { 10 | var dbConn, dbStmt; 11 | 12 | before(() => { 13 | dbConn = new dbconn(); 14 | dbConn.conn('*LOCAL'); 15 | }); 16 | 17 | after(() => { 18 | dbConn.disconn(); 19 | dbConn.close(); 20 | }); 21 | 22 | beforeEach(() => { 23 | dbStmt = new dbstmt(dbConn); 24 | }); 25 | 26 | afterEach(() => { 27 | dbStmt.close(); 28 | }); 29 | 30 | describe('setStmtAttr & getStmtAttr', () => { 31 | it('setStmtAttr(attribute, value) then getStmtAttr(attribute) should equal value', () => { 32 | const attr = db2a.SQL_ATTR_FOR_FETCH_ONLY; 33 | let value = db2a.SQL_TRUE; 34 | 35 | let result = dbStmt.setStmtAttr(attr, value); 36 | 37 | expect(result).to.be.true; 38 | 39 | result = dbStmt.getStmtAttr(attr); 40 | expect(result).to.be.equal(value); 41 | 42 | value = db2a.SQL_FALSE; 43 | result = dbStmt.setStmtAttr(attr, value); 44 | expect(result).to.be.true; 45 | 46 | result = dbStmt.getStmtAttr(attr); 47 | expect(result).to.be.equal(value); 48 | }); 49 | }); 50 | 51 | /* 52 | TODO create pssing unit test for nextResult() 53 | 54 | describe('nextResult', () => { 55 | it('Determines whether there is more information available on the statement', () => { 56 | let sql = 'SELECT * FROM QIWS.QCUSTCDT'; 57 | dbStmt.prepare(sql); 58 | dbStmt.execute(); 59 | let result = dbStmt.nextResult(); 60 | expect(result).to.be.a('object'); 61 | }); 62 | }) 63 | */ 64 | 65 | describe('rollback', () => { 66 | it('Rollback all changes to the database that have been made on the connection', (done) => { 67 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 68 | 69 | dbStmt.prepare(sql, (error) => { 70 | if (error) { 71 | throw error; 72 | } 73 | dbStmt.execute((out, error) => { 74 | if (error) { 75 | throw error; 76 | } 77 | const result = dbStmt.rollback(); 78 | expect(result).to.be.true; 79 | done(); 80 | }); 81 | }); 82 | }); 83 | }); 84 | 85 | 86 | describe('commit', () => { 87 | it('adds all changes to the database that have been made on the connection since connect time ', (done) => { 88 | const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 89 | 90 | const params = [9997, 'Doe', 'J D', '123 Broadway', 'Hope', 'WA', 98101, 2000, 1, 250, 0.00]; 91 | 92 | dbStmt.prepare(sql, (error) => { 93 | if (error) { 94 | throw error; 95 | } 96 | dbStmt.bindParam(params, (error) => { 97 | if (error) { 98 | throw error; 99 | } 100 | dbStmt.execute((out, error) => { 101 | if (error) { 102 | throw error; 103 | } 104 | const result = dbStmt.commit(); 105 | expect(result).to.be.true; 106 | done(); 107 | }); 108 | }); 109 | }); 110 | }); 111 | }); 112 | 113 | 114 | describe('numFields', () => { 115 | it('retrieves number of fields contained in result', (done) => { 116 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 117 | 118 | dbStmt.prepare(sql, (error) => { 119 | if (error) { 120 | throw error; 121 | } 122 | dbStmt.execute((out, error) => { 123 | if (error) { 124 | throw error; 125 | } 126 | const fields = dbStmt.numFields(); 127 | expect(fields).to.be.a('number'); 128 | done(); 129 | }); 130 | }); 131 | }); 132 | }); 133 | 134 | 135 | describe('numRows', () => { 136 | it('retrieves number of rows that were effected by a Querry', (done) => { 137 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 138 | 139 | dbStmt.prepare(sql, (error) => { 140 | if (error) { 141 | throw error; 142 | } 143 | dbStmt.execute((out, error) => { 144 | if (error) { 145 | throw error; 146 | } 147 | const rows = dbStmt.numRows(); 148 | expect(rows).to.be.a('number'); 149 | done(); 150 | }); 151 | }); 152 | }); 153 | }); 154 | 155 | 156 | describe('fieldType', () => { 157 | it('requires an int index parameter, returns the data type of the indicated column', (done) => { 158 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 159 | 160 | dbStmt.prepare(sql, (error) => { 161 | if (error) { 162 | throw error; 163 | } 164 | dbStmt.execute((out, error) => { 165 | if (error) { 166 | throw error; 167 | } 168 | const col1 = dbStmt.fieldType(0); 169 | const col2 = dbStmt.fieldType(1); 170 | 171 | expect(col1).to.be.a('number'); 172 | expect(col2).to.be.a('number'); 173 | done(); 174 | }); 175 | }); 176 | }); 177 | }); 178 | 179 | 180 | describe('fieldWidth', () => { 181 | it('requires an int index parameter, returns the field width of the indicated column', (done) => { 182 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 183 | 184 | dbStmt.prepare(sql, (error) => { 185 | if (error) { 186 | throw error; 187 | } 188 | dbStmt.execute((out, error) => { 189 | if (error) { 190 | throw error; 191 | } 192 | const col1 = dbStmt.fieldWidth(0); 193 | const col2 = dbStmt.fieldWidth(1); 194 | 195 | expect(col1).to.be.a('number'); 196 | expect(col2).to.be.a('number'); 197 | done(); 198 | }); 199 | }); 200 | }); 201 | }); 202 | 203 | 204 | describe('fieldNullable', () => { 205 | it('requires an int index parameter, returns t/f if the indicated column can be Null', (done) => { 206 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 207 | 208 | dbStmt.prepare(sql, (error) => { 209 | if (error) { 210 | throw error; 211 | } 212 | dbStmt.execute((out, error) => { 213 | if (error) { 214 | throw error; 215 | } 216 | const col1 = dbStmt.fieldNullable(0); 217 | const col2 = dbStmt.fieldNullable(1); 218 | 219 | expect(col1).to.equal(false); 220 | expect(col2).to.equal(false); 221 | done(); 222 | }); 223 | }); 224 | }); 225 | }); 226 | 227 | 228 | describe('fieldName', () => { 229 | it('requires an int index parameter,returns name of the indicated column ', (done) => { 230 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 231 | 232 | dbStmt.prepare(sql, (error) => { 233 | if (error) { 234 | throw error; 235 | } 236 | dbStmt.execute((out, error) => { 237 | if (error) { 238 | throw error; 239 | } 240 | const col1 = dbStmt.fieldName(0); 241 | const col2 = dbStmt.fieldName(1); 242 | 243 | expect(col1).to.be.a('string'); 244 | expect(col2).to.be.a('string'); 245 | done(); 246 | }); 247 | }); 248 | }); 249 | }); 250 | 251 | 252 | describe('fieldPrecise', () => { 253 | it('requires an int index parameter, returns the precision of the indicated column', (done) => { 254 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 255 | 256 | dbStmt.prepare(sql, (error) => { 257 | if (error) { 258 | throw error(); 259 | } 260 | dbStmt.execute((out, error) => { 261 | if (error) { 262 | throw error; 263 | } 264 | const col1 = dbStmt.fieldPrecise(0); 265 | const col2 = dbStmt.fieldPrecise(1); 266 | 267 | expect(col1).to.be.a('number'); 268 | expect(col2).to.be.a('number'); 269 | done(); 270 | }); 271 | }); 272 | }); 273 | }); 274 | 275 | 276 | describe('fieldScale', () => { 277 | it('requires an int index parameter, returns the scale of the indicated column', (done) => { 278 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 279 | 280 | dbStmt.prepare(sql, (error) => { 281 | if (error) { 282 | throw error; 283 | } 284 | dbStmt.executeSync((out, error) => { 285 | if (error) { 286 | throw error; 287 | } 288 | const col1 = dbStmt.fieldScale(0); 289 | const col2 = dbStmt.fieldScale(1); 290 | 291 | expect(col1).to.be.a('number'); 292 | expect(col2).to.be.a('number'); 293 | done(); 294 | }); 295 | }); 296 | }); 297 | }); 298 | 299 | 300 | describe('fieldInfo', () => { 301 | it('requires an int index parameter, returns the information of the indicated column', (done) => { 302 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 303 | 304 | dbStmt.prepare(sql, (error) => { 305 | if (error) { 306 | throw error; 307 | } 308 | dbStmt.execute((out, error) => { 309 | if (error) { 310 | throw error; 311 | } 312 | const col1 = dbStmt.fieldInfo(0); 313 | const col2 = dbStmt.fieldInfo(1); 314 | expect(col1).to.be.a('object'); 315 | expect(col2).to.be.a('object'); 316 | // console.log(JSON.stringify(col1, '', 2)); 317 | // console.log(JSON.stringify(col2, '', 2)); 318 | expect(col1).to.eql({ 319 | "Name": "CUSNUM", 320 | "Type": 2, 321 | "TypeName": "NUMERIC", 322 | "Width": 7, 323 | "Precise": 6, 324 | "Scale": 0, 325 | "Nullable": false 326 | }); 327 | expect(col2).to.eql({ 328 | "Name": "LSTNAM", 329 | "Type": 1, 330 | "TypeName": "CHAR", 331 | "Width": 7, 332 | "Precise": 8, 333 | "Scale": 0, 334 | "Nullable": false 335 | }); 336 | done(); 337 | }); 338 | }); 339 | }); 340 | }); 341 | 342 | 343 | describe('stmtError', () => { 344 | it('Returns the diagnostic information ', (done) => { 345 | const sql = 'SELECT * FROM NOT.THERE'; 346 | const expectedError = 'SQLSTATE=42704 SQLCODE=-204'; 347 | 348 | dbStmt.exec(sql, (out, error) => { 349 | dbStmt.stmtError(db2a.SQL_HANDLE_STMT, 1, (rs) => { 350 | expect(rs).to.include(expectedError); 351 | done(); 352 | }); 353 | }); 354 | }); 355 | }); 356 | 357 | 358 | describe('closeCursor', () => { 359 | it('closes any cursor associated with the dbstmt object and discards any pending results. ', (done) => { 360 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 361 | 362 | dbStmt.exec(sql, () => { 363 | const result = dbStmt.closeCursor(); 364 | expect(result).to.be.true; 365 | done(); 366 | }); 367 | }); 368 | }); 369 | 370 | 371 | describe('reset', () => { 372 | it('Reset the dbstmt object. ', (done) => { 373 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 374 | 375 | dbStmt.exec(sql, () => { 376 | const result = dbStmt.reset(); 377 | expect(result).to.be.undefined; 378 | done(); 379 | }); 380 | }); 381 | }); 382 | 383 | 384 | describe('close', () => { 385 | it('frees the statement object. ', (done) => { 386 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 387 | 388 | dbStmt.exec(sql, (result, error) => { 389 | const isClose = dbStmt.close(); 390 | expect(isClose).to.be.true; 391 | done(); 392 | }); 393 | }); 394 | }); 395 | 396 | describe('asNumber', () => { 397 | it('should default to false', () => { 398 | let result = dbStmt.asNumber(); 399 | expect(result).to.be.false; 400 | }); 401 | 402 | 403 | it('when false should return numbers as strings', (done) => { 404 | const sql = `select 405 | cast(-32768 as SMALLINT) MIN_SMALLINT, 406 | cast(+32767 as SMALLINT) MAX_SMALLINT 407 | from sysibm.sysdummy1`; 408 | 409 | dbStmt.asNumber(false); 410 | 411 | dbStmt.exec(sql, (result, error) => { 412 | expect(error).to.be.null; 413 | expect(result).to.be.an('array'); 414 | expect(result.length).to.be.greaterThan(0); 415 | expect(result).to.eql([{ 416 | "MIN_SMALLINT": "-32768", 417 | "MAX_SMALLINT": "32767" 418 | }]); 419 | done(); 420 | }); 421 | }); 422 | 423 | it('when true should return numbers when safe to do so', (done) => { 424 | const sql = `select 425 | cast(-32768 as SMALLINT) MIN_SMALLINT, 426 | cast(+32767 as SMALLINT) MAX_SMALLINT, 427 | cast(-2147483648 as INT) MIN_INT, 428 | cast(+2147483647 as INT) MAX_INT, 429 | cast(999999999999999 as DECIMAL(15,0)) as DEC_SAFE_15_0, 430 | cast(.999999999999999 as DECIMAL(15,15)) as DEC_SAFE_15_15, 431 | cast(-3.4e38 as REAL) MIN_REAL, 432 | cast(+3.4e38 as REAL) MAX_REAL, 433 | cast(-12345.54321 as REAL) LONG_REAL, 434 | cast(-1.18e-38 as REAL) MAX_NEG_REAL, 435 | cast(+1.18e-38 as REAL) MIN_POS_REAL, 436 | cast(-1.79e308 as DOUBLE) MIN_DOUBLE, 437 | cast(+1.79e308 as DOUBLE) MAX_DOUBLE, 438 | cast(-2.23e-308 as DOUBLE) MAX_NEG_DOUBLE, 439 | cast(+2.23e-308 as DOUBLE) MIN_POS_DOUBLE, 440 | --these values do not fit in a javascript number datatype 441 | cast(-9223372036854775808 as BIGINT) MIN_BIGINT, 442 | cast(+9223372036854775807 as BIGINT) MAX_BIGINT, 443 | cast(9999999999999999 as DECIMAL(16,0)) as DEC_NOT_SAFE_16_0, 444 | cast(-9.999999999999999e384 as DECFLOAT(16)) MIN_DECFLOAT16, 445 | cast(+9.999999999999999e384 as DECFLOAT(16)) MAX_DECFLOAT16, 446 | cast(-1e-383 as DECFLOAT(16)) MAX_NEG_DECFLOAT16, 447 | cast(+1e-383 as DECFLOAT(16)) MIN_POS_DECFLOAT16, 448 | cast(-9.999999999999999999999999999999999e6144 as DECFLOAT(34)) MIN_DECFLOAT34, 449 | cast(+9.999999999999999999999999999999999e6144 as DECFLOAT(34)) MAX_DECFLOAT34, 450 | cast(-1e-6143 as DECFLOAT(34)) MAX_NEG_DECFLOAT34, 451 | cast(+1e-6143 as DECFLOAT(34)) MIN_POS_DECFLOAT34, 452 | cast( -999999999999999999999999999999999999999999999999999999999999999 AS DECIMAL(63)) AS MIN_DEC63, 453 | cast( +999999999999999999999999999999999999999999999999999999999999999 AS DECIMAL(63)) AS MAX_DEC63, 454 | cast(-.000000000000000000000000000000000000000000000000000000000000001 AS DECIMAL(63,63)) AS MAX_NEG_DEC63P63, 455 | cast(+.000000000000000000000000000000000000000000000000000000000000001 AS DECIMAL(63,63)) AS MIN_POS_DEC63P63, 456 | cast( -999999999999999999999999999999999999999999999999999999999999999 AS NUMERIC(63)) AS MIN_NUM63P0, 457 | cast( +999999999999999999999999999999999999999999999999999999999999999 AS NUMERIC(63)) AS MAX_NUM63P0, 458 | cast(-.000000000000000000000000000000000000000000000000000000000000001 AS NUMERIC(63,63)) AS MAX_NEG_NUM63P63, 459 | cast(+.000000000000000000000000000000000000000000000000000000000000001 AS NUMERIC(63,63)) AS MIN_POS_NUM63P63 460 | from sysibm.sysdummy1`; 461 | 462 | dbStmt.asNumber(true); 463 | 464 | dbStmt.exec(sql, (result, error) => { 465 | expect(error).to.be.null; 466 | expect(result).to.be.an('array'); 467 | expect(result.length).to.be.greaterThan(0); 468 | expect(result).to.eql([{ 469 | "MIN_SMALLINT": -32768, 470 | "MAX_SMALLINT": 32767, 471 | "MIN_INT": -2147483648, 472 | "MAX_INT": 2147483647, 473 | "DEC_SAFE_15_0": 999999999999999, 474 | "DEC_SAFE_15_15": 0.999999999999999, 475 | "MIN_REAL": -3.4e38, 476 | "MAX_REAL": 3.4e38, 477 | "LONG_REAL": -12345.5, 478 | "MAX_NEG_REAL": -1.18e-38, 479 | "MIN_POS_REAL": 1.18e-38, 480 | "MIN_DOUBLE": -1.79e308, 481 | "MAX_DOUBLE": 1.79e308, 482 | "MAX_NEG_DOUBLE": -2.23e-308, 483 | "MIN_POS_DOUBLE": 2.23e-308, 484 | "MIN_BIGINT": "-9223372036854775808", 485 | "MAX_BIGINT": "9223372036854775807", 486 | "DEC_NOT_SAFE_16_0": "9999999999999999", 487 | "MIN_DECFLOAT16": "-9.999999999999999E+384", 488 | "MAX_DECFLOAT16": "9.999999999999999E+384", 489 | "MAX_NEG_DECFLOAT16": "-1E-383", 490 | "MIN_POS_DECFLOAT16": "1E-383", 491 | "MIN_DECFLOAT34": "-9.999999999999999999999999999999999E+6144", 492 | "MAX_DECFLOAT34": "9.999999999999999999999999999999999E+6144", 493 | "MAX_NEG_DECFLOAT34": "-1E-6143", 494 | "MIN_POS_DECFLOAT34": "1E-6143", 495 | "MIN_DEC63": "-999999999999999999999999999999999999999999999999999999999999999", 496 | "MAX_DEC63": "999999999999999999999999999999999999999999999999999999999999999", 497 | "MAX_NEG_DEC63P63": "-.000000000000000000000000000000000000000000000000000000000000001", 498 | "MIN_POS_DEC63P63": ".000000000000000000000000000000000000000000000000000000000000001", 499 | "MIN_NUM63P0": "-999999999999999999999999999999999999999999999999999999999999999", 500 | "MAX_NUM63P0": "999999999999999999999999999999999999999999999999999999999999999", 501 | "MAX_NEG_NUM63P63": "-.000000000000000000000000000000000000000000000000000000000000001", 502 | "MIN_POS_NUM63P63": ".000000000000000000000000000000000000000000000000000000000000001" 503 | }]); 504 | done(); 505 | }); 506 | }); 507 | }); 508 | }); 509 | -------------------------------------------------------------------------------- /test/syncStatementTest.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const util = require('util'); 3 | const db2a = require('../lib/db2a'); 4 | 5 | const { 6 | OUT, IN, CHAR, CLOB, NUMERIC, dbconn, dbstmt, 7 | } = db2a; 8 | 9 | describe('Statement Sync Test', () => { 10 | var dbConn, dbStmt; 11 | 12 | before(() => { 13 | dbConn = new dbconn(); 14 | dbConn.conn('*LOCAL'); 15 | }); 16 | 17 | after(() => { 18 | dbConn.disconn(); 19 | dbConn.close(); 20 | }); 21 | 22 | beforeEach(() => { 23 | dbStmt = new dbstmt(dbConn); 24 | }); 25 | 26 | afterEach(() => { 27 | dbStmt.close(); 28 | }); 29 | 30 | describe('prepare callback', () => { 31 | it('Prepares valid SQL and sends it to the DBMS, if fail, error is returned. ', () => { 32 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 33 | dbStmt.prepareSync(sql, (error) => { 34 | if (error) { 35 | throw error; 36 | } 37 | expect(error).to.be.null; 38 | }); 39 | }); 40 | }); 41 | 42 | describe('prepare no-callback', () => { 43 | it('Prepares valid SQL and sends it to the DBMS, if fail, error is returned. ', () => { 44 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 45 | dbStmt.prepareSync(sql); 46 | }); 47 | }); 48 | 49 | describe('bindParams callback', () => { 50 | it('associate parameter markers in an SQL statement to app variables', () => { 51 | const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 52 | const params = [ 53 | [9997, IN, NUMERIC], // CUSNUM 54 | ['Doe', IN, CHAR], // LASTNAME 55 | ['J D', IN, CHAR], // INITIAL 56 | ['123 Broadway', IN, CHAR], // ADDRESS 57 | ['Hope', IN, CHAR], // CITY 58 | ['WA', IN, CHAR], // STATE 59 | [98101, IN, NUMERIC], // ZIP 60 | [2000, IN, NUMERIC], // CREDIT LIMIT 61 | [1, IN, NUMERIC], // change 62 | [250, IN, NUMERIC], // BAL DUE 63 | [0.00, IN, NUMERIC], // CREDIT DUE 64 | ]; 65 | 66 | const dbConn2 = new dbconn(); 67 | dbConn2.conn('*LOCAL'); 68 | const dbStmt2 = new dbstmt(dbConn2); 69 | 70 | const result = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 71 | let rowsBefore = result[0]['00001']; 72 | 73 | rowsBefore = Number(rowsBefore); 74 | dbStmt.close(); 75 | 76 | dbStmt2.prepareSync(sql, (error) => { 77 | if (error) { 78 | throw error; 79 | } 80 | dbStmt2.bindParamSync(params, (error) => { 81 | if (error) { 82 | throw error; 83 | } 84 | expect(error).to.be.null; 85 | 86 | dbStmt2.executeSync((out, error) => { 87 | if (error) { 88 | throw error; 89 | } 90 | dbStmt = new dbstmt(dbConn); 91 | 92 | const result2 = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 93 | let rowsAfter = result2[0]['00001']; 94 | 95 | rowsAfter = Number(rowsAfter); 96 | 97 | expect(rowsAfter).to.equal(rowsBefore + 1); 98 | }); 99 | }); 100 | }); 101 | }); 102 | }); 103 | 104 | describe('bindParams callback (1-D array)', () => { 105 | it('associate parameter markers in an SQL statement to app variables', () => { 106 | const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 107 | const params = [9997, 'Doe', 'J D', '123 Broadway', 'Hope', 'WA', 98101, 2000, 1, 250, 0.00]; 108 | 109 | const dbConn2 = new dbconn(); 110 | dbConn2.conn('*LOCAL'); 111 | const dbStmt2 = new dbstmt(dbConn2); 112 | 113 | const result = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 114 | let rowsBefore = result[0]['00001']; 115 | 116 | rowsBefore = Number(rowsBefore); 117 | dbStmt.close(); 118 | 119 | dbStmt2.prepareSync(sql, (error) => { 120 | if (error) { 121 | throw error; 122 | } 123 | dbStmt2.bindParamSync(params, (error) => { 124 | if (error) { 125 | throw error; 126 | } 127 | expect(error).to.be.null; 128 | 129 | dbStmt2.executeSync((out, error) => { 130 | if (error) { 131 | throw error; 132 | } 133 | dbStmt = new dbstmt(dbConn); 134 | 135 | const result2 = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 136 | let rowsAfter = result2[0]['00001']; 137 | 138 | rowsAfter = Number(rowsAfter); 139 | 140 | expect(rowsAfter).to.equal(rowsBefore + 1); 141 | }); 142 | }); 143 | }); 144 | }); 145 | }); 146 | 147 | describe('bindParameters callback', () => { 148 | it('associate parameter markers in an SQL statement to app variables', () => { 149 | const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 150 | const params = [9997, 'Doe', 'J D', '123 Broadway', 'Hope', 'WA', 98101, 2000, 1, 250, 0.00]; 151 | 152 | const dbConn2 = new dbconn(); 153 | dbConn2.conn('*LOCAL'); 154 | const dbStmt2 = new dbstmt(dbConn2); 155 | 156 | const result = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 157 | let rowsBefore = result[0]['00001']; 158 | 159 | rowsBefore = Number(rowsBefore); 160 | dbStmt.close(); 161 | 162 | dbStmt2.prepareSync(sql, (error) => { 163 | if (error) { 164 | throw error; 165 | } 166 | dbStmt2.bindParametersSync(params, (error) => { 167 | if (error) { 168 | throw error; 169 | } 170 | expect(error).to.be.null; 171 | 172 | dbStmt2.executeSync((out, error) => { 173 | if (error) { 174 | throw error; 175 | } 176 | dbStmt = new dbstmt(dbConn); 177 | 178 | const result2 = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 179 | let rowsAfter = result2[0]['00001']; 180 | 181 | rowsAfter = Number(rowsAfter); 182 | 183 | expect(rowsAfter).to.equal(rowsBefore + 1); 184 | }); 185 | }); 186 | }); 187 | }); 188 | }); 189 | 190 | describe('bindParams no-callback', () => { 191 | it('associate parameter markers in an SQL statement to app variables', () => { 192 | const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 193 | const params = [ 194 | [9997, IN, NUMERIC], // CUSNUM 195 | ['Doe', IN, CHAR], // LASTNAME 196 | ['J D', IN, CHAR], // INITIAL 197 | ['123 Broadway', IN, CHAR], // ADDRESS 198 | ['Hope', IN, CHAR], // CITY 199 | ['WA', IN, CHAR], // STATE 200 | [98101, IN, NUMERIC], // ZIP 201 | [2000, IN, NUMERIC], // CREDIT LIMIT 202 | [1, IN, NUMERIC], // change 203 | [250, IN, NUMERIC], // BAL DUE 204 | [0.00, IN, NUMERIC], // CREDIT DUE 205 | ]; 206 | 207 | const dbConn2 = new dbconn(); 208 | dbConn2.conn('*LOCAL'); 209 | const dbStmt2 = new dbstmt(dbConn2); 210 | // first get count of current rows 211 | const result = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 212 | let rowsBefore = result[0]['00001']; 213 | 214 | rowsBefore = Number(rowsBefore); // count retrurns as a String cast it to Number 215 | 216 | dbStmt.close(); 217 | 218 | // now perform insert 219 | dbStmt2.prepareSync(sql); 220 | dbStmt2.bindParamSync(params); 221 | dbStmt2.executeSync(); 222 | 223 | dbStmt = new dbstmt(dbConn); 224 | 225 | const result2 = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 226 | let rowsAfter = result2[0]['00001']; 227 | 228 | rowsAfter = Number(rowsAfter); 229 | 230 | expect(rowsAfter).to.equal((rowsBefore + 1)); 231 | }); 232 | }); 233 | 234 | describe('bindParams no-callback (1-D array)', () => { 235 | it('associate parameter markers in an SQL statement to app variables', () => { 236 | const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 237 | const params = [9997, 'Doe', 'J D', '123 Broadway', 'Hope', 'WA', 98101, 2000, 1, 250, 0.00]; 238 | 239 | const dbConn2 = new dbconn(); 240 | dbConn2.conn('*LOCAL'); 241 | const dbStmt2 = new dbstmt(dbConn2); 242 | // first get count of current rows 243 | const result = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 244 | let rowsBefore = result[0]['00001']; 245 | 246 | rowsBefore = Number(rowsBefore); // count retrurns as a String cast it to Number 247 | 248 | dbStmt.close(); 249 | 250 | // now perform insert 251 | dbStmt2.prepareSync(sql); 252 | dbStmt2.bindParamSync(params); 253 | dbStmt2.executeSync(); 254 | 255 | dbStmt = new dbstmt(dbConn); 256 | 257 | const result2 = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 258 | let rowsAfter = result2[0]['00001']; 259 | 260 | rowsAfter = Number(rowsAfter); 261 | 262 | expect(rowsAfter).to.equal((rowsBefore + 1)); 263 | }); 264 | }); 265 | 266 | describe('bindParameters no-callback', () => { 267 | it('associate parameter markers in an SQL statement to app variables', () => { 268 | const sql = 'INSERT INTO QIWS.QCUSTCDT(CUSNUM,LSTNAM,INIT,STREET,CITY,STATE,ZIPCOD,CDTLMT,CHGCOD,BALDUE,CDTDUE) VALUES (?,?,?,?,?,?,?,?,?,?,?) with NONE '; 269 | const params = [9997, 'Doe', 'J D', '123 Broadway', 'Hope', 'WA', 98101, 2000, 1, 250, 0.00]; 270 | 271 | const dbConn2 = new dbconn(); 272 | dbConn2.conn('*LOCAL'); 273 | const dbStmt2 = new dbstmt(dbConn2); 274 | // first get count of current rows 275 | const result = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 276 | let rowsBefore = result[0]['00001']; 277 | 278 | rowsBefore = Number(rowsBefore); // count retrurns as a String cast it to Number 279 | 280 | dbStmt.close(); 281 | 282 | // now perform insert 283 | dbStmt2.prepareSync(sql); 284 | dbStmt2.bindParametersSync(params); 285 | dbStmt2.executeSync(); 286 | 287 | dbStmt = new dbstmt(dbConn); 288 | 289 | const result2 = dbStmt.execSync('SELECT COUNT(CUSNUM) FROM QIWS.QCUSTCDT'); 290 | let rowsAfter = result2[0]['00001']; 291 | 292 | rowsAfter = Number(rowsAfter); 293 | 294 | expect(rowsAfter).to.equal((rowsBefore + 1)); 295 | }); 296 | }); 297 | 298 | describe('exec callback', () => { 299 | it('performs action of given SQL String', () => { 300 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 301 | dbStmt.execSync(sql, (result, error) => { 302 | if (error) { 303 | throw error; 304 | } 305 | expect(error).to.be.null; 306 | expect(result).to.be.an('array'); 307 | expect(result.length).to.be.greaterThan(0); 308 | expect(result[0]).to.be.an('object'); 309 | }); 310 | }); 311 | }); 312 | 313 | describe('exec no-callback', () => { 314 | it('performs action of given SQL String', () => { 315 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 316 | const result = dbStmt.execSync(sql); 317 | expect(result).to.be.an('array'); 318 | expect(result.length).to.be.greaterThan(0); 319 | expect(result[0]).to.be.an('object'); 320 | }); 321 | }); 322 | 323 | describe('execute callback', () => { 324 | it('retrieves output parameters from stored proc using executeSync with a callback', () => { 325 | const sql = 'call QXMLSERV.iPLUG512K(?,?,?,?)'; 326 | const ipc = '*NA'; 327 | const ctl = '*here'; 328 | const xmlIn = `system 'wrksbs'`; 329 | const xmlOut = ''; 330 | const params = [ 331 | [ipc, IN, CHAR], 332 | [ctl, IN, CHAR], 333 | [xmlIn, IN, CLOB], 334 | [xmlOut, OUT, CLOB], 335 | ]; 336 | 337 | dbStmt.prepareSync(sql, (error) => { 338 | if (error) { 339 | throw error; 340 | } 341 | dbStmt.bindParamSync(params, (error) => { 342 | if (error) { 343 | throw error; 344 | } 345 | dbStmt.executeSync((result, error) => { 346 | if (error) { 347 | throw error; 348 | } 349 | expect(error).to.be.null; 350 | expect(result).to.be.a('array'); 351 | expect(result.length).to.be.eq(1); 352 | }); 353 | }); 354 | }); 355 | }); 356 | 357 | 358 | it('executes prepared statement using executeSync with callback. Returns null because no output params are available', () => { 359 | const sql = 'SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'; 360 | const params = [ 361 | [10.00, IN, NUMERIC], 362 | ]; 363 | dbStmt.prepareSync(sql, (error) => { 364 | if (error) { 365 | throw error; 366 | } 367 | dbStmt.bindParamSync(params, (error) => { 368 | if (error) { 369 | throw error; 370 | } 371 | dbStmt.executeSync((out, error) => { 372 | if (error) { 373 | throw error; 374 | } 375 | expect(error).to.be.null; 376 | expect(out).to.be.null; 377 | }); 378 | }); 379 | }); 380 | }); 381 | }); 382 | 383 | describe('execute callback (1-D array)', () => { 384 | it('retrieves output parameters from stored proc using executeSync with a callback', () => { 385 | const sql = 'call QXMLSERV.iPLUG512K(?,?,?,?)'; 386 | const ipc = '*NA'; 387 | const ctl = '*here'; 388 | const xmlIn = `system 'wrksbs'`; 389 | const xmlOut = ''; 390 | const params = [ipc, ctl, xmlIn, xmlOut]; 391 | 392 | dbStmt.prepareSync(sql, (error) => { 393 | if (error) { 394 | throw error; 395 | } 396 | dbStmt.bindParamSync(params, (error) => { 397 | if (error) { 398 | throw error; 399 | } 400 | dbStmt.executeSync((result, error) => { 401 | if (error) { 402 | throw error; 403 | } 404 | expect(error).to.be.null; 405 | expect(result).to.be.a('array'); 406 | expect(result).to.be.a('array'); 407 | expect(result.length).to.be.eq(params.length); 408 | }); 409 | }); 410 | }); 411 | }); 412 | 413 | 414 | it('executes prepared statement using executeSync with callback. Returns null because no output params are available', () => { 415 | const sql = 'SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'; 416 | const params = [ 10.00 ]; 417 | dbStmt.prepareSync(sql, (error) => { 418 | if (error) { 419 | throw error; 420 | } 421 | dbStmt.bindParamSync(params, (error) => { 422 | if (error) { 423 | throw error; 424 | } 425 | dbStmt.executeSync((result, error) => { 426 | if (error) { 427 | throw error; 428 | } 429 | expect(error).to.be.null; 430 | expect(result).to.be.a('array'); 431 | expect(result.length).to.be.eq(params.length); 432 | }); 433 | }); 434 | }); 435 | }); 436 | }); 437 | 438 | describe('execute callback (bindParameters)', () => { 439 | it('retrieves output parameters from stored proc using executeSync with a callback', () => { 440 | const sql = 'call QXMLSERV.iPLUG512K(?,?,?,?)'; 441 | const ipc = '*NA'; 442 | const ctl = '*here'; 443 | const xmlIn = `system 'wrksbs'`; 444 | const xmlOut = ''; 445 | const params = [ipc, ctl, xmlIn, xmlOut]; 446 | 447 | dbStmt.prepareSync(sql, (error) => { 448 | if (error) { 449 | throw error; 450 | } 451 | dbStmt.bindParametersSync(params, (error) => { 452 | if (error) { 453 | throw error; 454 | } 455 | dbStmt.executeSync((result, error) => { 456 | if (error) { 457 | throw error; 458 | } 459 | expect(error).to.be.null; 460 | expect(result).to.be.a('array'); 461 | expect(result.length).to.be.eq(params.length); 462 | }); 463 | }); 464 | }); 465 | }); 466 | 467 | 468 | it('executes prepared statement using executeSync with callback. Returns null because no output params are available', () => { 469 | const sql = 'SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'; 470 | const params = [ 10.00 ]; 471 | dbStmt.prepareSync(sql, (error) => { 472 | if (error) { 473 | throw error; 474 | } 475 | dbStmt.bindParametersSync(params, (error) => { 476 | if (error) { 477 | throw error; 478 | } 479 | dbStmt.executeSync((result, error) => { 480 | if (error) { 481 | throw error; 482 | } 483 | expect(error).to.be.null; 484 | expect(result).to.be.a('array'); 485 | expect(result.length).to.be.eq(params.length); 486 | }); 487 | }); 488 | }); 489 | }); 490 | }); 491 | 492 | describe('execute no-callback', () => { 493 | it('executes prepared statement using executeSync without callback. Returns null because no output params are available', () => { 494 | const sql = 'SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'; 495 | const params = [ 496 | [10.00, IN, NUMERIC], 497 | ]; 498 | 499 | dbStmt.prepareSync(sql); 500 | dbStmt.bindParamSync(params); 501 | const out = dbStmt.executeSync(); 502 | expect(out).to.be.null; 503 | }); 504 | }); 505 | 506 | describe('execute no-callback (1-D array)', () => { 507 | it('executes prepared statement using executeSync without callback. Returns null because no output params are available', () => { 508 | const sql = 'SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'; 509 | const params = [ 10.00 ]; 510 | 511 | dbStmt.prepareSync(sql); 512 | dbStmt.bindParamSync(params); 513 | const result = dbStmt.executeSync(); 514 | expect(result).to.be.a('array'); 515 | expect(result.length).to.be.eq(params.length); 516 | }); 517 | }); 518 | 519 | describe('execute no-callback (bindParameters)', () => { 520 | it('executes prepared statement using executeSync without callback. Returns null because no output params are available', () => { 521 | const sql = 'SELECT * FROM QIWS.QCUSTCDT WHERE BALDUE > ?'; 522 | const params = [ 10.00 ]; 523 | 524 | dbStmt.prepareSync(sql); 525 | dbStmt.bindParametersSync(params); 526 | const result = dbStmt.executeSync(); 527 | expect(result).to.be.a('array'); 528 | expect(result.length).to.be.eq(params.length); 529 | }); 530 | }); 531 | 532 | describe('fetchAll callback', () => { 533 | it('retrieves results from execute function:', () => { 534 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 535 | 536 | dbStmt.prepareSync(sql, (error) => { 537 | if (error) { 538 | throw error; 539 | } 540 | dbStmt.executeSync((out, error) => { 541 | if (error) { 542 | throw error; 543 | } 544 | dbStmt.fetchAllSync((result, error) => { 545 | if (error) { 546 | console.log(util.inspect(error)); 547 | throw error; 548 | } 549 | expect(error).to.be.null; 550 | expect(result).to.be.a('array'); 551 | expect(result.length).to.be.greaterThan(0); 552 | }); 553 | }); 554 | }); 555 | }); 556 | }); 557 | 558 | describe('fetchAll no-callback', () => { 559 | it('retrieves results from execute function:', () => { 560 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 561 | 562 | dbStmt.prepareSync(sql); 563 | dbStmt.executeSync(); 564 | const result = dbStmt.fetchAllSync(); 565 | expect(result).to.be.a('array'); 566 | expect(result.length).to.be.greaterThan(0); 567 | }); 568 | }); 569 | 570 | describe('fetch callback', () => { 571 | it('retrieves results from execute function:', () => { 572 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 573 | 574 | dbStmt.prepareSync(sql, (error) => { 575 | if (error) { 576 | throw error; 577 | } 578 | dbStmt.executeSync((out, error) => { 579 | if (error) { 580 | throw error; 581 | } 582 | dbStmt.fetchSync((result, returnCode) => { 583 | if (returnCode !== 0) { // SQL_SUCCESS 584 | throw new Error('Rreturn Code was Not SQL SUCESS'); 585 | } 586 | expect(returnCode).to.equal(0); 587 | expect(result).to.be.a('object'); 588 | }); 589 | }); 590 | }); 591 | }); 592 | }); 593 | 594 | describe('fetch no-callback', () => { 595 | it('retrieves results from execute function:', () => { 596 | const sql = 'SELECT * FROM QIWS.QCUSTCDT'; 597 | 598 | dbStmt.prepareSync(sql); 599 | dbStmt.executeSync(); 600 | const result = dbStmt.fetchSync(); 601 | expect(result).to.be.a('object'); 602 | }); 603 | }); 604 | }); 605 | --------------------------------------------------------------------------------