├── .env-cmdrc ├── .eslintrc.cjs ├── .gitignore ├── .npmignore ├── .vscode ├── .travis.yml ├── c_cpp_properties.json ├── launch.json ├── settings.json └── tasks.json ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── appveyor.yml ├── binding.gyp ├── docker ├── alpine-msnodesqlv8 │ ├── Dockerfile │ └── README ├── debian-msnodesqlv8 │ ├── Dockerfile │ └── README ├── fedora-msnodesqlv8 │ ├── Dockerfile │ └── README ├── rocky-msnodesqlv8 │ ├── Dockerfile │ └── README └── ubuntu-msnodesqlv8 │ ├── Dockerfile │ └── README ├── eslint.config.mjs ├── lib ├── base-promises.js ├── connection.js ├── data-struture │ ├── heap │ │ ├── Heap.js │ │ ├── MaxHeap.js │ │ └── MinHeap.js │ ├── priority-queue │ │ └── PriorityQueue.js │ └── utils │ │ └── comparator │ │ └── Comparator.js ├── dialect.js ├── driver.js ├── index.d.ts ├── meta.js ├── notifier.js ├── pool.js ├── prepared-statement.js ├── procedure-bound.js ├── procedure-meta.js ├── procedure.js ├── queries │ ├── proc_describe.sql │ ├── server_version.sql │ ├── table_describe.2014.sql │ ├── table_describe.sql │ └── user_type.sql ├── query-aggregator.js ├── query-handler.js ├── queue.js ├── reader.js ├── sequelize │ ├── connection.js │ ├── index.js │ └── request.js ├── shema-splitter.js ├── sql-client.js ├── sql.js ├── table-builder.js ├── table-bulk-op-mgr.js ├── table-column.js ├── table-meta.js ├── table.js ├── user.js └── util.js ├── package-lock.json ├── package.json ├── samples ├── javascript │ ├── geography.js │ ├── multiple-error.js │ ├── points.json │ ├── pooling.js │ ├── procedure.js │ ├── sequelize.js │ ├── simple-demo.js │ ├── simple-http.js │ ├── simple-pool.js │ ├── streaming.js │ ├── sybase-query.js │ ├── table-builder.js │ ├── thread-workers.js │ ├── throttle.js │ ├── tvp.js │ ├── txn.js │ ├── txn2.js │ ├── txn3.js │ └── worker-item.js └── typescript │ ├── builder.ts │ ├── db-backup.ts │ ├── demo-support.js │ ├── mssql-demo.ts │ ├── pooling.ts │ ├── promises.ts │ └── simple-demo.ts ├── src ├── BeginTranOperation.cpp ├── BeginTranOperation.h ├── BigIntColumn.h ├── BinaryColumn.cpp ├── BinaryColumn.h ├── BoolColumn.h ├── BoundDatum.cpp ├── BoundDatum.h ├── BoundDatumHelper.h ├── BoundDatumSet.cpp ├── BoundDatumSet.h ├── CMakeLists.txt ├── CancelOperation.cpp ├── CancelOperation.h ├── CharColumn.h ├── CloseOperation.cpp ├── CloseOperation.h ├── CollectOperation.cpp ├── CollectOperation.h ├── Column.cpp ├── Column.h ├── Connection.cpp ├── Connection.h ├── ConnectionHandles.cpp ├── ConnectionHandles.h ├── CriticalSection.h ├── EndTranOperation.cpp ├── EndTranOperation.h ├── FreeStatementOperation.cpp ├── FreeStatementOperation.h ├── IntColumn.h ├── MutateJS.cpp ├── MutateJS.h ├── NodeColumns.h ├── NullColumn.h ├── NumberColumn.h ├── OdbcConnection.cpp ├── OdbcConnection.h ├── OdbcConnectionBridge.cpp ├── OdbcConnectionBridge.h ├── OdbcError.cpp ├── OdbcError.h ├── OdbcHandle.cpp ├── OdbcHandle.h ├── OdbcHelper.h ├── OdbcOperation.cpp ├── OdbcOperation.h ├── OdbcOperationQueue.h ├── OdbcPreparedStatement.h ├── OdbcStatement.cpp ├── OdbcStatement.h ├── OdbcStatementCache.cpp ├── OdbcStatementCache.h ├── OpenOperation.cpp ├── OpenOperation.h ├── Operation.cpp ├── Operation.h ├── OperationManager.cpp ├── OperationManager.h ├── PollingModeOperation.cpp ├── PollingModeOperation.h ├── PrepareOperation.cpp ├── PrepareOperation.h ├── ProcedureOperation.cpp ├── ProcedureOperation.h ├── QueryOperation.cpp ├── QueryOperation.h ├── QueryOperationParams.cpp ├── QueryOperationParams.h ├── QueryPreparedOperation.cpp ├── QueryPreparedOperation.h ├── ReadColumnOperation.cpp ├── ReadColumnOperation.h ├── ReadNextResultOperation.cpp ├── ReadNextResultOperation.h ├── ResultSet.cpp ├── ResultSet.h ├── StringColumn.h ├── TimestampColumn.cpp ├── TimestampColumn.h ├── UnbindOperation.cpp ├── UnbindOperation.h ├── Utility.cpp ├── Utility.h ├── addon.cpp ├── addon.h ├── bcp.cpp ├── bcp.h ├── sqlncli-linux.h ├── sqlncli.h ├── stdafx.cpp └── stdafx.h ├── test ├── bcp.js ├── bulk.js ├── compoundqueries.js ├── concurrent.js ├── connect.js ├── connection-pool.js ├── datatypes.js ├── dates.js ├── encrypt.js ├── env │ ├── CommonTestFunctions.js │ ├── bcp-entry.js │ ├── builder-checker.js │ ├── bulk-table-test.js │ ├── cmd-test.ts │ ├── data │ │ └── SampleJPGImage_50kbmb.jpg │ ├── employee.js │ ├── encrypt-helper.js │ ├── geography-helper.js │ ├── json-helper.js │ ├── json │ │ ├── employee.json │ │ └── points.json │ ├── proc-helper.js │ ├── table-helper.js │ ├── test-env.js │ ├── time-helper.js │ ├── tvp-helper.js │ └── type-table-helper.js ├── geography.js ├── json.js ├── mocha.opts ├── multiple-errors.js ├── params.js ├── parsing.js ├── pause.js ├── prepared.js ├── promises.js ├── query.js ├── querycancel.js ├── querytimeout.js ├── sproc.js ├── sql │ ├── bulkColumn.sql │ ├── bulkTest.sql │ ├── employee.sql │ ├── emptyString.sql │ └── template.sql ├── table-builder.js ├── tvp.js ├── txn.js ├── userbind.js └── warnings.js ├── tool ├── appveyor-win.cmd ├── appveyor.ps1 ├── download_assets.sh ├── install-driver.ps1 ├── install_openssl.sh ├── odbc.sh ├── openssl.sh └── rename-release.ps1 ├── tsconfig.json └── wiki.md /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | "eslint:recommended", 4 | "plugin:@typescript-eslint/eslint-recommended", 5 | "plugin:@typescript-eslint/recommended", 6 | "standard-with-typescript" 7 | ], 8 | parser: '@typescript-eslint/parser', 9 | "plugins": [ 10 | "@typescript-eslint" 11 | ], 12 | parserOptions: { 13 | parser: '@typescript-eslint/parser', 14 | project: './tsconfig.json', 15 | tsconfigRootDir: __dirname, 16 | }, 17 | root: true, 18 | rules: { 19 | "@typescript-eslint/no-explicit-any":0, 20 | "@typescript-eslint/return-await": 0, 21 | "@typescript-eslint/no-this-alias": 0, 22 | "@typescript-eslint/no-var-requires": 0, 23 | "@typescript-eslint/no-unused-vars": 0, 24 | "@typescript-eslint/restrict-template-expressions": 0, 25 | "@typescript-eslint/no-misused-promises": 0, 26 | "@typescript-eslint/strict-boolean-expressions": 0, 27 | "@typescript-eslint/consistent-type-assertions": 0, 28 | "@typescript-eslint/no-extraneous-class": 0, 29 | "@typescript-eslint/no-dynamic-delete": 0, 30 | "no-this-assignment": 0, 31 | "no-async-promise-executor": 0, 32 | "valid-typeof": 0, 33 | "@typescript-eslint/consistent-type-imports":0, 34 | "@typescript-eslint/ban-types":0, 35 | "@typescript-eslint/prefer-ts-expect-error":0, 36 | "@typescript-eslint/explicit-function-return-type":0, 37 | "@typescript-eslint/no-unsafe-argument":0, 38 | "@typescript-eslint/unbound-method":0 39 | }, 40 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | tmp.txt 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (https://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # Typescript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | # next.js build output 62 | .next 63 | 64 | # compiled javascript 65 | dist/ 66 | 67 | # Prebuilds 68 | prebuilds/ 69 | build/ 70 | 71 | # Mac 72 | .DS_Store 73 | tags 74 | tags.lock 75 | tags.temp 76 | runtest-linux 77 | .gitignore 78 | runtest-win.exe 79 | runtest-macos 80 | /mochawesome-report/ 81 | .idea/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | .nyc_output/ 4 | coverage/ 5 | dist/ 6 | mochawesome-report/ 7 | build/ 8 | prebuilds/ 9 | samples/ 10 | test/ 11 | tool/ 12 | .npmignore 13 | appveyor.yml 14 | runtest.js -------------------------------------------------------------------------------- /.vscode/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "14" 4 | 5 | branches: 6 | only: 7 | - linux 8 | 9 | before_install: 10 | - sudo ACCEPT_EULA=Y apt-get install msodbcsql17 11 | - sudo ACCEPT_EULA=Y apt-get install mssql-tools 12 | - sudo apt-get install unixodbc-dev 13 | 14 | before_script: 15 | - node --version 16 | - npm install 17 | 18 | script: 19 | - -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/node_modules/nan", 7 | "${workspaceFolder}/**", 8 | "/usr/include/**", 9 | "/opt/microsoft/msodbcsql17/include/**" 10 | ], 11 | "defines": [ 12 | "LINUX_BUILD", 13 | "UNICODE" 14 | ], 15 | "compilerPath": "/usr/bin/gcc", 16 | "cStandard": "c11", 17 | "cppStandard": "gnu++14", 18 | "intelliSenseMode": "clang-x64" 19 | }, 20 | { 21 | "name": "Win32", 22 | "includePath": [ 23 | "${LocalAppData}/node-gyp/Cache/18.12.0/include/node/", 24 | "${workspaceFolder}/node_modules/nan", 25 | "${workspaceFolder}/src", 26 | "${workspaceFolder}/**" 27 | ], 28 | "defines": [ 29 | "_DEBUG", 30 | "WINDOWS_BUILD", 31 | "UNICODE", 32 | "_UNICODE" 33 | ], 34 | "windowsSdkVersion": "10.0.17763.0", 35 | "compilerPath": "C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.34.31933/bin/Hostx64/x64/cl.exe", 36 | "cStandard": "c11", 37 | "cppStandard": "c++17", 38 | "intelliSenseMode": "msvc-x64" 39 | } 40 | ], 41 | "version": 4 42 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "array": "cpp", 4 | "atomic": "cpp", 5 | "bit": "cpp", 6 | "*.tcc": "cpp", 7 | "cctype": "cpp", 8 | "chrono": "cpp", 9 | "clocale": "cpp", 10 | "cmath": "cpp", 11 | "codecvt": "cpp", 12 | "condition_variable": "cpp", 13 | "cstdarg": "cpp", 14 | "cstddef": "cpp", 15 | "cstdint": "cpp", 16 | "cstdio": "cpp", 17 | "cstdlib": "cpp", 18 | "ctime": "cpp", 19 | "cwchar": "cpp", 20 | "cwctype": "cpp", 21 | "deque": "cpp", 22 | "map": "cpp", 23 | "set": "cpp", 24 | "unordered_map": "cpp", 25 | "vector": "cpp", 26 | "exception": "cpp", 27 | "algorithm": "cpp", 28 | "functional": "cpp", 29 | "iterator": "cpp", 30 | "memory": "cpp", 31 | "memory_resource": "cpp", 32 | "numeric": "cpp", 33 | "optional": "cpp", 34 | "random": "cpp", 35 | "ratio": "cpp", 36 | "string": "cpp", 37 | "string_view": "cpp", 38 | "system_error": "cpp", 39 | "tuple": "cpp", 40 | "type_traits": "cpp", 41 | "utility": "cpp", 42 | "fstream": "cpp", 43 | "initializer_list": "cpp", 44 | "iosfwd": "cpp", 45 | "istream": "cpp", 46 | "limits": "cpp", 47 | "mutex": "cpp", 48 | "new": "cpp", 49 | "ostream": "cpp", 50 | "sstream": "cpp", 51 | "stdexcept": "cpp", 52 | "streambuf": "cpp", 53 | "thread": "cpp", 54 | "typeinfo": "cpp", 55 | "iostream": "cpp", 56 | "locale": "cpp", 57 | "xlocale": "cpp", 58 | "xstring": "cpp", 59 | "xutility": "cpp", 60 | "list": "cpp", 61 | "queue": "cpp", 62 | "xhash": "cpp", 63 | "xtree": "cpp", 64 | "stop_token": "cpp", 65 | "xlocmon": "cpp", 66 | "xlocnum": "cpp", 67 | "charconv": "cpp", 68 | "compare": "cpp", 69 | "xlocinfo": "cpp", 70 | "xloctime": "cpp", 71 | "xmemory": "cpp", 72 | "xstddef": "cpp", 73 | "*.rh": "cpp", 74 | "cstring": "cpp", 75 | "ios": "cpp", 76 | "xthread": "cpp", 77 | "xfacet": "cpp", 78 | "xiosbase": "cpp", 79 | "xlocbuf": "cpp", 80 | "xlocmes": "cpp", 81 | "xmemory0": "cpp", 82 | "xtr1common": "cpp", 83 | "unordered_set": "cpp" 84 | }, 85 | "cmake.configureOnOpen": false 86 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "shell", 8 | "label": "g++ build make file", 9 | "command": "node-gyp -d build", 10 | "args": [], 11 | "problemMatcher": { 12 | "base": "$gcc", 13 | "fileLocation": [ 14 | "relative", 15 | "/home/stephen/dev/js/sql/node_modules/msnodesqlv8/src/", 16 | "/home/stephen/dev/js/sql/node_modules/msnodesqlv8/build/" 17 | ] 18 | }, 19 | "detail": "make", 20 | "group": "build" 21 | }, 22 | { 23 | "type": "npm", 24 | "script": "builddbg", 25 | "group": { 26 | "kind": "build", 27 | "isDefault": true 28 | }, 29 | "problemMatcher": [], 30 | "label": "npm: builddbg", 31 | "detail": "npm builddbg" 32 | }, 33 | { 34 | "type": "npm", 35 | "script": "prebuild-node", 36 | "group": "build", 37 | "problemMatcher": [], 38 | "label": "npm: prebuild-node", 39 | "detail": "prebuild -t 10.16.0 -t 11.15.0 -t 12.18.3 -t 13.14.0 -t 14.14.0 -t 15.11.0 -t 16.13.0 -t 17.0.1 -t 18.7.0 -t 19.2.0 --strip " 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | 2 | # Contributors Attribution 3 | 4 | Copyright Microsoft Corporation and contributors 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | 9 | You may obtain a copy of the License at: 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | This file contains all entities that have contributed to the 19 | development, testing, or documentation of this module. 20 | 21 | ## Contributors 22 | 23 | Microsoft Corp. 24 | Einar Otto Stangvik 25 | Dino M. Gambone 26 | Stephen James 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Microsoft Corporation and contributors 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you 4 | may not use this file except in compliance with the License. You may 5 | obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | 3 | image: 4 | - Visual Studio 2022 5 | - Ubuntu2004 6 | 7 | platform: 8 | - x64 9 | 10 | environment: 11 | matrix: 12 | - nodejs_version: 23 13 | - nodejs_version: 22 14 | 15 | test_script: 16 | - ps: Write-Host "msnodesqlv8 test script" 17 | for: 18 | - 19 | matrix: 20 | only: 21 | - image: Visual Studio 2022 22 | 23 | clone_folder: c:\projects\node_modules\msnodesqlv8 24 | 25 | before_test: 26 | - cmd: sc config sqlbrowser start= auto 27 | - cmd: net start sqlbrowser 28 | 29 | install: 30 | - ps: tool\install-driver.ps1 31 | - npm install 32 | 33 | build_script: 34 | - cmd: node --version 35 | - cmd: npm run build 36 | 37 | test_script: 38 | - ps: Get-OdbcDriver 39 | - cmd: tool\appveyor-win.cmd 40 | - 41 | matrix: 42 | only: 43 | - image: Ubuntu2004 44 | platform: x64 45 | 46 | services: 47 | - mssql 48 | 49 | clone_folder: /home/appveyor/projects/node_modules/msnodesqlv8 50 | 51 | build_script: 52 | - sh: node --version 53 | - sh: npm run build 54 | - sh: chmod 775 /home/appveyor/projects/node_modules/msnodesqlv8/tool/install_openssl.sh 55 | - sh: /home/appveyor/projects/node_modules/msnodesqlv8/tool/install_openssl.sh 56 | 57 | test_script: 58 | - sh: "sleep 4" 59 | - sh: "./node_modules/.bin/env-cmd -e appv-ubuntu ./node_modules/.bin/mocha 2>&1" 60 | 61 | install: 62 | - nvm install $nodejs_version 63 | - npm install 64 | -------------------------------------------------------------------------------- /docker/alpine-msnodesqlv8/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.21 2 | 3 | RUN apk update && \ 4 | apk add curl gcc libc-dev g++ libffi-dev libxml2 unixodbc unixodbc-dev openssl perl gnupg python3 git vim pingu nodejs make npm bash 5 | 6 | RUN curl -O https://download.microsoft.com/download/1/f/f/1fffb537-26ab-4947-a46a-7a45c27f6f77/msodbcsql18_18.2.1.1-1_amd64.apk 7 | RUN curl -O https://download.microsoft.com/download/1/f/f/1fffb537-26ab-4947-a46a-7a45c27f6f77/mssql-tools18_18.2.1.1-1_amd64.apk 8 | 9 | #(Optional) Verify signature, if 'gpg' is missing install it using 'apk add gnupg': 10 | 11 | RUN curl -O https://download.microsoft.com/download/1/f/f/1fffb537-26ab-4947-a46a-7a45c27f6f77/msodbcsql18_18.2.1.1-1_amd64.sig 12 | RUN curl -O https://download.microsoft.com/download/1/f/f/1fffb537-26ab-4947-a46a-7a45c27f6f77/mssql-tools18_18.2.1.1-1_amd64.sig 13 | 14 | RUN curl https://packages.microsoft.com/keys/microsoft.asc | gpg --import - 15 | RUN gpg --verify msodbcsql18_18.2.1.1-1_amd64.sig msodbcsql18_18.2.1.1-1_amd64.apk 16 | RUN gpg --verify mssql-tools18_18.2.1.1-1_amd64.sig mssql-tools18_18.2.1.1-1_amd64.apk 17 | 18 | #Install the package(s) 19 | 20 | RUN apk add --allow-untrusted msodbcsql18_18.2.1.1-1_amd64.apk 21 | RUN apk add --allow-untrusted mssql-tools18_18.2.1.1-1_amd64.apk 22 | 23 | RUN npm install --g yarn 24 | RUN npm install --g npm-check-updates 25 | RUN apk add doas; \ 26 | adduser apprunner; \ 27 | echo 'apprunner:123' | chpasswd; \ 28 | echo 'permit apprunner as root' > /etc/doas.d/doas.conf 29 | 30 | USER apprunner 31 | RUN (mkdir -p $HOME/app; cd $HOME/app; git clone https://github.com/TimelordUK/msnodesqlv8_yarn_sample.git; cd msnodesqlv8_yarn_sample; yarn install) 32 | RUN (mkdir -p $HOME/app/driver/node_modules; cd $HOME/app/driver/node_modules; git clone https://github.com/TimelordUK/node-sqlserver-v8.git msnodesqlv8; cd msnodesqlv8; npm install) 33 | -------------------------------------------------------------------------------- /docker/alpine-msnodesqlv8/README: -------------------------------------------------------------------------------- 1 | 2 | from this folder build image 3 | 4 | docker build . -t msnodesqlv8-alpine 5 | 6 | or with human readable log and clean rebuild 7 | 8 | docker build --no-cache --progress=plain . 2>&1 | tee build.log 9 | 10 | list the docker images 11 | 12 | docker images 13 | 14 | list these images only 15 | 16 | docker images msnodesql* 17 | 18 | REPOSITORY TAG IMAGE ID CREATED SIZE 19 | msnodesqlv8-alpine latest 94aa468ed1b3 21 hours ago 1.25GB 20 | msnodesqlv8-alpine latest c1eee90b2e4d 26 hours ago 2.42GB 21 | 22 | start an interactive shell in a new container 23 | 24 | docker run -it msnodesqlv8-alpine bash 25 | 26 | go to driver folder git repo 27 | 28 | cd ~/app/driver/node_modules/msnodesqlv8 29 | 30 | change the connection string in 31 | 32 | cat .env-cmdrc 33 | 34 | exit shell 35 | 36 | exit 37 | 38 | list containers 39 | 40 | docker container ls -a 41 | 42 | start a container 43 | 44 | docker start abc6f443950c 45 | 46 | connect interactive shell to the container 47 | 48 | docker exec -it upbeat_easley bash 49 | 50 | list all container running or stopped 51 | 52 | docker ps -a -------------------------------------------------------------------------------- /docker/debian-msnodesqlv8/README: -------------------------------------------------------------------------------- 1 | 2 | from this folder build image 3 | 4 | docker build . -t msnodesqlv8-debian 5 | 6 | or with human readable log and clean rebuild 7 | 8 | docker build --no-cache --progress=plain . 2>&1 | tee build.log 9 | 10 | list the docker images 11 | 12 | docker images 13 | 14 | list these images only 15 | 16 | docker images msnodesql* 17 | 18 | REPOSITORY TAG IMAGE ID CREATED SIZE 19 | msnodesqlv8-alpine latest 94aa468ed1b3 21 hours ago 1.25GB 20 | msnodesqlv8-debian latest c1eee90b2e4d 26 hours ago 2.42GB 21 | 22 | start an interactive shell in a new container 23 | 24 | docker run -it msnodesqlv8-debian bash 25 | 26 | go to driver folder git repo 27 | 28 | cd ~/app/driver/node_modules/msnodesqlv8 29 | 30 | change the connection string in 31 | 32 | cat .env-cmdrc 33 | 34 | exit shell 35 | 36 | exit 37 | 38 | list containers 39 | 40 | docker container ls -a 41 | 42 | start a container 43 | 44 | docker start abc6f443950c 45 | 46 | connect interactive shell to the container 47 | 48 | docker exec -it upbeat_easley bash 49 | 50 | list all container running or stopped 51 | 52 | docker ps -a -------------------------------------------------------------------------------- /docker/fedora-msnodesqlv8/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fedora:41 2 | 3 | RUN yum install -y sudo vim git wget ca-certificates openssl iputils 4 | 5 | RUN curl https://packages.microsoft.com/config/rhel/9/prod.repo | sudo tee /etc/yum.repos.d/mssql-release.repo 6 | 7 | RUN yum remove unixODBC-utf16 unixODBC-utf16-devel #to avoid conflicts 8 | RUN ACCEPT_EULA=Y yum install -y msodbcsql18 9 | # optional: for bcp and sqlcmd 10 | RUN ACCEPT_EULA=Y yum install -y mssql-tools18 11 | 12 | RUN yum install -y python3 13 | RUN ACCEPT_EULA=Y yum install -y nodejs npm 14 | RUN useradd -ms /bin/bash apprunner 15 | 16 | USER apprunner 17 | RUN unset nvm 18 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash 19 | RUN export NVM_DIR="$HOME/.nvm" && . $HOME/.nvm/nvm.sh; nvm install 22 20 | RUN . ${HOME}/.nvm/nvm.sh; npm install -g yarn 21 | # RUN . ${HOME}/.nvm/nvm.sh && nvm install 22 && npm install -g yarn && npm install -g npm-check-updates 22 | RUN . ${HOME}/.nvm/nvm.sh; (mkdir -p $HOME/app; cd $HOME/app; git clone https://github.com/TimelordUK/msnodesqlv8_yarn_sample.git; cd msnodesqlv8_yarn_sample; yarn install) 23 | RUN . ${HOME}/.nvm/nvm.sh; (mkdir -p $HOME/app/driver/node_modules; cd $HOME/app/driver/node_modules; git clone https://github.com/TimelordUK/node-sqlserver-v8.git msnodesqlv8; cd msnodesqlv8; npm install) 24 | 25 | # RUN npm install --global yarn 26 | # USER apprunner 27 | # RUN (mkdir -p $HOME/app; cd $HOME/app; git clone https://github.com/TimelordUK/msnodesqlv8_yarn_sample.git; cd msnodesqlv8_yarn_sample; yarn install) 28 | # RUN (mkdir -p $HOME/app/driver/node_modules; cd $HOME/app/driver/node_modules; odbcinst -j; git clone https://github.com/TimelordUK/node-sqlserver-v8.git msnodesqlv8; cd msnodesqlv8; npm install) 29 | -------------------------------------------------------------------------------- /docker/fedora-msnodesqlv8/README: -------------------------------------------------------------------------------- 1 | 2 | from this folder build image 3 | 4 | docker build . -t msnodesqlv8-fedora 5 | 6 | or with human readable log and clean rebuild 7 | 8 | docker build --no-cache --progress=plain . 2>&1 | tee build.log 9 | 10 | list the docker images 11 | 12 | docker images 13 | 14 | list these images only 15 | 16 | docker images msnodesql* 17 | 18 | REPOSITORY TAG IMAGE ID CREATED SIZE 19 | msnodesqlv8-alpine latest 94aa468ed1b3 21 hours ago 1.25GB 20 | msnodesqlv8-fedora latest c1eee90b2e4d 26 hours ago 2.42GB 21 | 22 | start an interactive shell in a new container 23 | 24 | docker run -it msnodesqlv8-fedora bash 25 | 26 | go to driver folder git repo 27 | 28 | cd ~/app/driver/node_modules/msnodesqlv8 29 | 30 | change the connection string in 31 | 32 | cat .env-cmdrc 33 | 34 | exit shell 35 | 36 | exit 37 | 38 | list containers 39 | 40 | docker container ls -a 41 | 42 | start a container 43 | 44 | docker start abc6f443950c 45 | 46 | connect interactive shell to the container 47 | 48 | docker exec -it upbeat_easley bash 49 | 50 | list all container running or stopped 51 | 52 | docker ps -a -------------------------------------------------------------------------------- /docker/rocky-msnodesqlv8/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rockylinux:9 2 | 3 | RUN ACCEPT_EULA=Y \ 4 | yum install -y sudo vim git wget perl-core perl-IPC-Cmd dialog ca-certificates gnupg2 openssl 5 | 6 | RUN curl https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/mssql-release.repo 7 | 8 | RUN yum remove unixODBC-utf16 unixODBC-utf16-devel 9 | 10 | RUN yum install -y unixODBC-devel 11 | 12 | RUN yum install -y gmp-devel mpfr-devel libmpc-devel wget 13 | 14 | RUN ACCEPT_EULA=Y \ 15 | yum install -y python3 unixODBC unixODBC-devel 16 | 17 | RUN ACCEPT_EULA=Y \ 18 | yum install -y msodbcsql18 19 | RUN ACCEPT_EULA=Y \ 20 | yum install -y mssql-tools18 21 | 22 | RUN curl -sL https://rpm.nodesource.com/setup_20.x | bash - 23 | RUN ACCEPT_EULA=Y \ 24 | yum install -y nodejs iputils 25 | RUN npm install --g yarn 26 | RUN npm install --g npm-check-updates 27 | RUN useradd -ms /bin/bash apprunner 28 | ENV LD_LIBRARY_PATH /usr/local/lib64 29 | RUN (export LD_LIBRARY_PATH=/usr/local/lib64:${LD_LIBRARY_PATH};) 30 | RUN mkdir -p $HOME/projects 31 | 32 | USER apprunner 33 | RUN unset nvm 34 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash 35 | RUN export NVM_DIR="$HOME/.nvm" && . $HOME/.nvm/nvm.sh; nvm install 22 36 | RUN . ${HOME}/.nvm/nvm.sh; npm install -g yarn 37 | # RUN . ${HOME}/.nvm/nvm.sh && nvm install 22 && npm install -g yarn && npm install -g npm-check-updates 38 | RUN . ${HOME}/.nvm/nvm.sh; (mkdir -p $HOME/app; cd $HOME/app; git clone https://github.com/TimelordUK/msnodesqlv8_yarn_sample.git; cd msnodesqlv8_yarn_sample; yarn install) 39 | RUN . ${HOME}/.nvm/nvm.sh; (mkdir -p $HOME/app/driver/node_modules; cd $HOME/app/driver/node_modules; git clone https://github.com/TimelordUK/node-sqlserver-v8.git msnodesqlv8; cd msnodesqlv8; npm install) 40 | 41 | -------------------------------------------------------------------------------- /docker/rocky-msnodesqlv8/README: -------------------------------------------------------------------------------- 1 | 2 | from this folder build image 3 | 4 | docker build . -t msnodesqlv8-centos 5 | 6 | or with human readable log and clean rebuild 7 | 8 | docker build --no-cache --progress=plain . 2>&1 | tee build.log 9 | 10 | list the docker images 11 | 12 | docker images 13 | 14 | list these images only 15 | 16 | docker images msnodesql* 17 | 18 | REPOSITORY TAG IMAGE ID CREATED SIZE 19 | msnodesqlv8-alpine latest 94aa468ed1b3 21 hours ago 1.25GB 20 | msnodesqlv8-centos latest c1eee90b2e4d 26 hours ago 2.42GB 21 | 22 | start an interactive shell in a new container 23 | 24 | docker run -it msnodesqlv8-centos bash 25 | 26 | go to driver folder git repo 27 | 28 | cd ~/app/driver/node_modules/msnodesqlv8 29 | 30 | change the connection string in 31 | 32 | cat .env-cmdrc 33 | 34 | exit shell 35 | 36 | exit 37 | 38 | list containers 39 | 40 | docker container ls -a 41 | 42 | start a container 43 | 44 | docker start abc6f443950c 45 | 46 | connect interactive shell to the container 47 | 48 | docker exec -it upbeat_easley bash 49 | 50 | list all container running or stopped 51 | 52 | docker ps -a -------------------------------------------------------------------------------- /docker/ubuntu-msnodesqlv8/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | RUN echo 'APT::Install-Suggests "0";' >> /etc/apt/apt.conf.d/00-docker 3 | RUN echo 'APT::Install-Recommends "0";' >> /etc/apt/apt.conf.d/00-docker 4 | 5 | RUN DEBIAN_FRONTEND=noninteractive \ 6 | apt-get update \ 7 | && apt-get install -y vim git build-essential curl perl dialog apt-utils ca-certificates apt-transport-https locales gnupg2 openssl lsb-release 8 | 9 | RUN curl https://packages.microsoft.com/keys/microsoft.asc | tee | apt-key add - 10 | RUN curl https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/prod.list > /etc/apt/sources.list.d/mssql-release.list 11 | RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - 12 | RUN echo msodbcsql18 msodbcsql/ACCEPT_EULA boolean true | debconf-set-selections 13 | 14 | RUN DEBIAN_FRONTEND=noninteractive \ 15 | apt-get update \ 16 | && apt-get install -y python3 unixodbc unixodbc-dev 17 | RUN ACCEPT_EULA=Y \ 18 | apt-get install -y msodbcsql18 19 | RUN ACCEPT_EULA=Y \ 20 | apt-get install -y mssql-tools18 21 | 22 | RUN mkdir -p $HOME/projects 23 | RUN git clone https://github.com/openssl/openssl.git --depth 1 --branch openssl-3.1.1 $HOME/projects/openssl 24 | RUN ( cd $HOME/projects/openssl ; ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared; make; make install) 25 | RUN (cd /etc/ld.so.conf.d/; echo '/usr/local/ssl/lib64/' | tee openssl-3.2.0.1s.conf;) 26 | RUN (cd /usr/local/ssl; mv certs certs.old; ln -s /etc/ssl/certs certs) 27 | RUN ldconfig -v 28 | RUN DEBIAN_FRONTEND=noninteractive \ 29 | apt-get update \ 30 | && apt-get install -y iputils-ping curl 31 | RUN useradd -ms /bin/bash apprunner 32 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash 33 | RUN . ${HOME}/.nvm/nvm.sh && nvm install 22 && npm install -g yarn && npm install -g npm-check-updates && chmod -R 755 ${HOME} 34 | USER apprunner 35 | RUN unset nvm 36 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash 37 | RUN export NVM_DIR="$HOME/.nvm" && . $HOME/.nvm/nvm.sh; nvm install 22 38 | # RUN . ${HOME}/.nvm/nvm.sh && nvm install 22 && npm install -g yarn && npm install -g npm-check-updates 39 | RUN . ${HOME}/.nvm/nvm.sh; (mkdir -p $HOME/app; cd $HOME/app; git clone https://github.com/TimelordUK/msnodesqlv8_yarn_sample.git; cd msnodesqlv8_yarn_sample; yarn install) 40 | RUN . ${HOME}/.nvm/nvm.sh; (mkdir -p $HOME/app/driver/node_modules; cd $HOME/app/driver/node_modules; git clone https://github.com/TimelordUK/node-sqlserver-v8.git msnodesqlv8; cd msnodesqlv8; npm install) 41 | -------------------------------------------------------------------------------- /docker/ubuntu-msnodesqlv8/README: -------------------------------------------------------------------------------- 1 | 2 | from this folder build image 3 | 4 | docker build . -t msnodesqlv8-ubuntu 5 | 6 | or with human readable log and clean rebuild 7 | 8 | docker build --no-cache --progress=plain . 2>&1 | tee build.log 9 | 10 | list the docker images 11 | 12 | docker images 13 | 14 | list these images only 15 | 16 | docker images msnodesql* 17 | 18 | REPOSITORY TAG IMAGE ID CREATED SIZE 19 | msnodesqlv8-alpine latest 94aa468ed1b3 21 hours ago 1.25GB 20 | msnodesqlv8-ubuntu latest c1eee90b2e4d 26 hours ago 2.42GB 21 | 22 | start an interactive shell in a new container 23 | 24 | docker run -it msnodesqlv8-ubuntu bash 25 | 26 | go to driver folder git repo 27 | 28 | cd ~/app/driver/node_modules/msnodesqlv8 29 | 30 | change the connection string in 31 | 32 | cat .env-cmdrc 33 | 34 | exit shell 35 | 36 | exit 37 | 38 | list containers 39 | 40 | docker container ls -a 41 | 42 | start a container 43 | 44 | docker start abc6f443950c 45 | 46 | connect interactive shell to the container 47 | 48 | docker exec -it upbeat_easley bash 49 | 50 | list all container running or stopped 51 | 52 | docker ps -a -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import tseslint from "typescript-eslint"; 4 | 5 | 6 | /** @type {import('eslint').Linter.Config[]} */ 7 | export default [ 8 | {files: ["**/*.{js,mjs,cjs,ts}"]}, 9 | {languageOptions: { globals: globals.browser }}, 10 | pluginJs.configs.recommended, 11 | ...tseslint.configs.recommended, 12 | ]; -------------------------------------------------------------------------------- /lib/base-promises.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class BasePromises { 4 | async op (f) { 5 | return new Promise((resolve, reject) => { 6 | setImmediate(() => { 7 | try { 8 | f((err, v) => { 9 | if (err) { 10 | setImmediate(() => { reject(err) }) 11 | } else { 12 | setImmediate(() => { resolve(v) }) 13 | } 14 | }) 15 | } catch (e) { 16 | setImmediate(() => { reject(e) }) 17 | } 18 | }) 19 | }) 20 | } 21 | } 22 | exports.BasePromises = BasePromises 23 | -------------------------------------------------------------------------------- /lib/data-struture/heap/MaxHeap.js: -------------------------------------------------------------------------------- 1 | const heap = require('./Heap') 2 | 3 | class MaxHeap extends heap.Heap { 4 | /** 5 | * Checks if pair of heap elements is in correct order. 6 | * For MinHeap the first element must be always smaller or equal. 7 | * For MaxHeap the first element must be always bigger or equal. 8 | * 9 | * @param {*} firstElement 10 | * @param {*} secondElement 11 | * @return {boolean} 12 | */ 13 | pairIsInCorrectOrder (firstElement, secondElement) { 14 | return this.compare.greaterThanOrEqual(firstElement, secondElement) 15 | } 16 | } 17 | 18 | exports.MaxHeap = MaxHeap 19 | -------------------------------------------------------------------------------- /lib/data-struture/heap/MinHeap.js: -------------------------------------------------------------------------------- 1 | const heap = require('./Heap') 2 | 3 | class MinHeap extends heap.Heap { 4 | /** 5 | * Checks if pair of heap elements is in correct order. 6 | * For MinHeap the first element must be always smaller or equal. 7 | * For MaxHeap the first element must be always bigger or equal. 8 | * 9 | * @param {*} firstElement 10 | * @param {*} secondElement 11 | * @return {boolean} 12 | */ 13 | pairIsInCorrectOrder (firstElement, secondElement) { 14 | return this.compare.lessThanOrEqual(firstElement, secondElement) 15 | } 16 | } 17 | 18 | exports.MinHeap = MinHeap 19 | -------------------------------------------------------------------------------- /lib/data-struture/priority-queue/PriorityQueue.js: -------------------------------------------------------------------------------- 1 | const minHeap = require('../heap/MinHeap') 2 | const comparator = require('../utils/comparator/Comparator') 3 | 4 | // It is the same as min heap except that when comparing two elements 5 | // we take into account its priority instead of the element's value. 6 | class PriorityQueue extends minHeap.MinHeap { 7 | constructor () { 8 | // Call MinHip constructor first. 9 | super() 10 | 11 | // Setup priorities map. 12 | this.priorities = new Map() 13 | 14 | // Use custom comparator for heap elements that will take element priority 15 | // instead of element value into account. 16 | this.compare = new comparator.Comparator(this.comparePriority.bind(this)) 17 | } 18 | 19 | /** 20 | * Add item to the priority queue. 21 | * @param {*} item - item we're going to add to the queue. 22 | * @param {number} [priority] - items priority. 23 | * @return {PriorityQueue} 24 | */ 25 | add (item, priority = 0) { 26 | this.priorities.set(item, priority) 27 | super.add(item) 28 | return this 29 | } 30 | 31 | /** 32 | * Remove item from priority queue. 33 | * @param {*} item - item we're going to remove. 34 | * @param {Comparator} [customFindingComparator] - custom function for finding the item to remove 35 | * @return {PriorityQueue} 36 | */ 37 | remove (item, customFindingComparator) { 38 | super.remove(item, customFindingComparator) 39 | this.priorities.delete(item) 40 | return this 41 | } 42 | 43 | /** 44 | * Change priority of the item in a queue. 45 | * @param {*} item - item we're going to re-prioritize. 46 | * @param {number} priority - new item's priority. 47 | * @return {PriorityQueue} 48 | */ 49 | changePriority (item, priority) { 50 | this.remove(item, new comparator.Comparator(this.compareValue)) 51 | this.add(item, priority) 52 | return this 53 | } 54 | 55 | /** 56 | * Find item by ite value. 57 | * @param {*} item 58 | * @return {Number[]} 59 | */ 60 | findByValue (item) { 61 | return this.find(item, new comparator.Comparator(this.compareValue)) 62 | } 63 | 64 | /** 65 | * Check if item already exists in a queue. 66 | * @param {*} item 67 | * @return {boolean} 68 | */ 69 | hasValue (item) { 70 | return this.findByValue(item).length > 0 71 | } 72 | 73 | /** 74 | * Compares priorities of two items. 75 | * @param {*} a 76 | * @param {*} b 77 | * @return {number} 78 | */ 79 | comparePriority (a, b) { 80 | if (this.priorities.get(a) === this.priorities.get(b)) { 81 | return 0 82 | } 83 | return this.priorities.get(a) < this.priorities.get(b) ? -1 : 1 84 | } 85 | 86 | /** 87 | * Compares values of two items. 88 | * @param {*} a 89 | * @param {*} b 90 | * @return {number} 91 | */ 92 | compareValue (a, b) { 93 | if (a === b) { 94 | return 0 95 | } 96 | return a < b ? -1 : 1 97 | } 98 | } 99 | 100 | exports.PriorityQueue = PriorityQueue 101 | -------------------------------------------------------------------------------- /lib/data-struture/utils/comparator/Comparator.js: -------------------------------------------------------------------------------- 1 | class Comparator { 2 | /** 3 | * @param {function(a: *, b: *)} [compareFunction] - It may be custom compare function that, let's 4 | * say may compare custom objects together. 5 | */ 6 | constructor (compareFunction) { 7 | this.compare = compareFunction || Comparator.defaultCompareFunction 8 | } 9 | 10 | /** 11 | * Default comparison function. It just assumes that "a" and "b" are strings or numbers. 12 | * @param {(string|number)} a 13 | * @param {(string|number)} b 14 | * @returns {number} 15 | */ 16 | static defaultCompareFunction (a, b) { 17 | if (a === b) { 18 | return 0 19 | } 20 | 21 | return a < b ? -1 : 1 22 | } 23 | 24 | /** 25 | * Checks if two variables are equal. 26 | * @param {*} a 27 | * @param {*} b 28 | * @return {boolean} 29 | */ 30 | equal (a, b) { 31 | return this.compare(a, b) === 0 32 | } 33 | 34 | /** 35 | * Checks if variable "a" is less than "b". 36 | * @param {*} a 37 | * @param {*} b 38 | * @return {boolean} 39 | */ 40 | lessThan (a, b) { 41 | return this.compare(a, b) < 0 42 | } 43 | 44 | /** 45 | * Checks if variable "a" is greater than "b". 46 | * @param {*} a 47 | * @param {*} b 48 | * @return {boolean} 49 | */ 50 | greaterThan (a, b) { 51 | return this.compare(a, b) > 0 52 | } 53 | 54 | /** 55 | * Checks if variable "a" is less than or equal to "b". 56 | * @param {*} a 57 | * @param {*} b 58 | * @return {boolean} 59 | */ 60 | lessThanOrEqual (a, b) { 61 | return this.lessThan(a, b) || this.equal(a, b) 62 | } 63 | 64 | /** 65 | * Checks if variable "a" is greater than or equal to "b". 66 | * @param {*} a 67 | * @param {*} b 68 | * @return {boolean} 69 | */ 70 | greaterThanOrEqual (a, b) { 71 | return this.greaterThan(a, b) || this.equal(a, b) 72 | } 73 | 74 | /** 75 | * Reverses the comparison order. 76 | */ 77 | reverse () { 78 | const compareOriginal = this.compare 79 | this.compare = (a, b) => compareOriginal(b, a) 80 | } 81 | } 82 | 83 | exports.Comparator = Comparator 84 | -------------------------------------------------------------------------------- /lib/dialect.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | class ServerDialect { 3 | constructor (name) { 4 | this.name = name 5 | } 6 | 7 | static Sybase = new ServerDialect('Sybase') 8 | static SqlServer = new ServerDialect('SqlServer') 9 | } 10 | 11 | module.exports = { 12 | ServerDialect 13 | } 14 | -------------------------------------------------------------------------------- /lib/prepared-statement.js: -------------------------------------------------------------------------------- 1 | const { utilModule } = require('./util') 2 | const { BasePromises } = require('./base-promises') 3 | 4 | class PreparedStatementPromises extends BasePromises { 5 | async query (params, options) { 6 | const q = this.prepared.preparedQuery(params) 7 | return this.aggregator.queryPrepared(q, options) 8 | } 9 | 10 | async free () { 11 | return this.op(cb => this.prepared.free(cb)) 12 | } 13 | 14 | constructor (c, ps) { 15 | super() 16 | this.aggregator = new utilModule.QueryAggregator(c) 17 | this.connection = c 18 | this.prepared = ps 19 | } 20 | } 21 | 22 | class PreparedStatement { 23 | constructor (notifier, driverMgr, preparedSignature, connection, preparedNotifier, preparedMeta) { 24 | this.notifier = notifier 25 | this.driverMgr = driverMgr 26 | this.connection = connection 27 | this.meta = preparedMeta 28 | this.notify = preparedNotifier 29 | this.active = true 30 | this.signature = preparedSignature 31 | this.promises = new PreparedStatementPromises(connection, this) 32 | } 33 | 34 | getMeta () { 35 | return this.meta 36 | } 37 | 38 | getSignature () { 39 | return this.signature 40 | } 41 | 42 | getId () { 43 | return this.notify.getQueryId() 44 | } 45 | 46 | preparedQuery (paramsOrCallback, callback) { 47 | if (!this.active) { 48 | if (callback) { 49 | callback(new Error('error; prepared statement has been Debugd.')) 50 | } 51 | } 52 | const chunky = this.notifier.getChunkyArgs(paramsOrCallback, callback) 53 | 54 | const onPreparedQuery = (err, results, more) => { 55 | if (chunky.callback) { 56 | if (err) { 57 | chunky.callback(err) 58 | } else { 59 | chunky.callback(err, this.driverMgr.objectify(results), more) 60 | } 61 | } 62 | } 63 | 64 | if (chunky.callback) { 65 | this.driverMgr.readAllPrepared(this.notify, {}, chunky.params, onPreparedQuery) 66 | } else { 67 | this.driverMgr.readAllPrepared(this.notify, {}, chunky.params) 68 | } 69 | 70 | return this.notify 71 | } 72 | 73 | free (callback) { 74 | this.driverMgr.freeStatement(this.notify, err => { 75 | this.active = false 76 | if (callback) { 77 | callback(err, null) 78 | } 79 | }) 80 | } 81 | } 82 | 83 | exports.PreparedStatement = PreparedStatement 84 | -------------------------------------------------------------------------------- /lib/queries/proc_describe.sql: -------------------------------------------------------------------------------- 1 | with proc_exist_cte(object_id, proc_name, type_desc) as 2 | ( 3 | SELECT object_id, name as proc_name, type_desc 4 | FROM sys.objects WHERE type = 'P' AND object_id = object_id('.') 5 | ), 6 | proc_params_cte(object_id, 7 | has_default_value, 8 | default_value, 9 | is_output, 10 | [name], 11 | type_id, 12 | max_length, 13 | [precision], 14 | [scale], 15 | [order], 16 | collation, 17 | is_user_defined) as 18 | ( 19 | select 20 | object_id, 21 | has_default_value, 22 | default_value, 23 | is_output, 24 | sp.name, 25 | type_id = type_name(sp.user_type_id), 26 | sp.max_length, 27 | sp.precision, 28 | sp.scale, 29 | 'order' = parameter_id, 30 | 'collation' = convert(sysname, 31 | case 32 | when sp.system_type_id in (35, 99, 167, 175, 231, 239) 33 | then ServerProperty('collation') end), 34 | ty.is_user_defined 35 | from 36 | sys.parameters sp 37 | left outer join sys.table_types ty 38 | on ty.name=type_name(sp.user_type_id) 39 | and ty.schema_id = schema_id('') 40 | where 41 | object_id = object_id('.') 42 | ) select 43 | r.proc_name, r.type_desc, 44 | p.* 45 | from proc_exist_cte r 46 | outer apply 47 | proc_params_cte p 48 | -------------------------------------------------------------------------------- /lib/queries/server_version.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | CASE 3 | WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '8%' THEN 2000 4 | WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '9%' THEN 2005 5 | WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '10.0%' THEN 2008 6 | WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '10.5%' THEN 2008.2 7 | WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '11%' THEN 2012 8 | WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '12%' THEN 2014 9 | WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '13%' THEN 2016 10 | WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '14%' THEN 2017 11 | WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '15%' THEN 2019 12 | WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '16%' THEN 2022 13 | ELSE 0 14 | END AS MajorVersion, 15 | SERVERPROPERTY('ProductLevel') AS ProductLevel, 16 | SERVERPROPERTY('Edition') AS Edition, 17 | SERVERPROPERTY('ProductVersion') AS ProductVersion, 18 | DB_NAME() as Cat -------------------------------------------------------------------------------- /lib/queries/table_describe.2014.sql: -------------------------------------------------------------------------------- 1 | WITH t_name_cte(id, full_name, table_name) AS 2 | (SELECT TOP (1) 3 | 0 AS id, 4 | TABLE_CATALOG + '..' + TABLE_NAME AS full_name, 5 | TABLE_NAME 6 | FROM 7 | .INFORMATION_SCHEMA.COLUMNS sc 8 | inner join .sys.objects so 9 | on so.name = sc.TABLE_NAME 10 | WHERE object_id = OBJECT_ID('..') 11 | ), 12 | primary_keys AS 13 | (SELECT COLUMN_NAME, 14 | TABLE_NAME, 15 | 1 AS is_primary_key 16 | FROM 17 | .INFORMATION_SCHEMA.KEY_COLUMN_USAGE sk 18 | join .sys.objects so 19 | on sk.CONSTRAINT_NAME = so.name 20 | WHERE so.type = 'PK'), 21 | foreign_keys AS 22 | (SELECT COLUMN_NAME, 23 | TABLE_NAME, 24 | 1 AS is_foreign_key 25 | FROM .INFORMATION_SCHEMA.KEY_COLUMN_USAGE sk 26 | join .sys.objects so 27 | on sk.CONSTRAINT_NAME = so.name 28 | WHERE so.type = 'F') 29 | SELECT 30 | distinct 31 | sc.ordinal_position, 32 | sc.table_catalog, 33 | sc.table_schema, 34 | sc.table_name, 35 | sc.column_default, 36 | c.name name, 37 | t.Name type, 38 | c.max_length, 39 | c.precision, 40 | c.scale, 41 | c.is_nullable, 42 | c.is_computed, 43 | c.is_identity, 44 | c.object_id, 45 | 0 as generated_always_type, 46 | 'NOT_APPLICABLE' as generated_always_type_desc, 47 | 0 as is_hidden, 48 | COALESCE(pk.is_primary_key, 0) AS is_primary_key, 49 | COALESCE(fk.is_foreign_key, 0) AS is_foreign_key 50 | FROM .INFORMATION_SCHEMA.TABLES st 51 | INNER JOIN .INFORMATION_SCHEMA.COLUMNS sc 52 | ON sc.TABLE_CATALOG = st.TABLE_CATALOG 53 | AND sc.TABLE_SCHEMA = st.TABLE_SCHEMA 54 | AND sc.TABLE_NAME = st.TABLE_NAME 55 | LEFT JOIN .INFORMATION_SCHEMA.KEY_COLUMN_USAGE u 56 | ON sc.TABLE_CATALOG = u.TABLE_CATALOG 57 | AND sc.TABLE_SCHEMA = u.TABLE_SCHEMA 58 | AND sc.TABLE_NAME = u.TABLE_NAME 59 | AND sc.COLUMN_NAME = u.COLUMN_NAME 60 | LEFT JOIN primary_keys pk 61 | ON sc.COLUMN_NAME = pk.COLUMN_NAME 62 | AND sc.TABLE_NAME = pk.TABLE_NAME 63 | LEFT JOIN foreign_keys fk 64 | ON sc.COLUMN_NAME = fk.COLUMN_NAME 65 | AND sc.TABLE_NAME = fk.TABLE_NAME 66 | cross join t_name_cte r 67 | INNER JOIN 68 | .sys.columns c ON c.name = sc.column_name 69 | INNER JOIN 70 | .sys.types t ON c.user_type_id = t.user_type_id 71 | INNER JOIN 72 | .sys.tables ta ON ta.name = r.table_name 73 | LEFT OUTER JOIN 74 | .sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id 75 | LEFT OUTER JOIN 76 | .sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id 77 | WHERE 78 | c.object_id = ta.object_id 79 | AND TABLE_TYPE = 'BASE TABLE' 80 | AND sc.TABLE_NAME = r.table_name 81 | AND (sc.TABLE_SCHEMA = '' or '' = '') 82 | AND (ta.schema_id = SCHEMA_ID('') or '' = '') 83 | 84 | -------------------------------------------------------------------------------- /lib/queries/table_describe.sql: -------------------------------------------------------------------------------- 1 | WITH t_name_cte(id, full_name, table_name) AS 2 | (SELECT TOP (1) 3 | 0 AS id, 4 | TABLE_CATALOG + '..' + TABLE_NAME AS full_name, 5 | TABLE_NAME 6 | FROM 7 | .INFORMATION_SCHEMA.COLUMNS sc 8 | inner join .sys.objects so 9 | on so.name = sc.TABLE_NAME 10 | WHERE object_id = OBJECT_ID('..') 11 | ), 12 | primary_keys AS 13 | (SELECT COLUMN_NAME, 14 | TABLE_NAME, 15 | 1 AS is_primary_key 16 | FROM 17 | .INFORMATION_SCHEMA.KEY_COLUMN_USAGE sk 18 | join .sys.objects so 19 | on sk.CONSTRAINT_NAME = so.name 20 | WHERE so.type = 'PK'), 21 | foreign_keys AS 22 | (SELECT COLUMN_NAME, 23 | TABLE_NAME, 24 | 1 AS is_foreign_key 25 | FROM .INFORMATION_SCHEMA.KEY_COLUMN_USAGE sk 26 | join .sys.objects so 27 | on sk.CONSTRAINT_NAME = so.name 28 | WHERE so.type = 'F') 29 | SELECT 30 | distinct 31 | sc.ordinal_position, 32 | sc.table_catalog, 33 | sc.table_schema, 34 | sc.table_name, 35 | sc.column_default, 36 | c.name name, 37 | t.Name type, 38 | c.max_length, 39 | c.precision, 40 | c.scale, 41 | c.is_nullable, 42 | c.is_computed, 43 | c.is_identity, 44 | c.object_id, 45 | c.generated_always_type, 46 | c.generated_always_type_desc, 47 | c.is_hidden, 48 | COALESCE(pk.is_primary_key, 0) AS is_primary_key, 49 | COALESCE(fk.is_foreign_key, 0) AS is_foreign_key 50 | FROM .INFORMATION_SCHEMA.TABLES st 51 | INNER JOIN .INFORMATION_SCHEMA.COLUMNS sc 52 | ON sc.TABLE_CATALOG = st.TABLE_CATALOG 53 | AND sc.TABLE_SCHEMA = st.TABLE_SCHEMA 54 | AND sc.TABLE_NAME = st.TABLE_NAME 55 | LEFT JOIN .INFORMATION_SCHEMA.KEY_COLUMN_USAGE u 56 | ON sc.TABLE_CATALOG = u.TABLE_CATALOG 57 | AND sc.TABLE_SCHEMA = u.TABLE_SCHEMA 58 | AND sc.TABLE_NAME = u.TABLE_NAME 59 | AND sc.COLUMN_NAME = u.COLUMN_NAME 60 | LEFT JOIN primary_keys pk 61 | ON sc.COLUMN_NAME = pk.COLUMN_NAME 62 | AND sc.TABLE_NAME = pk.TABLE_NAME 63 | LEFT JOIN foreign_keys fk 64 | ON sc.COLUMN_NAME = fk.COLUMN_NAME 65 | AND sc.TABLE_NAME = fk.TABLE_NAME 66 | cross join t_name_cte r 67 | INNER JOIN 68 | .sys.columns c ON c.name = sc.column_name 69 | INNER JOIN 70 | .sys.types t ON c.user_type_id = t.user_type_id 71 | INNER JOIN 72 | .sys.tables ta ON ta.name = r.table_name 73 | LEFT OUTER JOIN 74 | .sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id 75 | LEFT OUTER JOIN 76 | .sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id 77 | WHERE 78 | c.object_id = ta.object_id 79 | AND TABLE_TYPE = 'BASE TABLE' 80 | AND sc.TABLE_NAME = r.table_name 81 | AND (sc.TABLE_SCHEMA = '' or '' = '') 82 | AND (ta.schema_id = SCHEMA_ID('') or '' = '') 83 | 84 | -------------------------------------------------------------------------------- /lib/queries/user_type.sql: -------------------------------------------------------------------------------- 1 | SELECT SCHEMA_NAME(type_t.schema_id) + '.' + type_t.name AS type_name, 2 | SCHEMA_NAME(type_t.schema_id) as schema_name, 3 | type_t.name as type_id, 4 | col.column_id, 5 | SUBSTRING(CAST(col.column_id + 100 AS char(3)), 2, 2) + ': ' + col.name AS ordered_column, 6 | col.name AS column_name, 7 | col.name AS name, 8 | ST.name AS data_type, 9 | ST.name AS type_id, 10 | ST.name AS declaration, 11 | (CASE col.Is_Nullable 12 | WHEN 1 THEN '' 13 | ELSE 'NOT NULL' 14 | END) AS nullable, 15 | col.max_length AS length, 16 | col.[precision] AS precision, 17 | col.scale AS scale, 18 | ST.collation AS collation, 19 | 0 As is_output, 20 | col.system_type_id, 21 | col.column_id 22 | 23 | FROM sys.table_types type_t 24 | JOIN sys.columns col 25 | ON type_t.type_table_object_id = col.object_id 26 | JOIN sys.systypes AS ST 27 | ON ST.xtype = col.system_type_id and st.xusertype=col.user_type_id 28 | where 29 | type_t.is_user_defined = 1 30 | and type_t.schema_id = schema_id('') 31 | and type_t.name = '' 32 | 33 | ORDER BY type_name, 34 | col.column_id 35 | -------------------------------------------------------------------------------- /lib/query-handler.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class QueryHandler { 4 | constructor (cppDriver) { 5 | this.cppDriver = cppDriver 6 | } 7 | 8 | begin (queryId, query, params, callback) { } 9 | end (queryId, outputParams, callback, results, more) { } 10 | } 11 | 12 | class NativePreparedQueryHandler extends QueryHandler { 13 | begin (queryId, query, params, callback) { 14 | this.cppDriver.bindQuery(queryId, params, (err, meta) => { 15 | if (callback) { 16 | callback(err, meta) 17 | } 18 | }) 19 | } 20 | 21 | end (queryId, outputParams, callback, results, more) { 22 | if (callback) { 23 | callback(null, results, more, outputParams) 24 | } 25 | } 26 | } 27 | 28 | class NativeQueryHandler extends QueryHandler { 29 | constructor (cppDriver, onStatementComplete) { 30 | super(cppDriver) 31 | this.onStatementCompleteHandler = onStatementComplete 32 | } 33 | 34 | begin (queryId, query, params, callback) { 35 | this.cppDriver.query(queryId, query, params, (err, results, more) => { 36 | if (callback) { 37 | callback(err, results, more) 38 | } 39 | }) 40 | } 41 | 42 | end (not, outputParams, callback, results, endMore) { 43 | this.onStatementCompleteHandler.onStatementComplete(not, outputParams, callback, results, endMore) 44 | } 45 | } 46 | 47 | class NativeProcedureQueryHandler extends QueryHandler { 48 | constructor (cppDriver, onStatementComplete, workQueue, unbindEnum) { 49 | super(cppDriver) 50 | this.onStatementCompleteHandler = onStatementComplete 51 | this.workQueue = workQueue 52 | this.unbindEnum = unbindEnum 53 | } 54 | 55 | begin (queryId, procedure, params, callback) { 56 | this.cppDriver.callProcedure(queryId, procedure, params, (err, results, params) => { 57 | if (callback) { 58 | callback(err, results, params) 59 | } 60 | }) 61 | } 62 | 63 | // for a stored procedure with multiple statements, only unbind after all 64 | // statements are completed 65 | 66 | unbind (more, not, qid, results, callback) { 67 | this.cppDriver.unbind(qid, (err, outputVector) => { 68 | if (err && callback) { 69 | callback(err, results) 70 | } 71 | not.emit('output', outputVector) 72 | this.onStatementCompleteHandler.onStatementComplete(not, outputVector, callback, results, more) 73 | if (!more) { 74 | this.workQueue.nextOp() 75 | } 76 | }) 77 | } 78 | 79 | end (not, outputParams, callback, results, endMore) { 80 | if (!endMore) { 81 | const qid = not.getQueryId() 82 | this.workQueue.enqueue(this.unbindEnum, (a) => 83 | setImmediate(() => { this.unbind(a, not, qid, results, callback) }), [endMore]) 84 | } else { 85 | this.onStatementCompleteHandler.onStatementComplete(not, null, callback, results, endMore) 86 | } 87 | } 88 | } 89 | 90 | exports.NativePreparedQueryHandler = NativePreparedQueryHandler 91 | exports.NativeQueryHandler = NativeQueryHandler 92 | exports.NativeProcedureQueryHandler = NativeProcedureQueryHandler 93 | -------------------------------------------------------------------------------- /lib/sequelize/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | /* 3 | thanks to https://www.npmjs.com/package/sequelize-msnodesqlv8 4 | this module is now included and supported as part of msnodesqlv8 5 | */ 6 | 7 | const Connection = require('./connection.js') 8 | const Request = require('./request.js') 9 | 10 | module.exports = { 11 | Connection: Connection, 12 | Request: Request, 13 | ISOLATION_LEVEL: {}, 14 | TYPES: {} 15 | } 16 | -------------------------------------------------------------------------------- /lib/sequelize/request.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const EventEmitter = require('events').EventEmitter 4 | const uuid = require('uuid') 5 | const debug = require('debug')('msnodesqlv8-sequelize') 6 | 7 | class Request extends EventEmitter { 8 | constructor (sql, callback) { 9 | super() 10 | 11 | this.uuid = uuid.v4() 12 | this.sql = sql 13 | this.callback = callback 14 | this.params = [] 15 | 16 | debug(`creating request (${this.uuid}): ${this.sql.length > 80 ? this.sql.slice(0, 80) + '...' : this.sql}`) 17 | } 18 | 19 | static createColumn (metadata, index, data) { 20 | const columnMetadata = metadata[index] 21 | return { 22 | metadata: { 23 | colName: columnMetadata.name, 24 | type: { 25 | id: columnMetadata.sqlType 26 | }, 27 | nullable: columnMetadata.nullable, 28 | size: columnMetadata.size 29 | }, 30 | value: data 31 | } 32 | } 33 | 34 | addParameter (key, paramType, value, typeOptions) { 35 | this.params.push(value) 36 | } 37 | 38 | execute (context) { 39 | let metadata = null 40 | let currentRow = null 41 | let e = null 42 | let rowCount = 0 43 | let lastColumn = 0 44 | 45 | debug(`connection (${context.uuid}): executing request (${this.uuid})`) 46 | let s = this.sql 47 | context.requests.push(this) 48 | try { 49 | if (this.params.length > 0) { 50 | if (s.startsWith('INSERT')) { 51 | s = s.replace(/@\d+/g, '?') 52 | } else if (s.startsWith('UPDATE')) { 53 | s = s.replace(/\s*=\s*@\d+/g, ' = ?') 54 | } 55 | } 56 | const request = context.connection.queryRaw(s, this.params) 57 | 58 | request.on('meta', (meta) => { 59 | metadata = meta 60 | currentRow = [metadata.length] 61 | lastColumn = metadata.length - 1 62 | }) 63 | 64 | request.on('column', (index, data) => { 65 | currentRow[index] = Request.createColumn(metadata, index, data) 66 | if (index === lastColumn) { 67 | ++rowCount 68 | this.emit('row', currentRow) 69 | currentRow = [metadata.length] 70 | } 71 | }) 72 | 73 | request.on('error', err => { 74 | e = err 75 | context.removeRequest(this, e) 76 | }) 77 | 78 | request.on('done', () => { 79 | context.removeRequest(this) 80 | if (typeof this.callback === 'function') { 81 | this.callback(e, rowCount) 82 | } 83 | }) 84 | } catch (ex) { 85 | context.removeRequest(this, ex) 86 | context.close() 87 | } 88 | } 89 | } 90 | 91 | module.exports = Request 92 | -------------------------------------------------------------------------------- /lib/shema-splitter.js: -------------------------------------------------------------------------------- 1 | class SchemaSplitter { 2 | stripEscape (columnName) { 3 | const columnParts = columnName.split(/\.(?![^[]*])/g) 4 | const qualified = columnParts[columnParts.length - 1] 5 | const columnNameRegexp = /\[?(.*?)]?$/g 6 | const match = columnNameRegexp.exec(qualified) 7 | const trim = match.filter(r => r !== '') 8 | return trim[trim.length - 1] 9 | } 10 | 11 | strip (name) { 12 | return name.replace(/^\[|]$/g, '').replace(/]]/g, ']') 13 | } 14 | 15 | substitute (sql, decomp) { 16 | // removes brackets at start end, change ']]' to ']' 17 | sql = sql.replace(//g, this.strip(decomp.table)) 18 | // removes brackets at start end, change ']]' to ']' 19 | .replace(//g, this.strip(decomp.schema)) 20 | // use the escaped table name for the OBJECT_ID() function 21 | .replace(//g, decomp.fullTableName) 22 | // use the escaped table name for the OBJECT_ID() function 23 | .replace(//g, decomp.cat) 24 | return sql 25 | } 26 | 27 | decomposeSchema (qualifiedName, cat) { 28 | cat = cat || '' 29 | // Split table names like 'dbo.table1' to: ['dbo', 'table1'] and 'table1' to: ['table1'] 30 | const tableParts = qualifiedName.split(/\.(?![^[]*])/g) 31 | const table = tableParts[tableParts.length - 1] // get the table name 32 | let fullTableName = table 33 | // get the table schema, if missing set schema to '' 34 | const schema = tableParts.length >= 2 ? tableParts[tableParts.length - 2] || '' : '' 35 | if (tableParts.length > 2) { 36 | cat = tableParts[tableParts.length - 3] 37 | } else if (table[0] === '#') { 38 | cat = '[tempdb]' 39 | fullTableName = `${cat}.${schema}.${table}` 40 | } 41 | return { 42 | qualifiedName, 43 | fullTableName, 44 | cat, 45 | schema, 46 | table 47 | } 48 | } 49 | } 50 | exports.SchemaSplitter = SchemaSplitter 51 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | const utilModule = ((() => { 2 | const { QueryAggregator } = require('./query-aggregator') 3 | const { SchemaSplitter } = require('./shema-splitter') 4 | const cppDriver = require('../build/Release/sqlserverv8.node') 5 | /* 6 | console.log(stripescape('[node].[dbo].[businessid]')) 7 | console.log(stripescape('[dbo].[businessid]')) 8 | console.log(stripescape('dbo.[businessid]')) 9 | console.log(stripescape('node.dbo.[businessid]')) 10 | console.log(stripescape('node.dbo.businessid')) 11 | console.log(stripescape('[age]')) 12 | console.log(stripescape('name')) 13 | 14 | businessid 15 | businessid 16 | businessid 17 | businessid 18 | businessid 19 | age 20 | name 21 | */ 22 | 23 | class Native { 24 | constructor () { 25 | this.cppDriver = cppDriver 26 | } 27 | } 28 | 29 | return { 30 | QueryAggregator, 31 | SchemaSplitter, 32 | Native 33 | } 34 | })()) 35 | 36 | exports.utilModule = utilModule 37 | -------------------------------------------------------------------------------- /samples/javascript/geography.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const sql = require('msnodesqlv8') 4 | const { TestEnv } = require('../../test/env/test-env') 5 | const env = new TestEnv() 6 | 7 | main().then(() => { 8 | console.log('done') 9 | }) 10 | 11 | async function main () { 12 | try { 13 | await env.open() 14 | const con = env.theConnection 15 | await asFunction((con)) 16 | await env.close() 17 | } catch (err) { 18 | if (err) { 19 | if (Array.isArray(err)) { 20 | err.forEach((e) => { 21 | console.log(e.message) 22 | }) 23 | } else { 24 | console.log(err.message) 25 | } 26 | } 27 | } 28 | } 29 | 30 | function asTvpTable (table, allGeography) { 31 | allGeography.forEach(l => { 32 | // each row is represented as an array of columns 33 | table.rows[table.rows.length] = [l] 34 | }) 35 | return sql.TvpFromTable(table) 36 | } 37 | 38 | async function asFunction (theConnection) { 39 | console.log('work with SQL server Geography using tvp') 40 | 41 | const geographyHelper = env.geographyHelper 42 | const coordinates = geographyHelper.getCoordinates() 43 | const allGeography = geographyHelper.all(coordinates) 44 | 45 | console.log('create table') 46 | 47 | const table = await geographyHelper.createGeographyTable() 48 | const procName = 'InsertGeographyTvp' 49 | 50 | const tp = asTvpTable(table, allGeography) 51 | table.rows = [] 52 | console.log(`call proc ${procName} with tvp to bulk insert ${allGeography.length} rows`) 53 | await theConnection.promises.callProc(procName, [tp]) 54 | console.log(`select all data '${geographyHelper.selectSql}'`) 55 | const res = await theConnection.promises.query(geographyHelper.selectSql) 56 | const json = JSON.stringify(res, null, 4) 57 | console.log(`json = ${json}`) 58 | } 59 | -------------------------------------------------------------------------------- /samples/javascript/multiple-error.js: -------------------------------------------------------------------------------- 1 | 2 | const { TestEnv } = require('../../test/env/test-env') 3 | const env = new TestEnv() 4 | 5 | main().then(() => { 6 | console.log('done') 7 | }) 8 | 9 | async function main () { 10 | try { 11 | await env.open() 12 | const con = env.theConnection 13 | await asFunction(con) 14 | console.log('') 15 | await asStream(con) 16 | await env.close() 17 | } catch (err) { 18 | if (err) { 19 | if (Array.isArray(err)) { 20 | err.forEach((e) => { 21 | console.log(e.message) 22 | }) 23 | } else { 24 | console.log(err.message) 25 | } 26 | } 27 | } 28 | } 29 | 30 | function asStream (con) { 31 | return new Promise((resolve, reject) => { 32 | let errors = 0 33 | console.log('stream based multiple errors query') 34 | const q = con.query('select a;select b;') 35 | q.on('error', (err, more) => { 36 | if (err) { 37 | console.log(`on.error [${errors}] message = ${err.message} more = ${more}`) 38 | ++errors 39 | } 40 | }) 41 | 42 | q.on('done', () => { 43 | console.log('on.done') 44 | if (errors === 0) { 45 | reject(new Error('no errors raised')) 46 | } 47 | }) 48 | 49 | q.on('free', () => { 50 | console.log('on.free') 51 | if (errors === 0) { 52 | reject(new Error('no errors raised')) 53 | } 54 | resolve(null) 55 | }) 56 | }) 57 | } 58 | 59 | function asFunction (con) { 60 | return new Promise((resolve, reject) => { 61 | console.log('callback based multiple errors query') 62 | const q = con.query('select a;select b;', function (err, res, more) { 63 | if (err) { 64 | console.log(err.message + 'more = ' + more) 65 | } else { 66 | console.log(res) 67 | reject(new Error('no errors raised')) 68 | } 69 | }) 70 | q.on('done', () => { 71 | console.log('done') 72 | }) 73 | 74 | q.on('free', () => { 75 | console.log('free') 76 | resolve(null) 77 | }) 78 | }) 79 | } 80 | -------------------------------------------------------------------------------- /samples/javascript/points.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [ 11 | -0.19535064697265625, 12 | 51.509249951770364 13 | ], 14 | [ 15 | -0.19148826599121094, 16 | 51.5100245354003 17 | ], 18 | [ 19 | -0.185093879699707, 20 | 51.510425176938 21 | ], 22 | [ 23 | -0.17569541931152344, 24 | 51.511600371790095 25 | ], 26 | [ 27 | -0.17045974731445312, 28 | 51.51226808263225 29 | ], 30 | [ 31 | -0.15844345092773438, 32 | 51.51344322994464 33 | ], 34 | [ 35 | -0.15672683715820312, 36 | 51.5102649207457 37 | ], 38 | [ 39 | -0.15110492706298828, 40 | 51.50569738228904 41 | ], 42 | [ 43 | -0.15106201171874997, 44 | 51.50404120260676 45 | ], 46 | [ 47 | -0.15097618103027344, 48 | 51.50337337118891 49 | ], 50 | [ 51 | -0.15552520751953125, 52 | 51.50251853269236 53 | ], 54 | [ 55 | -0.16028881072998044, 56 | 51.50193082192403 57 | ], 58 | [ 59 | -0.16427993774414062, 60 | 51.501690392607 61 | ], 62 | [ 63 | -0.17127513885498047, 64 | 51.501877393296546 65 | ], 66 | [ 67 | -0.17711162567138672, 68 | 51.501556820216095 69 | ], 70 | [ 71 | -0.18097400665283203, 72 | 51.50144996202149 73 | ], 74 | [ 75 | -0.1877117156982422, 76 | 51.502358248689035 77 | ], 78 | [ 79 | -0.19093036651611328, 80 | 51.50203767899114 81 | ], 82 | [ 83 | -0.19230365753173828, 84 | 51.50412134171919 85 | ], 86 | [ 87 | -0.19329071044921875, 88 | 51.50436175821088 89 | ], 90 | [ 91 | -0.19466400146484375, 92 | 51.506792564820394 93 | ] 94 | ] 95 | } 96 | } 97 | ] 98 | } -------------------------------------------------------------------------------- /samples/javascript/pooling.js: -------------------------------------------------------------------------------- 1 | const sql = require('msnodesqlv8') 2 | const { TestEnv } = require('../../test/env/test-env') 3 | const env = new TestEnv() 4 | const connectionString = env.connectionString 5 | 6 | const pool = new sql.Pool({ 7 | connectionString 8 | }) 9 | 10 | pool.on('open', (options) => { 11 | console.log(`ready options = ${JSON.stringify(options, null, 4)}`) 12 | }) 13 | 14 | pool.on('debug', msg => { 15 | console.log(`\t\t\t\t\t\t${new Date().toLocaleTimeString()} ${msg}`) 16 | }) 17 | 18 | pool.on('status', s => { 19 | console.log(`status = ${JSON.stringify(s, null, 4)}`) 20 | }) 21 | 22 | pool.on('error', e => { 23 | console.log(e) 24 | }) 25 | 26 | const testSql = 'waitfor delay \'00:00:10\';' 27 | 28 | function submit (sql) { 29 | const q = pool.query(sql) 30 | const timeStr = new Date().toLocaleTimeString() 31 | console.log(`send ${timeStr}, sql = ${sql}`) 32 | q.on('submitted', d => { 33 | console.log(`query submitted ${timeStr}, sql = ${d.query_str}`) 34 | q.on('done', () => console.log(`query done ${timeStr}`)) 35 | }) 36 | return q 37 | } 38 | 39 | for (let i = 0; i < 7; ++i) { 40 | const q = submit(testSql) 41 | switch (i) { 42 | case 5: 43 | console.log('cancel a query') 44 | q.cancelQuery() 45 | break 46 | case 6: 47 | q.pauseQuery() 48 | setTimeout(() => { 49 | console.log('resume a paused query') 50 | q.resumeQuery() 51 | }, 50000) 52 | break 53 | default: 54 | break 55 | } 56 | } 57 | 58 | setInterval(() => { 59 | submit(testSql) 60 | }, 60000) 61 | 62 | pool.open() 63 | -------------------------------------------------------------------------------- /samples/javascript/sequelize.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize') 2 | const { TestEnv } = require('../../test/env/test-env') 3 | const env = new TestEnv() 4 | const connectionString = env.connectionString 5 | 6 | const sequelize = new Sequelize({ 7 | dialect: 'mssql', 8 | dialectModulePath: 'msnodesqlv8/lib/sequelize', 9 | dialectOptions: { 10 | user: '', 11 | password: '', 12 | database: 'node', 13 | options: { 14 | driver: '', 15 | connectionString, 16 | trustedConnection: true, 17 | instanceName: '' 18 | } 19 | }, 20 | pool: { 21 | min: 0, 22 | max: 5, 23 | idle: 10000 24 | } 25 | }) 26 | 27 | function createUserModel () { 28 | return sequelize.define('user', { 29 | username: { 30 | type: Sequelize.STRING 31 | }, 32 | job: { 33 | type: Sequelize.STRING 34 | } 35 | }) 36 | } 37 | 38 | async function userModel () { 39 | const user = createUserModel() 40 | // force: true will drop the table if it already exists 41 | await user.sync({ force: true }) 42 | await Promise.all([ 43 | user.create({ 44 | username: 'techno01', 45 | job: 'Programmer' 46 | }), 47 | user.create({ 48 | username: 'techno02', 49 | job: 'Head Programmer' 50 | }), 51 | user.create({ 52 | username: 'techno03', 53 | job: 'Agile Leader' 54 | }) 55 | ]) 56 | 57 | const id1 = await user.findByPk(3) 58 | console.log(JSON.stringify(id1, null, 4)) 59 | 60 | const agile = await user.findOne({ 61 | where: { job: 'Agile Leader' } 62 | }) 63 | console.log(JSON.stringify(agile, null, 4)) 64 | 65 | const all = await user.findAll() 66 | console.log(JSON.stringify(all, null, 4)) 67 | 68 | const programmers = await user 69 | .findAndCountAll({ 70 | where: { 71 | job: { 72 | [Sequelize.Op.like]: '%Programmer' 73 | } 74 | }, 75 | limit: 2 76 | }) 77 | console.log(programmers.count) 78 | const dataValues = programmers.rows.reduce((aggregate, latest) => { 79 | aggregate.push(latest.dataValues) 80 | return aggregate 81 | }, []) 82 | console.log(dataValues) 83 | 84 | const updated = await user.update( 85 | { job: 'Scrum Master' }, 86 | { where: { id: 3 } }) 87 | console.log(updated) 88 | } 89 | 90 | userModel().then(() => { 91 | sequelize.close().then(() => { 92 | console.log('done') 93 | }) 94 | }) 95 | -------------------------------------------------------------------------------- /samples/javascript/simple-demo.js: -------------------------------------------------------------------------------- 1 | const { TestEnv } = require('../../test/env/test-env') 2 | const env = new TestEnv() 3 | const connectionString = env.connectionString 4 | 5 | const query = 'SELECT * FROM syscomments' 6 | 7 | env.sql.open(connectionString, function (err, con) { 8 | if (err) { 9 | console.log(`failed to open ${err.message}`) 10 | } 11 | const d = new Date() 12 | con.query(query, function (err, rows) { 13 | if (err) { 14 | console.log(err.message) 15 | return 16 | } 17 | const elapsed = new Date() - d 18 | console.log(`rows.length ${rows.length} elapsed ${elapsed}`) 19 | console.log(`${JSON.stringify(rows, null, 4)}`) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /samples/javascript/simple-http.js: -------------------------------------------------------------------------------- 1 | const http = require('http') 2 | const hostname = 'localhost' 3 | const port = 2020 4 | const { TestEnv } = require('../../test/env/test-env') 5 | const env = new TestEnv() 6 | const connectionString = env.connectionString 7 | 8 | const query = 'select top 50 object_name(c.object_id), (select dc.definition from sys.default_constraints as dc where dc.object_id = c.default_object_id) as DefaultValueExpression from sys.columns as c' 9 | 10 | async function test (request, response) { 11 | response.statusCode = 200 12 | response.setHeader('Content-Type', 'text/plain') 13 | 14 | console.log('>> test') 15 | try { 16 | console.log('sqlOpen ....') 17 | const connection = await env.sql.promises.open(connectionString) 18 | console.log('..... sqlOpen') 19 | try { 20 | const d = new Date() 21 | console.log('connectionQuery 1 ....') 22 | const data = await connection.promises.query(query) 23 | console.log('... connectionQuery 1') 24 | const elapsed = new Date() - d 25 | console.log(`rows.length ${data.first.length} elapsed ${elapsed}`) 26 | response.end(JSON.stringify(data, null, 4)) 27 | console.log('close ...') 28 | await connection.promises.close() 29 | console.log('... close') 30 | console.log('<< test') 31 | } catch (err) { 32 | response.end(err.message) 33 | } 34 | } catch (err) { 35 | response.end(err.message) 36 | } 37 | } 38 | 39 | const httpServer = http.createServer((request, response) => { 40 | test(request, response) 41 | }) 42 | httpServer.listen(port, hostname, () => { 43 | console.log(`Server running at http://${hostname}:${port}/`) 44 | }) 45 | -------------------------------------------------------------------------------- /samples/javascript/simple-pool.js: -------------------------------------------------------------------------------- 1 | const { TestEnv } = require('../../test/env/test-env') 2 | const env = new TestEnv() 3 | const connectionString = env.connectionString 4 | 5 | process.on('uncaughtException', err => { 6 | console.error('There was an uncaught error', err) 7 | process.exit(1) // mandatory (as per the Node.js docs) 8 | }) 9 | 10 | async function run (iterations) { 11 | const pool = new env.sql.Pool({ 12 | connectionString 13 | }) 14 | 15 | pool.on('error', err => { 16 | console.error('pool error was an uncaught error', err) 17 | }) 18 | await pool.promises.open() 19 | 20 | const testSql = 'select a;' 21 | for (let i = 0; i < iterations; ++i) { 22 | try { 23 | await pool.promises.query(testSql) 24 | } catch (e) { 25 | console.error('error', e) 26 | } 27 | } 28 | 29 | await pool.promises.close() 30 | } 31 | 32 | run(4).then(() => { 33 | console.log('exit.') 34 | }) 35 | -------------------------------------------------------------------------------- /samples/javascript/streaming.js: -------------------------------------------------------------------------------- 1 | const { TestEnv } = require('../../test/env/test-env') 2 | const env = new TestEnv() 3 | 4 | function dispatch (con, query) { 5 | return new Promise((resolve, reject) => { 6 | let metadata = null 7 | let currentRow = null 8 | let lastColumn = 0 9 | const d = new Date() 10 | const q = con.query(query) 11 | const rows = [] 12 | 13 | q.on('meta', (meta) => { 14 | metadata = meta 15 | currentRow = [metadata.length] 16 | lastColumn = metadata.length - 1 17 | console.log(`meta = ${JSON.stringify(meta, null, 4)}`) 18 | }) 19 | 20 | q.on('submitted', (m) => { 21 | const elapsed = new Date() - d 22 | console.log(`submitted ${m.query_str} elapsed ${elapsed} ms`) 23 | }) 24 | 25 | q.on('column', (index, data) => { 26 | console.log(`column [${index}] = ${JSON.stringify(data, null, 4)}`) 27 | currentRow[index] = data 28 | if (index === lastColumn) { 29 | currentRow = [metadata.length] 30 | rows.push(currentRow) 31 | } 32 | }) 33 | 34 | q.on('row', (index) => { 35 | console.log(`row [${index}]`) 36 | }) 37 | 38 | q.on('error', err => { 39 | reject(err) 40 | }) 41 | 42 | q.on('info', i => { 43 | console.log(i) 44 | }) 45 | 46 | q.on('done', () => { 47 | console.log('done') 48 | resolve(rows) 49 | }) 50 | 51 | q.on('free', () => { 52 | console.log('free') 53 | }) 54 | }) 55 | } 56 | 57 | async function run () { 58 | await env.open() 59 | const connectionString = env.connectionString 60 | console.log(`run with ${connectionString}`) 61 | const conn = env.theConnection 62 | 63 | for (let i = 0; i < 5; ++i) { 64 | const d = new Date() 65 | const rows = await dispatch(conn, 'select top 5 * from master..syscolumns') 66 | const elapsed = new Date() - d 67 | console.log(`${rows.length} rows returned elapsed ${elapsed}`) 68 | } 69 | 70 | await env.close() 71 | return [] 72 | } 73 | 74 | run().then(() => { 75 | console.log('finished') 76 | }).catch(err => { 77 | console.log(err) 78 | }) 79 | -------------------------------------------------------------------------------- /samples/javascript/thread-workers.js: -------------------------------------------------------------------------------- 1 | 2 | const path = require('path') 3 | const filePath = path.resolve(__dirname, './worker-item.js') 4 | const { Worker } = require('worker_threads') 5 | 6 | const worker1 = new Worker(filePath) 7 | const worker2 = new Worker(filePath) 8 | 9 | function dispatch (worker) { 10 | worker.on('message', msg => { 11 | switch (msg.command) { 12 | case 'task_result': { 13 | console.log(JSON.stringify(msg, null, 4)) 14 | } 15 | } 16 | }) 17 | 18 | worker.on('error', error => { 19 | console.log(error) 20 | }) 21 | } 22 | 23 | dispatch(worker1) 24 | dispatch(worker2) 25 | 26 | function sendTask (worker, num) { 27 | worker.postMessage( 28 | { 29 | command: 'task', 30 | num 31 | }) 32 | } 33 | 34 | function clean () { 35 | setTimeout(async () => { 36 | console.log('exit.') 37 | await Promise.all([ 38 | worker1.terminate(), 39 | worker2.terminate() 40 | ]) 41 | }, 5000) 42 | } 43 | 44 | for (let i = 0; i < 40; i += 2) { 45 | sendTask(worker1, i) 46 | sendTask(worker2, i + 1) 47 | } 48 | 49 | clean() 50 | -------------------------------------------------------------------------------- /samples/javascript/throttle.js: -------------------------------------------------------------------------------- 1 | const { TestEnv } = require('../../test/env/test-env') 2 | const env = new TestEnv() 3 | 4 | main().then(() => { 5 | console.log('done') 6 | }) 7 | 8 | async function main () { 9 | const connectionString = env.connectionString 10 | try { 11 | const con = await env.sql.promises.open(connectionString) 12 | await asStream((con)) 13 | await con.promises.close() 14 | } catch (err) { 15 | if (err) { 16 | if (Array.isArray(err)) { 17 | err.forEach((e) => { 18 | console.log(e.message) 19 | }) 20 | } else { 21 | console.log(err.message) 22 | } 23 | } 24 | } 25 | } 26 | 27 | // create a long running task e.g. use batch for web call 28 | function dispatch (batch) { 29 | return new Promise((resolve, reject) => { 30 | setTimeout(() => { 31 | resolve(null) 32 | }, 1500, batch) 33 | }) 34 | } 35 | 36 | function asStream (con) { 37 | return new Promise((resolve, reject) => { 38 | const sql = 'select top 750 * from master..syscolumns;' 39 | console.log('stream fetch query with throttle back to server to slow results') 40 | const q = con.query(sql) 41 | const errors = [] 42 | let row = null 43 | let meta = null 44 | const rowsPerBatch = 100 45 | let batch = [] 46 | let rowCount = 0 47 | let batchIndex = 0 48 | 49 | q.on('submitted', q => { 50 | console.log(`on.submitted ${q.query_str}`) 51 | }) 52 | 53 | q.on('error', (err, more) => { 54 | if (more) { 55 | errors.push(err) 56 | } else { 57 | const msg = errors.map(e => e.message).join(', ') 58 | reject(new Error(msg)) 59 | } 60 | }) 61 | 62 | q.on('meta', (m) => { 63 | meta = m 64 | }) 65 | 66 | // start of row so create a new array and store for later 67 | q.on('row', () => { 68 | ++rowCount 69 | row = [meta.length] 70 | batch.push(row) 71 | }) 72 | 73 | function next () { 74 | const d = new Date() 75 | const ts = d.toLocaleTimeString() 76 | console.log(`${ts} - [${batchIndex}] (rowCount ${rowCount}): pause and dispatch ${batch.length} rows ...`) 77 | q.pauseQuery() 78 | dispatch(batch).then(() => { 79 | console.log(`${ts} - [${batchIndex}] (rowCount ${rowCount}): ... done, resume query`) 80 | batchIndex++ 81 | batch = [] 82 | q.resumeQuery() 83 | }) 84 | } 85 | 86 | q.on('column', (c, d) => { 87 | const last = meta.length - 1 88 | row[c] = d 89 | if (c === last && batch.length === rowsPerBatch) { 90 | next() 91 | } 92 | }) 93 | 94 | q.on('done', () => { 95 | console.log('on.done') 96 | if (batch.length > 0) { 97 | console.log(`[${batchIndex}] (rowCount ${rowCount}): last dispatch ${batch.length} rows`) 98 | dispatch(batch).then(() => { 99 | }) 100 | } 101 | }) 102 | 103 | q.on('free', () => { 104 | resolve(null) 105 | }) 106 | }) 107 | } 108 | -------------------------------------------------------------------------------- /samples/javascript/tvp.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { TestEnv } = require('../../test/env/test-env') 4 | const env = new TestEnv() 5 | 6 | main().then(() => { 7 | console.log('done') 8 | }) 9 | 10 | async function main () { 11 | try { 12 | await env.open() 13 | await asFunction(env.theConnection) 14 | await env.close() 15 | } catch (err) { 16 | if (err) { 17 | if (Array.isArray(err)) { 18 | err.forEach((e) => { 19 | console.log(e.message) 20 | }) 21 | } else { 22 | console.log(err.message) 23 | } 24 | } 25 | } 26 | } 27 | 28 | async function asFunction (theConnection) { 29 | console.log('work with tvp type to bulk insert') 30 | 31 | const tableName = 'TestTvp' 32 | const helper = env.tvpHelper(tableName) 33 | const vec = helper.getExtendedVec(10) 34 | const table = await helper.create() 35 | table.addRowsFromObjects(vec) 36 | const tp = env.sql.TvpFromTable(table) 37 | table.rows = [] 38 | const execSql = 'exec insertTestTvp @tvp = ?;' 39 | console.log(`exec ${execSql}`) 40 | await theConnection.promises.query(execSql, [tp]) 41 | const selectSql = `select * from ${tableName}` 42 | console.log(`select results ${selectSql}`) 43 | const res = await theConnection.promises.query(selectSql) 44 | const json = JSON.stringify(res, null, 4) 45 | console.log(`json = ${json}`) 46 | } 47 | -------------------------------------------------------------------------------- /samples/javascript/txn3.js: -------------------------------------------------------------------------------- 1 | 2 | const { TestEnv } = require('../../test/env/test-env') 3 | const env = new TestEnv() 4 | 5 | const connectionString = env.connectionString 6 | console.log(`connectionString = ${connectionString}`) 7 | 8 | const isolation = 'SNAPSHOT' 9 | 10 | async function run (id) { 11 | const conn = await env.sql.promises.open(connectionString) 12 | await conn.promises.query(`SET TRANSACTION ISOLATION LEVEL ${isolation}`) 13 | iterate(id, conn) 14 | } 15 | 16 | let counter = 0 17 | async function iterate (id, conn) { 18 | console.log(`start [${id}] counter = ${counter}`) 19 | try { 20 | await conn.promises.query('BEGIN TRANSACTION') 21 | await conn.promises.query('INSERT INTO _customer (name) OUTPUT INSERTED.id,INSERTED.name VALUES (?)', ['foo']) 22 | await conn.promises.query('select top 10 * from _customer order by id DESC') 23 | await conn.promises.query('COMMIT') 24 | } catch (e) { 25 | console.log(`[${id}] error = ${e}`) 26 | } 27 | console.log(`done [${id}] counter = ${counter}`) 28 | 29 | setTimeout(() => { 30 | ++counter 31 | iterate(id, conn) 32 | }, 100) 33 | } 34 | 35 | for (let i = 0; i < 10; i++) { 36 | run(i) 37 | } 38 | -------------------------------------------------------------------------------- /samples/javascript/worker-item.js: -------------------------------------------------------------------------------- 1 | const { parentPort } = require('worker_threads') 2 | const sql = require('msnodesqlv8') 3 | const { TestEnv } = require('../../test/env/test-env') 4 | const env = new TestEnv() 5 | const connectionString = env.connectionString 6 | 7 | async function compute (msg) { 8 | try { 9 | console.log(`worker receive task ${msg.num}`) 10 | const conn = await sql.promises.open(connectionString) 11 | const query = `select ${msg.num} as i, @@SPID as spid` 12 | const res = await conn.promises.query(query) 13 | await conn.promises.close() 14 | parentPort.postMessage( 15 | { 16 | command: 'task_result', 17 | data: `spid ${res.first[0].spid}`, 18 | num: msg.num, 19 | fib: getFib(msg.num) 20 | }) 21 | } catch (e) { 22 | parentPort.emit('error', e) 23 | } 24 | } 25 | 26 | parentPort.on('message', async msg => { 27 | switch (msg.command) { 28 | case 'task': { 29 | await compute(msg) 30 | break 31 | } 32 | default: { 33 | console.log(`unknown command ${msg.command}`) 34 | break 35 | } 36 | } 37 | }) 38 | 39 | function getFib (num) { 40 | if (num === 0) { 41 | return 0 42 | } else if (num === 1) { 43 | return 1 44 | } else { 45 | return getFib(num - 1) + getFib(num - 2) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /samples/typescript/db-backup.ts: -------------------------------------------------------------------------------- 1 | import { SqlClient, Error } from 'msnodesqlv8/types' 2 | const sql: SqlClient = require('msnodesqlv8') 3 | 4 | const connectionString = 'Driver={SQL Server Native Client 11.0};Server=(localdb)\\node;Database=AdventureWorks2019;Trusted_Connection=yes;' 5 | const sqlQuery = `BACKUP DATABASE [AdventureWorks2019] TO DISK = N'H:\\sql server\\backups\\AdventureWorks2019.bak' WITH COPY_ONLY, 6 | NOFORMAT, INIT, NAME = N'SampleDb-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10` 7 | 8 | const query = sql.query(connectionString, sqlQuery, (err, rows, more) => { 9 | more = more ?? false 10 | if (more) return 11 | console.error(`cb more ${more ? 'T' : 'F'}`) 12 | if (err != null) { 13 | console.error('cb err') 14 | } else { 15 | console.log('cb rows') 16 | } 17 | }) 18 | 19 | query.on('info', (info: Error) => { 20 | console.log(`info: ${JSON.stringify(info)}`) 21 | }) 22 | 23 | query.on('error', (e: Error) => { 24 | console.log(`error: ${JSON.stringify(e)}`) 25 | }) 26 | 27 | query.on('done', () => { 28 | console.log('done') 29 | }) 30 | -------------------------------------------------------------------------------- /samples/typescript/pooling.ts: -------------------------------------------------------------------------------- 1 | import { Error, PoolOptions, Query, SqlClient, QueryDescription, Pool, PoolStatusRecord } from 'msnodesqlv8/types' 2 | 3 | const sql: SqlClient = require('msnodesqlv8') 4 | 5 | const { TestEnv } = require('../../../test/env/test-env') 6 | const env = new TestEnv() 7 | const str = env.connectionString 8 | 9 | const pool: Pool = new sql.Pool({ 10 | connectionString: str 11 | }) 12 | 13 | pool.on('open', (options: PoolOptions) => { 14 | console.log(`ready options = ${JSON.stringify(options, null, 4)}`) 15 | }) 16 | 17 | pool.on('debug', (msg: string) => { 18 | console.log(`\t\t\t\t\t\t${new Date().toLocaleTimeString()} ${msg}`) 19 | }) 20 | 21 | pool.on('status', (s: PoolStatusRecord) => { 22 | console.log(`status = ${JSON.stringify(s, null, 4)}`) 23 | }) 24 | 25 | pool.on('error', (e: Error) => { 26 | console.log(e) 27 | }) 28 | 29 | const testSql = 'waitfor delay \'00:00:10\';' 30 | 31 | function submit (sql: string): Query { 32 | const q: Query = pool.query(sql) 33 | const timeStr = new Date().toLocaleTimeString() 34 | console.log(`send ${timeStr}, sql = ${sql}`) 35 | q.on('submitted', (d: QueryDescription) => { 36 | console.log(`query submitted ${timeStr}, sql = ${d.query_str}`) 37 | q.on('done', () => console.log(`query done ${timeStr}`)) 38 | }) 39 | return q 40 | } 41 | 42 | for (let i = 0; i < 7; ++i) { 43 | const q: Query = submit(testSql) 44 | switch (i) { 45 | case 5: 46 | console.log('cancel a query') 47 | q.cancelQuery() 48 | break 49 | case 6: 50 | q.pauseQuery() 51 | setTimeout(() => { 52 | console.log('resume a paused query') 53 | q.resumeQuery() 54 | }, 50000) 55 | break 56 | default: 57 | break 58 | } 59 | } 60 | 61 | setInterval(() => { 62 | submit(testSql) 63 | }, 60000) 64 | 65 | pool.open((e: Error, options: PoolOptions) => { 66 | if (e != null) { 67 | console.log(`Error ${e.message}`) 68 | } else { 69 | console.log(JSON.stringify(options, null, 4)) 70 | } 71 | }) 72 | -------------------------------------------------------------------------------- /samples/typescript/simple-demo.ts: -------------------------------------------------------------------------------- 1 | import { SqlClient } from 'msnodesqlv8/types' 2 | 3 | const sql: SqlClient = require('msnodesqlv8') 4 | 5 | const connectionString = 'server=.;Database=Master;Trusted_Connection=Yes;Driver={SQL Server Native Client 11.0}' 6 | const query = 'SELECT name FROM sys.databases' 7 | 8 | sql.query(connectionString, query, (err, rows) => { 9 | if (err != null) { 10 | console.error(err) 11 | } else { 12 | console.log(rows) 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /src/BeginTranOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "OdbcConnection.h" 2 | #include 3 | 4 | namespace mssql 5 | { 6 | BeginTranOperation::BeginTranOperation(const shared_ptr &connection, const Local callback) 7 | : OdbcOperation(connection, callback) 8 | { 9 | } 10 | 11 | bool BeginTranOperation::TryInvokeOdbc() 12 | { 13 | return _connection->try_begin_tran(); 14 | } 15 | 16 | Local BeginTranOperation::CreateCompletionArg() 17 | { 18 | const nodeTypeFactory fact; 19 | return fact.null(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/BeginTranOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class BeginTranOperation : public OdbcOperation 32 | { 33 | public: 34 | BeginTranOperation(const shared_ptr &connection, const Local callback); 35 | bool TryInvokeOdbc() override; 36 | Local CreateCompletionArg() override; 37 | }; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/BigIntColumn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace mssql 9 | { 10 | using namespace std; 11 | 12 | class BigIntColumn : public Column 13 | { 14 | public: 15 | 16 | BigIntColumn(int id, DatumStorage::bigint_t d) : Column(id), value(d) 17 | { 18 | } 19 | 20 | BigIntColumn(int id, shared_ptr storage) : Column(id), value((*storage->bigint_vec_ptr)[0]) 21 | { 22 | } 23 | 24 | inline Local ToString() override 25 | { 26 | return AsString(value); 27 | } 28 | 29 | inline Local ToNative() override 30 | { 31 | return Nan::New((double)value); 32 | } 33 | 34 | private: 35 | DatumStorage::bigint_t value; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /src/BinaryColumn.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: Column.cpp 3 | // Contents: Column objects from SQL Server to return as Javascript types 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #include "stdafx.h" 21 | #include 22 | 23 | namespace mssql { 24 | 25 | BinaryColumn::BinaryColumn(const int id, const shared_ptr s, const size_t l) : Column(id) 26 | , storage(s->charvec_ptr), len(l), offset(0) 27 | { 28 | } 29 | 30 | BinaryColumn::BinaryColumn(const int id, const shared_ptr s, const size_t offset, const size_t l) : Column(id) , storage(s->charvec_ptr), len(l), offset(offset) 31 | { 32 | } 33 | 34 | Local BinaryColumn::ToString() 35 | { 36 | const auto* const ptr = storage->data() + offset; 37 | const std::string s(ptr, ptr + len); 38 | storage->reserve(0); 39 | storage = nullptr; 40 | const auto st = Nan::Encode(s.data() + offset, s.size() * 2, Nan::UCS2); 41 | // fprintf(stderr, "[%d], ToValue len = %zu, offset = %zu, ptr = %p, destructed = %d\n", Id(), len, offset, str, destructed); 42 | return st; 43 | } 44 | 45 | Local BinaryColumn::ToNative() 46 | { 47 | const auto* const ptr = storage->data() + offset; 48 | const auto buff = Nan::CopyBuffer(ptr, len).ToLocalChecked(); 49 | storage->reserve(0); 50 | storage = nullptr; 51 | // fprintf(stderr, "[%d], ToValue len = %zu, offset = %zu, ptr = %p, destructed = %d\n", Id(), len, offset, str, destructed); 52 | return buff; 53 | } 54 | } // namespace mssql -------------------------------------------------------------------------------- /src/BinaryColumn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "Column.h" 6 | #include 7 | #include "BoundDatumHelper.h" 8 | 9 | namespace mssql 10 | { 11 | using namespace std; 12 | 13 | class BinaryColumn : public Column 14 | { 15 | 16 | public: 17 | BinaryColumn(const int id, shared_ptr s, size_t l); 18 | BinaryColumn(const int id, shared_ptr s, size_t offset, size_t l); 19 | Local ToNative() override; 20 | Local ToString() override; 21 | 22 | private: 23 | shared_ptr storage; 24 | size_t len; 25 | size_t offset; 26 | 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/BoolColumn.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "stdafx.h" 5 | #include 6 | 7 | namespace mssql 8 | { 9 | using namespace std; 10 | 11 | class BoolColumn : public Column 12 | { 13 | public: 14 | BoolColumn(int id, shared_ptr storage) : Column(id), value((*storage->charvec_ptr)[0] != 0 ? true : false) 15 | { 16 | } 17 | 18 | BoolColumn(int id, char v) : Column(id), value(v != 0 ? true : false) 19 | { 20 | } 21 | 22 | inline Local ToString() override 23 | { 24 | return AsString(value); 25 | } 26 | 27 | inline Local ToNative() override 28 | { 29 | return Nan::New(value); 30 | } 31 | private: 32 | bool value; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /src/BoundDatumSet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace mssql 7 | { 8 | class ResultSet; 9 | class QueryOperationParams; 10 | 11 | class BoundDatumSet 12 | { 13 | public: 14 | typedef vector> param_bindings; 15 | BoundDatumSet(); 16 | BoundDatumSet(const shared_ptr params); 17 | bool reserve(const shared_ptr &set, size_t row_count) const; 18 | bool bind(Local &node_params); 19 | Local unbind() const; 20 | void clear() { _bindings->clear(); } 21 | size_t size() { return _bindings->size(); } 22 | shared_ptr & atIndex(int i) { return (*_bindings)[i]; } 23 | param_bindings::iterator begin() { return _bindings->begin(); } 24 | param_bindings::iterator end() { return _bindings->end(); } 25 | 26 | char * err; 27 | uint32_t first_error; 28 | 29 | private: 30 | bool tvp(Local &v) const; 31 | int _output_param_count; 32 | shared_ptr _bindings; 33 | shared_ptr _params; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3) 2 | project(src) 3 | 4 | # e.g. "date_time" for "libboost_date_time" 5 | 6 | set(Boost_USE_STATIC_LIBS ON) # only find static libs 7 | set(Boost_USE_MULTITHREADED ON) 8 | set(Boost_USE_STATIC_RUNTIME OFF) 9 | find_package(Boost COMPONENTS date_time filesystem system) 10 | 11 | 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 13 | 14 | set(SOURCE_FILES 15 | BinaryColumn.h 16 | BoolColumn.h 17 | Column.cpp 18 | Column.h 19 | Connection.cpp 20 | Connection.h 21 | CriticalSection.h 22 | IntColumn.h 23 | NodeColumns.h 24 | NullColumn.h 25 | NumberColumn.h 26 | OdbcConnection.cpp 27 | OdbcConnection.h 28 | OdbcConnectionBridge.h 29 | OdbcError.cpp 30 | OdbcError.h 31 | OdbcHandle.h 32 | OdbcOperation.cpp 33 | OdbcOperation.h 34 | Operation.h 35 | ResultSet.cpp 36 | ResultSet.h 37 | sqlncli.h 38 | stdafx.cpp 39 | stdafx.h 40 | StringColumn.h 41 | TimestampColumn.h 42 | Utility.cpp 43 | Utility.h) 44 | 45 | if(Boost_FOUND) 46 | include_directories(${Boost_INCLUDE_DIRS}) 47 | add_executable(src ${SOURCE_FILES}) 48 | target_link_libraries(nodelib ${Boost_LIBRARIES}) 49 | endif() -------------------------------------------------------------------------------- /src/CancelOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | bool CancelOperation::TryInvokeOdbc() 8 | { 9 | if (!_statement) return false; 10 | return _statement->cancel(); 11 | } 12 | 13 | Local CancelOperation::CreateCompletionArg() 14 | { 15 | const nodeTypeFactory fact; 16 | return fact.null(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/CancelOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class CancelOperation : public OdbcOperation 32 | { 33 | public: 34 | 35 | CancelOperation(shared_ptr connection, size_t queryId, Local callback) 36 | : OdbcOperation(connection, callback) 37 | { 38 | _statementId = queryId; 39 | _can_lock = false; 40 | } 41 | 42 | bool TryInvokeOdbc() override; 43 | 44 | Local CreateCompletionArg() override; 45 | }; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/CharColumn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Column.h" 8 | #include "Utility.h" 9 | #include "BoundDatumHelper.h" 10 | #include 11 | 12 | namespace mssql 13 | { 14 | using namespace std; 15 | 16 | class CharColumn : public Column 17 | { 18 | public: 19 | virtual ~CharColumn() 20 | { 21 | } 22 | 23 | CharColumn(int id, shared_ptr s, size_t size) 24 | : 25 | Column(id), 26 | size(size), 27 | storage(s->charvec_ptr) 28 | { 29 | } 30 | 31 | CharColumn(int id, shared_ptr s, size_t size) 32 | : 33 | Column(id), 34 | size(size), 35 | storage(s) 36 | { 37 | } 38 | 39 | CharColumn(int id, shared_ptr s, size_t offset, size_t size) 40 | : 41 | Column(id), 42 | size(size), 43 | storage(s), 44 | offset(offset) 45 | { 46 | } 47 | 48 | inline Local ToString() override 49 | { 50 | return ToValue(); 51 | } 52 | 53 | inline Local ToNative() override 54 | { 55 | auto sptr = storage->data(); 56 | auto s = Nan::Encode(sptr + offset, size, Nan::UTF8); 57 | return s; 58 | } 59 | 60 | private: 61 | size_t size; 62 | shared_ptr storage; 63 | size_t offset = 0; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/CloseOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | CloseOperation::CloseOperation(const shared_ptr &connection, const Local callback) 8 | : OdbcOperation(connection, callback) 9 | { 10 | } 11 | 12 | bool CloseOperation::TryInvokeOdbc() 13 | { 14 | //fprintf(stderr, "invoke TryClose statementId = %d operationId = %llu\n", 15 | // statementId, 16 | // OperationID ); 17 | // cerr << "CloseOperation statementId = " << _statementId << endl; 18 | return _connection->TryClose(); 19 | } 20 | 21 | Local CloseOperation::CreateCompletionArg() 22 | { 23 | const nodeTypeFactory fact; 24 | return fact.null(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/CloseOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include "OdbcOperation.h" 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class CloseOperation : public OdbcOperation 32 | { 33 | public: 34 | CloseOperation(const shared_ptr &connection, const Local callback); 35 | bool TryInvokeOdbc() override; 36 | Local CreateCompletionArg() override; 37 | }; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/CollectOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | CollectOperation::CollectOperation(const shared_ptr &connection) 8 | : OdbcOperation(connection, Local()) 9 | { 10 | } 11 | 12 | bool CollectOperation::TryInvokeOdbc() 13 | { 14 | return _connection->TryClose(); 15 | } 16 | 17 | Local CollectOperation::CreateCompletionArg() 18 | { 19 | const nodeTypeFactory fact; 20 | return fact.null(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/CollectOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include "OdbcOperation.h" 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class CollectOperation : public OdbcOperation 32 | { 33 | public: 34 | CollectOperation(const shared_ptr &connection); 35 | bool TryInvokeOdbc() override; 36 | Local CreateCompletionArg() override; 37 | // override to not call a callback 38 | }; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/Column.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: Column.cpp 3 | // Contents: Column objects from SQL Server to return as Javascript types 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #include 21 | 22 | namespace mssql { 23 | 24 | Column::~Column() 25 | { 26 | } 27 | } // namespace mssql -------------------------------------------------------------------------------- /src/Column.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: Column.h 3 | // Contents: Column objects from SQL Server to return as Javascript types 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | namespace mssql 26 | { 27 | using namespace std; 28 | using namespace v8; 29 | 30 | class Column 31 | { 32 | public: 33 | Column(int id) : _id(id), _asNative(true) 34 | { 35 | } 36 | virtual ~Column(); 37 | 38 | virtual Local ToNative() = 0; 39 | virtual Local ToValue() { 40 | return _asNative ? ToNative() : ToString(); 41 | } 42 | 43 | virtual Local ToString() = 0; 44 | 45 | int Id() const { return _id; } 46 | void AsString() { 47 | _asNative = false; 48 | } 49 | 50 | template Local AsString(T value) 51 | { 52 | auto str = to_wstring(value); 53 | auto sptr = str.data(); 54 | auto s = Nan::Encode(sptr, str.size() * 2, Nan::UCS2); 55 | return s; 56 | } 57 | 58 | private: 59 | int _id; 60 | bool _asNative; 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /src/Connection.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: Connection.h 3 | // Contents: C++ interface to Microsoft Driver for Node.js for SQL Server 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | #include "OdbcConnectionBridge.h" 24 | 25 | namespace mssql 26 | { 27 | using namespace std; 28 | using namespace v8; 29 | 30 | class Connection : public Nan::ObjectWrap 31 | { 32 | public: 33 | static NAN_MODULE_INIT(Init); 34 | Connection(); 35 | virtual ~Connection(); 36 | 37 | private: 38 | typedef Nan::NAN_METHOD_ARGS_TYPE NanCb; 39 | static NAN_METHOD(New); 40 | static NAN_METHOD(close); 41 | static NAN_METHOD(begin_transaction); 42 | static NAN_METHOD(commit); 43 | static NAN_METHOD(rollback); 44 | static NAN_METHOD(open); 45 | static NAN_METHOD(query); 46 | static NAN_METHOD(prepare); 47 | static NAN_METHOD(bind_query); 48 | static NAN_METHOD(call_procedure); 49 | static NAN_METHOD(unbind); 50 | static NAN_METHOD(free_statement); 51 | static NAN_METHOD(read_row); 52 | static NAN_METHOD(cancel_statement); 53 | static NAN_METHOD(read_column); 54 | static NAN_METHOD(read_next_result); 55 | static NAN_METHOD(polling_mode); 56 | 57 | static Nan::Persistent constructor; 58 | static void api(Local& tpl); 59 | unique_ptr connectionBridge; 60 | Persistent This; 61 | }; 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/ConnectionHandles.cpp: -------------------------------------------------------------------------------- 1 | #include "ConnectionHandles.h" 2 | 3 | namespace mssql { 4 | ConnectionHandles::ConnectionHandles(const OdbcEnvironmentHandle& env) { 5 | _connectionHandle = make_shared(); 6 | if (!_connectionHandle->alloc(env)) { 7 | _connectionHandle = nullptr; 8 | } 9 | } 10 | 11 | ConnectionHandles::~ConnectionHandles() { 12 | clear(); 13 | _connectionHandle->free(); 14 | _connectionHandle = nullptr; 15 | } 16 | 17 | void ConnectionHandles::clear() 18 | { 19 | // cerr << "OdbcStatementCache - size = " << statements.size() << endl; 20 | vector ids; 21 | // fprintf(stderr, "destruct OdbcStatementCache\n"); 22 | 23 | for_each(_statementHandles.begin(), _statementHandles.end(), [&](const auto & p) { 24 | const shared_ptr s = (p.second); 25 | // std::cerr << " clear " << p.first << " p = " << this << std::endl; 26 | s->free(); 27 | ids.insert(ids.begin(), p.first); 28 | }); 29 | 30 | for_each(ids.begin(), ids.end(), [&](const long id) { 31 | // cerr << "destruct OdbcStatementCache - erase statement" << id << endl; 32 | _statementHandles.erase(id); 33 | }); 34 | } 35 | 36 | shared_ptr ConnectionHandles::find(const long statement_id) 37 | { 38 | shared_ptr statement_handle = nullptr; 39 | const auto itr = _statementHandles.find(statement_id); 40 | if (itr != _statementHandles.end()) { 41 | statement_handle = itr->second; 42 | } 43 | return statement_handle; 44 | } 45 | 46 | shared_ptr ConnectionHandles::store(shared_ptr handle) 47 | { 48 | _statementHandles.insert(pair>(handle->statementId, handle)); 49 | return handle; 50 | } 51 | 52 | shared_ptr ConnectionHandles::checkout(long statement_id) 53 | { 54 | if (statement_id < 0) 55 | { 56 | //fprintf(stderr, "dont fetch id %ld\n", statementId); 57 | return nullptr; 58 | } 59 | auto statement = find(statement_id); 60 | if (statement) return statement; 61 | const auto handle = make_shared(statement_id); 62 | handle->alloc(*_connectionHandle); 63 | //std::cerr << " checkout " << statement_id << " p = " << this << endl; 64 | return store(handle); 65 | } 66 | 67 | void ConnectionHandles::checkin(long statementId) { 68 | // std::cerr << " checkin " << statementId << " p = " << this << endl; 69 | const auto handle = find(statementId); 70 | if (handle == nullptr) return; 71 | _statementHandles.erase(statementId); 72 | handle->free(); 73 | } 74 | } -------------------------------------------------------------------------------- /src/ConnectionHandles.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: ConnectionHandles.h 3 | // Contents: Object to manage ODBC handles 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include "stdafx.h" 23 | #include 24 | #include 25 | 26 | namespace mssql 27 | { 28 | using namespace std; 29 | class OdbcConnectionHandle; 30 | class OdbcStatementHandle; 31 | class OdbcEnvironmentHandle; 32 | 33 | class ConnectionHandles 34 | { 35 | public: 36 | ConnectionHandles(const OdbcEnvironmentHandle &env); 37 | ~ConnectionHandles(); 38 | shared_ptr checkout(long statementId); 39 | void checkin(long statementId); 40 | inline shared_ptr connectionHandle() { return _connectionHandle; } 41 | void clear(); 42 | 43 | private: 44 | 45 | shared_ptr store(shared_ptr handle); 46 | shared_ptr find(const long statement_id); 47 | map> _statementHandles; 48 | shared_ptr _connectionHandle; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /src/CriticalSection.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: CriticalSection.h 3 | // Contents: Wrapper for a critical section that handles scope 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | #include 20 | #include 21 | 22 | namespace mssql { 23 | 24 | #pragma once 25 | 26 | class ScopedCriticalSectionLock { 27 | 28 | public: 29 | 30 | ScopedCriticalSectionLock( std::mutex & cs ) : 31 | criticalSection_( cs ) 32 | { 33 | criticalSection_.lock(); 34 | } 35 | 36 | ~ScopedCriticalSectionLock( void ) 37 | { 38 | criticalSection_.unlock(); 39 | } 40 | 41 | private: 42 | std::mutex & criticalSection_; 43 | }; 44 | 45 | } // mssql 46 | -------------------------------------------------------------------------------- /src/EndTranOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | EndTranOperation::EndTranOperation(const shared_ptr &connection, 8 | const SQLSMALLINT completion_type, const Local callback) 9 | : OdbcOperation(connection, callback), 10 | completionType(completion_type) 11 | { 12 | } 13 | 14 | bool EndTranOperation::TryInvokeOdbc() 15 | { 16 | return _connection->try_end_tran(completionType); 17 | } 18 | 19 | Local EndTranOperation::CreateCompletionArg() 20 | { 21 | const nodeTypeFactory fact; 22 | return fact.null(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/EndTranOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include "OdbcOperation.h" 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class EndTranOperation : public OdbcOperation 32 | { 33 | SQLSMALLINT completionType; 34 | 35 | public: 36 | EndTranOperation(const shared_ptr &connection, SQLSMALLINT completion_type, Local callback); 37 | bool TryInvokeOdbc() override; 38 | Local CreateCompletionArg() override; 39 | }; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/FreeStatementOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace mssql 8 | { 9 | bool FreeStatementOperation::TryInvokeOdbc() 10 | { 11 | // std::cerr << "FreeStatementOperation() " << _statementId << " " << endl; 12 | _connection->getStatamentCache()->checkin(_statementId); 13 | //fprintf(stderr, " checkin statementId %d size %llu\n", statementId, connection->statements->size()); 14 | return true; 15 | } 16 | 17 | Local FreeStatementOperation::CreateCompletionArg() 18 | { 19 | const nodeTypeFactory fact; 20 | return fact.null(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/FreeStatementOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include "OdbcOperation.h" 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class FreeStatementOperation : public OdbcOperation 32 | { 33 | public: 34 | 35 | FreeStatementOperation(shared_ptr connection, size_t queryId, Local callback) 36 | : OdbcOperation(connection, queryId, callback) 37 | { 38 | } 39 | 40 | bool TryInvokeOdbc() override; 41 | 42 | Local CreateCompletionArg() override; 43 | }; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/IntColumn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Column.h" 5 | #include "BoundDatumHelper.h" 6 | 7 | namespace mssql 8 | { 9 | using namespace std; 10 | 11 | class IntColumn : public Column 12 | { 13 | public: 14 | IntColumn(int id,shared_ptr storage) : Column(id), value((*storage->int64vec_ptr)[0]) 15 | { 16 | } 17 | 18 | IntColumn(int id, long v) : Column(id), value(v) 19 | { 20 | } 21 | 22 | inline Local ToString() override 23 | { 24 | return AsString(value); 25 | } 26 | 27 | inline Local ToNative() override 28 | { 29 | return Nan::New(static_cast(value)); 30 | } 31 | 32 | private: 33 | int64_t value; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/MutateJS.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | bool MutateJS::getbool(const Local query_object, const char* v) 8 | { 9 | const auto l = get(query_object, v); 10 | return as_boolean(l); 11 | } 12 | 13 | bool MutateJS::set_property_value(const Local& o, const Local& p, const Local& v) 14 | { 15 | return Nan::Set(o, p, v).ToChecked(); 16 | } 17 | 18 | Local MutateJS::get_property_as_value(const Local& o, const Local& v) 19 | { 20 | if ( o->IsUndefined() || o->IsNull()) 21 | { 22 | const nodeTypeFactory fact; 23 | return fact.null(); 24 | } 25 | const auto p = Nan::Get(o,v).ToLocalChecked(); 26 | return p; 27 | } 28 | 29 | static bool isUnDefined(const Local l) { 30 | return l->IsUndefined() || l->IsNull(); 31 | } 32 | 33 | int32_t MutateJS::getint32(const Local query_object, const char* v) 34 | { 35 | const auto l = get(query_object, v); 36 | if (!isUnDefined(l)) 37 | { 38 | return Nan::To(l).ToChecked(); 39 | } 40 | return 0; 41 | } 42 | 43 | bool MutateJS::as_boolean(const Local& as_val) 44 | { 45 | if (!isUnDefined(as_val)) 46 | { 47 | return Nan::To(as_val).ToChecked(); 48 | } 49 | return false; 50 | } 51 | 52 | int32_t MutateJS::getint32(const Local l) 53 | { 54 | const auto v = isUnDefined(l) ? 0 : Nan::To(l).ToChecked(); 55 | return v; 56 | } 57 | 58 | int64_t MutateJS::getint64(const Local query_object, const char* v) 59 | { 60 | const auto l = get(query_object, v); 61 | if (!isUnDefined(l)) 62 | { 63 | return Nan::To(l).ToChecked(); 64 | } 65 | return 0; 66 | } 67 | 68 | int64_t MutateJS::getint64(const Local l) 69 | { 70 | if (!isUnDefined(l)) 71 | { 72 | return Nan::To(l).ToChecked(); 73 | } 74 | return 0; 75 | } 76 | 77 | Local MutateJS::get_property_as_value(const Local& o, const char* v) 78 | { 79 | return get(o, v); 80 | } 81 | 82 | Local MutateJS::get(const Local o, const char* v) 83 | { 84 | if (isUnDefined(o)) 85 | { 86 | const nodeTypeFactory fact; 87 | return fact.null(); 88 | } 89 | const auto p = Nan::Get(o, Nan::New(v).ToLocalChecked()).ToLocalChecked(); 90 | return p; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/MutateJS.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // undo these tokens to use numeric_limits below 4 | 5 | #include 6 | 7 | namespace mssql 8 | { 9 | using namespace std; 10 | 11 | class MutateJS { 12 | public: 13 | static bool as_boolean(const Local& as_val); 14 | static Local get_property_as_value(const Local& o, const char* v); 15 | static Local get_property_as_value(const Local& o, const Local& v); 16 | static bool set_property_value(const Local& o, const Local& p, const Local& v); 17 | static int32_t getint32(Local query_object, const char* v); 18 | static int64_t getint64(Local query_object, const char* v); 19 | static int64_t getint64(Local l); 20 | static int32_t getint32(Local l); 21 | static Local get(Local o, const char* v); 22 | static bool getbool(Local query_object, const char* v); 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /src/NodeColumns.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | -------------------------------------------------------------------------------- /src/NullColumn.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace mssql 8 | { 9 | using namespace std; 10 | 11 | class NullColumn : public Column 12 | { 13 | public: 14 | NullColumn(int id) : Column(id) 15 | { 16 | } 17 | 18 | inline Local ToString() override 19 | { 20 | nodeTypeFactory fact; 21 | return fact.null(); 22 | } 23 | 24 | Local ToNative() override 25 | { 26 | nodeTypeFactory fact; 27 | return fact.null(); 28 | } 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/NumberColumn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace mssql 9 | { 10 | using namespace std; 11 | 12 | class NumberColumn : public Column 13 | { 14 | public: 15 | 16 | NumberColumn(int id, double d) : Column(id), value(d) 17 | { 18 | } 19 | 20 | NumberColumn(int id, shared_ptr storage) : Column(id), value((*storage->doublevec_ptr)[0]) 21 | { 22 | } 23 | 24 | inline Local ToString() override 25 | { 26 | return AsString(value); 27 | } 28 | 29 | inline Local ToNative() override 30 | { 31 | return Nan::New(value); 32 | } 33 | 34 | private: 35 | double value; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /src/OdbcConnection.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcConnection.h 3 | // Contents: Async calls to ODBC done in background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include "stdafx.h" 23 | #include 24 | #include 25 | 26 | namespace mssql 27 | { 28 | using namespace std; 29 | 30 | class OdbcStatementCache; 31 | class ResultSet; 32 | class OdbcOperation; 33 | class OperationManager; 34 | class ConnectionHandles; 35 | 36 | class OdbcConnection 37 | { 38 | public: 39 | OdbcConnection(); 40 | ~OdbcConnection(); 41 | static bool InitializeEnvironment(); 42 | bool try_begin_tran(); 43 | bool send(OdbcOperation* op) const; 44 | bool try_end_tran(SQLSMALLINT completion_type); 45 | bool try_open(shared_ptr> connection_string, int timeout); 46 | shared_ptr>> errors(void) const { return _errors; } 47 | bool TryClose(); 48 | shared_ptr getStatamentCache() { return _statements; } 49 | 50 | private: 51 | shared_ptr _statements; 52 | bool ReturnOdbcError(); 53 | bool CheckOdbcError(SQLRETURN ret); 54 | 55 | static OdbcEnvironmentHandle environment; 56 | SQLRETURN open_timeout(int timeout); 57 | shared_ptr _connectionHandles; 58 | std::mutex closeCriticalSection; 59 | 60 | // any error that occurs when a Try* function returns false is stored here 61 | // and may be retrieved via the Error function below. 62 | 63 | shared_ptr>> _errors; 64 | 65 | enum ConnectionStates 66 | { 67 | Closed, 68 | Opening, 69 | TurnOffAutoCommit, 70 | Open 71 | } connectionState; 72 | 73 | // set binary true if a binary Buffer should be returned instead of a JS string 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /src/OdbcConnectionBridge.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcConnectionBridge.h 3 | // Contents: Create (bridge) operations to be completed on background thread queue 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class OdbcConnectionBridge 32 | { 33 | public: 34 | 35 | OdbcConnectionBridge(); 36 | ~OdbcConnectionBridge(); 37 | Local close(Local callback) const; 38 | void collect(void) const ; 39 | Local begin_transaction(Local callback) const; 40 | Local commit(Local callback) const; 41 | Local rollback(Local callback) const; 42 | Local query(Local query_id, Local query_object, Local params, Local callback) const; 43 | Local query_prepared(Local query_id, Local params, Local callback) const; 44 | Local prepare(Local query_id, Local query_object, Local callback) const; 45 | Local call_procedure(Local query_id, Local query_object, Local params, Local callback) const; 46 | Local unbind_parameters(Local query_id, Local callback) const; 47 | Local cancel(Local query_id, Local callback) const ; 48 | Local polling_mode(Local query_id, Local mode, Local callback) const; 49 | Local read_row(Local query_id, Local callback) const; 50 | Local read_next_result(Local query_id, Local callback) const; 51 | Local read_column(Local query_id, Local number_rows, Local callback) const; 52 | Local open(Local connection_object, Local callback, Local backpointer) const; 53 | Local free_statement(Local query_id, Local callback) const; 54 | 55 | private: 56 | shared_ptr connection; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /src/OdbcError.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcError.cpp 3 | // Contents: Custom errors for this driver 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //-------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #include "stdafx.h" 21 | 22 | // list of msnodesql specific errors 23 | namespace mssql { 24 | 25 | // error returned when a string returns no data but it's not a NULL field 26 | // ODBC returns SQL_NO_DATA so we translate this into an error and return it to node.js 27 | OdbcError OdbcError::NODE_SQL_NO_DATA = OdbcError( "IMNOD", "No data returned", 1, 0, "", "", 0); 28 | } 29 | -------------------------------------------------------------------------------- /src/OdbcError.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcError.h 3 | // Contents: Object that represents ODBC errors 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //-------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | namespace mssql 23 | { 24 | using namespace std; 25 | 26 | class OdbcError 27 | { 28 | public: 29 | 30 | OdbcError( const char* sqlstate, const char* message, SQLINTEGER code, 31 | const int severity, const char* serverName, const char* procName, const unsigned int lineNumber 32 | ) 33 | : sqlstate( sqlstate ), message(message), code(code), 34 | severity(severity), serverName(serverName), procName(procName), lineNumber(lineNumber) 35 | { 36 | } 37 | 38 | const char* Message( void ) const 39 | { 40 | return message.c_str(); 41 | } 42 | 43 | const char* SqlState( void ) const 44 | { 45 | return sqlstate.c_str(); 46 | } 47 | 48 | SQLINTEGER Code( void ) const 49 | { 50 | return code; 51 | } 52 | 53 | int Severity( void ) const 54 | { 55 | return severity; 56 | } 57 | 58 | const char* ServerName( void ) const 59 | { 60 | return serverName.c_str(); 61 | } 62 | 63 | const char* ProcName( void ) const 64 | { 65 | return procName.c_str(); 66 | } 67 | 68 | unsigned int LineNumber( void ) const 69 | { 70 | return lineNumber; 71 | } 72 | 73 | // list of msnodesql specific errors 74 | static OdbcError NODE_SQL_NO_DATA; 75 | 76 | private: 77 | string sqlstate; 78 | string message; 79 | SQLINTEGER code; 80 | int severity; 81 | string serverName; 82 | string procName; 83 | unsigned int lineNumber; 84 | }; 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/OdbcHandle.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcHandle.h 3 | // Contents: Object to manage ODBC handles 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include "stdafx.h" 23 | #include 24 | 25 | namespace mssql 26 | { 27 | using namespace std; 28 | 29 | class OdbcHandle 30 | { 31 | public: 32 | SQLSMALLINT HandleType; 33 | OdbcHandle(SQLSMALLINT ht); 34 | virtual ~OdbcHandle(); 35 | bool alloc(); 36 | bool alloc(const OdbcHandle &parent); 37 | void free(); 38 | SQLHANDLE get() const; 39 | operator SQLHANDLE() const { return handle; } 40 | operator bool() const { return handle != nullptr; } 41 | void read_errors(shared_ptr>> & errors) const; 42 | 43 | private: 44 | std::string trim(vector&v, SQLSMALLINT len) const; 45 | void operator=(const OdbcHandle& orig) 46 | { 47 | assert(false); 48 | } 49 | 50 | SQLHANDLE handle; 51 | }; 52 | 53 | class OdbcEnvironmentHandle : public OdbcHandle 54 | { 55 | public: 56 | OdbcEnvironmentHandle() : OdbcHandle(SQL_HANDLE_ENV) 57 | { 58 | } 59 | }; 60 | 61 | class OdbcConnectionHandle : public OdbcHandle 62 | { 63 | public: 64 | OdbcConnectionHandle() : OdbcHandle(SQL_HANDLE_DBC) 65 | { 66 | } 67 | }; 68 | 69 | class OdbcStatementHandle : public OdbcHandle 70 | { 71 | public: 72 | OdbcStatementHandle(long id) : OdbcHandle(SQL_HANDLE_STMT), statementId(id) 73 | { 74 | } 75 | ~OdbcStatementHandle(); 76 | long statementId; 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /src/OdbcHelper.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcConnection.h 3 | // Contents: Async calls to ODBC done in background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | namespace mssql 23 | { 24 | #ifdef WINDOWS_BUILD 25 | #pragma intrinsic( memset ) 26 | #endif 27 | 28 | // boilerplate macro for checking if SQL_NO_DATA was returned for field data 29 | #define CHECK_ODBC_NO_DATA( r, handle ) { \ 30 | if( r == SQL_NO_DATA ) { \ 31 | error = make_shared( OdbcError::NODE_SQL_NO_DATA.SqlState(), OdbcError::NODE_SQL_NO_DATA.Message(), \ 32 | OdbcError::NODE_SQL_NO_DATA.Code() ); \ 33 | handle.Free(); \ 34 | return false; \ 35 | } } 36 | 37 | // to use with numeric_limits below 38 | #undef max 39 | 40 | namespace { 41 | 42 | // max characters within a (var)char field in SQL Server 43 | const int SQL_SERVER_MAX_STRING_SIZE = 8000; 44 | 45 | // default size to retrieve from a LOB field and we don't know the size 46 | const int LOB_PACKET_SIZE = 8192; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/OdbcOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | namespace mssql 26 | { 27 | using namespace std; 28 | using namespace v8; 29 | 30 | class OdbcConnection; 31 | class OdbcStatement; 32 | 33 | class OdbcOperation : public Nan::AsyncWorker 34 | { 35 | public: 36 | 37 | OdbcOperation(size_t query_id, Local cb); 38 | OdbcOperation(const shared_ptr& connection, size_t query_id, Local); 39 | OdbcOperation(const shared_ptr& connection, Local cb); 40 | 41 | virtual ~OdbcOperation(); 42 | virtual bool TryInvokeOdbc() = 0; 43 | virtual Local CreateCompletionArg() = 0; 44 | void getFailure(); 45 | 46 | protected: 47 | 48 | friend OdbcConnection; 49 | void Execute (); 50 | void HandleOKCallback (); 51 | shared_ptr _connection; 52 | shared_ptr _statement; 53 | Persistent _callback; 54 | Local _output_param; 55 | Local _cb; 56 | bool _can_lock; 57 | bool fetch_statement(); 58 | long _statementId; 59 | 60 | private: 61 | 62 | bool _failed; 63 | shared_ptr>> _failures; 64 | //clock_capture timer; 65 | int error(Local args[]); 66 | int success(Local args[]); 67 | }; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/OdbcOperationQueue.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace mssql 27 | { 28 | using namespace std; 29 | using namespace v8; 30 | 31 | class OdbcConnection; 32 | class OdbcStatement; 33 | class OdbcOperation; 34 | 35 | class OdbcOperationQueue 36 | { 37 | public: 38 | OdbcOperationQueue(const shared_ptr& connection); 39 | void enqueue(shared_ptr op); 40 | void dispatch(); 41 | 42 | private: 43 | shared_ptr>> _operations; 44 | mutex g_i_mutex; 45 | }; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/OdbcPreparedStatement.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcConnection.h 3 | // Contents: Async calls to ODBC done in background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include "OdbcStatement.h" 23 | #include 24 | 25 | namespace mssql 26 | { 27 | class OdbcPreparedStatement : public OdbcStatement 28 | { 29 | public: 30 | OdbcPreparedStatement(int statementId, OdbcConnectionHandle &c); 31 | virtual ~OdbcPreparedStatement(); 32 | bool TryExecute(const std::wstring& query, u_int timeout, BoundDatumSet& paramIt); 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /src/OdbcStatementCache.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcStatementCache.h 3 | // Contents: Async calls to ODBC done in background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include "stdafx.h" 23 | #include 24 | #include 25 | #include 26 | 27 | namespace mssql 28 | { 29 | using namespace std; 30 | 31 | class OdbcStatement; 32 | class ConnectionHandles; 33 | 34 | class OdbcStatementCache 35 | { 36 | public: 37 | OdbcStatementCache(const shared_ptr connectionHandles); 38 | ~OdbcStatementCache(); 39 | shared_ptr checkout(long statement_id); 40 | void checkin(long statement_id); 41 | size_t size() const { return statements.size(); } 42 | void clear(); 43 | 44 | private: 45 | shared_ptr find(long statement_id); 46 | shared_ptr store(shared_ptr statement); 47 | 48 | typedef map> map_statements_t; 49 | typedef unordered_set set_ids_t; 50 | 51 | map_statements_t statements; 52 | shared_ptr _connectionHandles; 53 | set_ids_t _spent_statements; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /src/OpenOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | OpenOperation::OpenOperation(const shared_ptr &connection, 8 | Local connection_string, 9 | const int timeout, 10 | const Local callback, 11 | const Local backpointer) 12 | : OdbcOperation(connection, callback), 13 | connectionString(js2u16(connection_string)), 14 | backpointer(Isolate::GetCurrent(), backpointer), 15 | timeout(timeout) 16 | { 17 | } 18 | 19 | OpenOperation::~OpenOperation() 20 | { 21 | backpointer.Reset(); 22 | } 23 | 24 | bool OpenOperation::TryInvokeOdbc() 25 | { 26 | return _connection->try_open(connectionString, timeout); 27 | } 28 | 29 | Local OpenOperation::CreateCompletionArg() 30 | { 31 | const auto o = Nan::New(backpointer); 32 | return o; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/OpenOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | namespace mssql 26 | { 27 | using namespace std; 28 | using namespace v8; 29 | 30 | class OdbcConnection; 31 | 32 | class OpenOperation : public OdbcOperation 33 | { 34 | shared_ptr> connectionString; 35 | Persistent backpointer; 36 | int timeout; 37 | 38 | public: 39 | OpenOperation(const shared_ptr &connection, 40 | Local connectionString, 41 | int timeout, 42 | Local callback, 43 | Local backpointer); 44 | virtual ~OpenOperation(void); 45 | bool TryInvokeOdbc() override; 46 | Local CreateCompletionArg() override; 47 | }; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/Operation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace mssql 4 | { 5 | Operation::~Operation() 6 | { 7 | //fprintf(stderr, "destruct Operation\n"); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Operation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: Operation.h 3 | // Contents: Queue calls to ODBC on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | #include 22 | #include 23 | #include 24 | 25 | namespace mssql { 26 | 27 | using namespace std; 28 | 29 | class OdbcStatement; 30 | class OperationManager; 31 | 32 | class Operation 33 | { 34 | friend class OperationManager; 35 | 36 | public: 37 | Operation() : 38 | OperationID(-1) 39 | { 40 | } 41 | 42 | virtual ~Operation(); 43 | size_t OperationID; 44 | shared_ptr mgr; 45 | 46 | private: 47 | uv_work_t work; 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /src/OperationManager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | OperationManager::OperationManager() : _id(0) 8 | { 9 | } 10 | 11 | OperationManager::~OperationManager() 12 | { 13 | // fprintf(stderr, "~OperationManager\n"); 14 | } 15 | 16 | bool OperationManager::add(shared_ptr operation_ptr) 17 | { 18 | lock_guard lock(g_i_mutex); 19 | operation_ptr->OperationID = static_cast(++_id); 20 | operations.insert(pair>(operation_ptr->OperationID, operation_ptr)); 21 | operation_ptr->work.data = operation_ptr.get(); 22 | 23 | const auto result = uv_queue_work(uv_default_loop(), &operation_ptr->work, on_background, reinterpret_cast(on_foreground)); 24 | return result == 0; 25 | } 26 | 27 | void OperationManager::on_foreground(uv_work_t* work, int x) 28 | { 29 | //auto* operation = static_cast(work->data); 30 | //fprintf(stderr, "OnForeground %llu\n ", operation->OperationID); 31 | //operation->complete_foreground(); 32 | //operation->mgr->check_in_operation(operation->OperationID); 33 | } 34 | 35 | void OperationManager::check_in_operation(const size_t id) 36 | { 37 | lock_guard lock(g_i_mutex); 38 | operations.erase(id); 39 | } 40 | 41 | void OperationManager::on_background(uv_work_t*work) 42 | { 43 | //auto* operation = static_cast(work->data); 44 | //operation->invoke_background(); 45 | } 46 | } -------------------------------------------------------------------------------- /src/OperationManager.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OperationManager.h 3 | // Contents: Queue calls to ODBC on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | #include 22 | #include 23 | #include 24 | 25 | namespace mssql { 26 | 27 | using namespace std; 28 | 29 | class Operation; 30 | 31 | /* need to think about threading with multiple active connections */ 32 | 33 | class OperationManager 34 | { 35 | typedef map> map_operations_t; 36 | 37 | public: 38 | OperationManager(); 39 | ~OperationManager(); 40 | bool add(shared_ptr operation_ptr); 41 | void check_in_operation(size_t id); 42 | shared_ptr get_operation(int id) 43 | { 44 | map_operations_t::const_iterator itr = operations.find(id); 45 | return itr->second; 46 | } 47 | 48 | private: 49 | map_operations_t operations; 50 | ssize_t _id; 51 | static void on_background(uv_work_t* work); 52 | static void on_foreground(uv_work_t* work, int x); 53 | mutex g_i_mutex; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /src/PollingModeOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | bool PollingModeOperation::TryInvokeOdbc() 8 | { 9 | if (!_statement) return false; 10 | return _statement->set_polling(_polling); 11 | } 12 | 13 | Local PollingModeOperation::CreateCompletionArg() 14 | { 15 | return Nan::Null(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/PollingModeOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class PollingModeOperation : public OdbcOperation 32 | { 33 | public: 34 | bool _polling; 35 | PollingModeOperation(shared_ptr connection, size_t queryId, bool mode, Local callback) 36 | : OdbcOperation(connection, callback) 37 | { 38 | _polling = mode; 39 | _can_lock = false; 40 | _statementId = queryId; 41 | } 42 | 43 | bool TryInvokeOdbc() override; 44 | 45 | Local CreateCompletionArg() override; 46 | }; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/PrepareOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace mssql 9 | { 10 | PrepareOperation::PrepareOperation( 11 | const shared_ptr &connection, 12 | const shared_ptr &query, 13 | const Local callback) : 14 | QueryOperation(connection, query, callback) 15 | { 16 | } 17 | 18 | bool PrepareOperation::TryInvokeOdbc() 19 | { 20 | _statement = _connection->getStatamentCache()->checkout(_statementId); 21 | if (!_statement) return false; 22 | _statement->set_polling(_query->polling()); 23 | return _statement->try_prepare(_query); 24 | } 25 | } -------------------------------------------------------------------------------- /src/PrepareOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: ProcedureOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class PrepareOperation : public QueryOperation 32 | { 33 | public: 34 | bool TryInvokeOdbc() override; 35 | PrepareOperation(const shared_ptr &connection, const shared_ptr &query, Local callback); 36 | }; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/ProcedureOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace mssql 9 | { 10 | ProcedureOperation::ProcedureOperation(const shared_ptr &connection, 11 | const shared_ptr &query, 12 | const Local callback) : 13 | QueryOperation(connection, query, callback) 14 | { 15 | } 16 | 17 | bool ProcedureOperation::TryInvokeOdbc() 18 | { 19 | _statement = _connection->getStatamentCache()->checkout(_statementId); 20 | if (!_statement) return false; 21 | _statement->set_polling(_query->polling()); 22 | return _statement->try_execute_direct(_query, _params); 23 | } 24 | 25 | Local ProcedureOperation::CreateCompletionArg() 26 | { 27 | return _statement->get_meta_value(); 28 | } 29 | } -------------------------------------------------------------------------------- /src/ProcedureOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: ProcedureOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class ProcedureOperation : public QueryOperation 32 | { 33 | public: 34 | bool TryInvokeOdbc() override; 35 | Local CreateCompletionArg() override; 36 | ProcedureOperation(const shared_ptr &connection, const shared_ptr &query, Local callback); 37 | }; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/QueryOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace mssql 11 | { 12 | QueryOperation::QueryOperation( 13 | const shared_ptr &connection, 14 | const shared_ptr &query, 15 | const Local callback) : 16 | OdbcOperation(connection, callback), 17 | _query(query), 18 | output_param_count(0) 19 | { 20 | _statementId = static_cast(_query->id()); 21 | _params = make_shared(); 22 | } 23 | 24 | QueryOperation::~QueryOperation() { 25 | // auto size = _connection->statements->size(); 26 | // std::cerr << "size = " << size << " statementId = " << _statementId << endl; 27 | } 28 | 29 | bool QueryOperation::parameter_error_to_user_callback(const uint32_t param, const char* error) const 30 | { 31 | const nodeTypeFactory fact; 32 | 33 | _params->clear(); 34 | 35 | stringstream full_error; 36 | full_error << "IMNOD: [msnodesql] Parameter " << param + 1 << ": " << error; 37 | 38 | const auto err = fact.error(full_error); 39 | const auto imn = Nan::New("IMNOD").ToLocalChecked(); 40 | Nan::Set(err, Nan::New("sqlstate").ToLocalChecked(), imn); 41 | Nan::Set(err, Nan::New("code").ToLocalChecked(), Nan::New(-1)); 42 | 43 | Local args[1]; 44 | args[0] = err; 45 | constexpr auto argc = 1; 46 | 47 | Nan::Call(Nan::New(_callback), Nan::GetCurrentContext()->Global(), argc, args); 48 | 49 | return false; 50 | } 51 | 52 | bool QueryOperation::bind_parameters(Local &node_params) const 53 | { 54 | const auto res = _params->bind(node_params); 55 | if (!res) 56 | { 57 | parameter_error_to_user_callback(_params->first_error, _params->err); 58 | } 59 | 60 | return res; 61 | } 62 | 63 | bool QueryOperation::TryInvokeOdbc() 64 | { 65 | _statement = _connection->getStatamentCache()->checkout(_statementId); 66 | if (!_statement) return false; 67 | _statement->set_polling(_query->polling()); 68 | _statement->set_numeric_string(_query->numeric_string()); 69 | const auto res = _statement->try_execute_direct(_query, _params); 70 | return res; 71 | } 72 | 73 | Local QueryOperation::CreateCompletionArg() 74 | { 75 | return _statement->get_meta_value(); 76 | } 77 | } -------------------------------------------------------------------------------- /src/QueryOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | class OdbcStatement; 31 | class BoundDatumSet; 32 | class QueryOperationParams; 33 | 34 | class QueryOperation : public OdbcOperation 35 | { 36 | public: 37 | QueryOperation( 38 | const shared_ptr &connection, 39 | const shared_ptr &query, 40 | Local callback); 41 | bool bind_parameters(Local & node_params) const; 42 | // called by BindParameters when an error occurs. It passes a node.js error to the user's callback. 43 | bool parameter_error_to_user_callback(uint32_t param, const char* error) const; 44 | bool TryInvokeOdbc() override; 45 | Local CreateCompletionArg() override; 46 | virtual ~QueryOperation(); 47 | protected: 48 | shared_ptr _query; 49 | shared_ptr _params; 50 | int output_param_count; 51 | }; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/QueryOperationParams.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "stdafx.h" 4 | 5 | namespace mssql 6 | { 7 | using namespace std; 8 | using namespace v8; 9 | 10 | /* 11 | shared_ptr> _query_string; 12 | int32_t _timeout; 13 | int32_t _query_tz_adjustment; 14 | int64_t _id; 15 | size_t _max_prepared_column_size; 16 | bool _numeric_string; 17 | bool _polling; 18 | */ 19 | QueryOperationParams::QueryOperationParams(const Local query_id, 20 | const Local query_object) : 21 | _timeout(MutateJS::getint32(query_object, "query_timeout")), 22 | _query_tz_adjustment(0), 23 | _id(MutateJS::getint32(query_id)), 24 | _max_prepared_column_size(MutateJS::getint64(query_object, "max_prepared_column_size")), 25 | _numeric_string(MutateJS::getbool(query_object, "numeric_string")), 26 | _polling(MutateJS::getbool(query_object, "query_polling")) 27 | { 28 | const auto qs = Nan::Get(query_object, Nan::New("query_str").ToLocalChecked()).ToLocalChecked(); 29 | const auto maybe_value = Nan::To(qs); 30 | const auto str = maybe_value.FromMaybe(Nan::EmptyString()); 31 | 32 | _query_string = js2u16(str); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/QueryOperationParams.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace mssql 6 | { 7 | using namespace std; 8 | using namespace v8; 9 | 10 | class QueryOperationParams 11 | { 12 | public: 13 | shared_ptr> query_string() { return _query_string; } 14 | int64_t id() { return _id; } 15 | int32_t timeout() { return _timeout; } 16 | int32_t query_tz_adjustment() { return _query_tz_adjustment; } 17 | size_t max_prepared_column_size() { return _max_prepared_column_size; } 18 | bool polling() { return _polling; } 19 | bool numeric_string() { return _numeric_string; } 20 | 21 | QueryOperationParams(Local query_id, Local query_object); 22 | private: 23 | shared_ptr> _query_string; 24 | int32_t _timeout; 25 | int32_t _query_tz_adjustment; 26 | int64_t _id; 27 | size_t _max_prepared_column_size; 28 | bool _numeric_string; 29 | bool _polling; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/QueryPreparedOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace mssql 9 | { 10 | QueryPreparedOperation::QueryPreparedOperation( 11 | const shared_ptr &connection, 12 | const size_t query_id, const u_int timeout, 13 | const Local callback) : 14 | OdbcOperation(connection, callback), 15 | _timeout(timeout), 16 | _output_param_count(0) 17 | { 18 | _statementId = static_cast(query_id); 19 | _params = make_shared(); 20 | } 21 | 22 | bool QueryPreparedOperation::parameter_error_to_user_callback(const uint32_t param, const char* error) const 23 | { 24 | const nodeTypeFactory fact; 25 | 26 | _params->clear(); 27 | 28 | stringstream full_error; 29 | full_error << "IMNOD: [msnodesql] Parameter " << param + 1 << ": " << error; 30 | 31 | const auto err = fact.error(full_error); 32 | const auto imn = fact.new_string("IMNOD"); 33 | MutateJS::set_property_value(err, fact.new_string("sqlstate"), imn); 34 | MutateJS::set_property_value(err, fact.new_string("code"), Nan::New(-1)); 35 | 36 | Local args[1]; 37 | args[0] = err; 38 | constexpr auto argc = 1; 39 | 40 | Nan::Call(Nan::New(_callback), Nan::GetCurrentContext()->Global(), argc, args); 41 | return false; 42 | } 43 | 44 | bool QueryPreparedOperation::bind_parameters(Local &node_params) const 45 | { 46 | const auto res = _params->bind(node_params); 47 | if (!res) 48 | { 49 | parameter_error_to_user_callback(_params->first_error, _params->err); 50 | } 51 | 52 | return res; 53 | } 54 | 55 | bool QueryPreparedOperation::TryInvokeOdbc() 56 | { 57 | if (_statement == nullptr) return false; 58 | return _statement->bind_fetch(_params); 59 | } 60 | 61 | Local QueryPreparedOperation::CreateCompletionArg() 62 | { 63 | return _statement->get_meta_value(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/QueryPreparedOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | class OdbcStatement; 31 | class BoundDatumSet; 32 | 33 | class QueryPreparedOperation : public OdbcOperation 34 | { 35 | public: 36 | QueryPreparedOperation(const shared_ptr &connection, 37 | size_t query_id, u_int timeout, 38 | Local callback); 39 | bool bind_parameters(Local & node_params) const; 40 | // called by BindParameters when an error occurs. It passes a node.js error to the user's callback. 41 | bool parameter_error_to_user_callback(uint32_t param, const char* error) const; 42 | bool TryInvokeOdbc() override; 43 | Local CreateCompletionArg() override; 44 | 45 | protected: 46 | 47 | u_int _timeout; 48 | shared_ptr _params; 49 | int _output_param_count; 50 | }; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/ReadColumnOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | bool ReadColumnOperation::TryInvokeOdbc() 8 | { 9 | if (!_statement) return false; 10 | return _statement->try_read_columns(this->_number_rows); 11 | } 12 | 13 | Local ReadColumnOperation::CreateCompletionArg() 14 | { 15 | return _statement->get_column_values(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/ReadColumnOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class ReadColumnOperation : public OdbcOperation 32 | { 33 | int _number_rows; 34 | 35 | public: 36 | 37 | ReadColumnOperation(shared_ptr connection, size_t queryId, int number_rows, Local callback) 38 | : OdbcOperation(connection, callback), 39 | _number_rows(number_rows) 40 | { 41 | _statementId = queryId; 42 | } 43 | 44 | bool TryInvokeOdbc() override; 45 | 46 | Local CreateCompletionArg() override; 47 | }; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/ReadNextResultOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | bool ReadNextResultOperation::TryInvokeOdbc() 8 | { 9 | if (!_statement) return false; 10 | preRowCount = _statement->get_row_count(); 11 | const auto res = _statement->try_read_next_result(); 12 | postRowCount = _statement->get_row_count(); 13 | return res; 14 | } 15 | 16 | Local ReadNextResultOperation::CreateCompletionArg() 17 | { 18 | const auto more_meta = Nan::New(); 19 | Nan::Set(more_meta, Nan::New("endOfResults").ToLocalChecked(), _statement->handle_end_of_results()); 20 | Nan::Set(more_meta, Nan::New("endOfRows").ToLocalChecked(), _statement->end_of_rows()); 21 | Nan::Set(more_meta, Nan::New("meta").ToLocalChecked(), _statement->get_meta_value()); 22 | Nan::Set(more_meta, Nan::New("preRowCount").ToLocalChecked(), Nan::New(static_cast(preRowCount))); 23 | Nan::Set(more_meta, Nan::New("rowCount").ToLocalChecked(), Nan::New(static_cast(postRowCount))); 24 | 25 | return more_meta; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ReadNextResultOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class ReadNextResultOperation : public OdbcOperation 32 | { 33 | public: 34 | ReadNextResultOperation(shared_ptr connection, size_t queryId, Local callback) 35 | : OdbcOperation(connection, callback), preRowCount(-1), postRowCount(-1) 36 | { 37 | _statementId = queryId; 38 | } 39 | 40 | bool TryInvokeOdbc() override; 41 | 42 | Local CreateCompletionArg() override; 43 | SQLLEN preRowCount; 44 | SQLLEN postRowCount; 45 | }; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/ResultSet.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: ResultSet.h 3 | // Contents: ResultSet object that holds metadata and current column to return to Javascript 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include 23 | #include "Column.h" 24 | 25 | namespace mssql 26 | { 27 | using namespace std; 28 | 29 | class ResultSet 30 | { 31 | 32 | public: 33 | typedef vector> t_row; 34 | struct ColumnDefinition 35 | { 36 | vector name; 37 | SQLULEN columnSize; 38 | SQLSMALLINT dataType; 39 | string dataTypeName; 40 | SQLSMALLINT decimalDigits; 41 | SQLSMALLINT nullable; 42 | string udtTypeName; 43 | }; 44 | 45 | ResultSet(int num_columns) 46 | : _row_count(0), 47 | _end_of_rows(true) 48 | { 49 | _metadata.resize(num_columns); 50 | } 51 | 52 | ColumnDefinition & get_meta_data(int column) 53 | { 54 | return _metadata[column]; 55 | } 56 | 57 | size_t get_column_count() const 58 | { 59 | return _metadata.size(); 60 | } 61 | void start_results() 62 | { 63 | _rows.clear(); 64 | } 65 | Local meta_to_value(); 66 | void add_column(size_t row_id, const shared_ptr & column); 67 | shared_ptr get_column(size_t row_id, size_t id) const; 68 | size_t get_result_count() const 69 | { 70 | return _rows.size(); 71 | } 72 | 73 | SQLLEN row_count() const 74 | { 75 | return _row_count; 76 | } 77 | 78 | bool EndOfRows() const 79 | { 80 | return _end_of_rows; 81 | } 82 | 83 | private: 84 | static Local get_entry(const ColumnDefinition & definition); 85 | vector _metadata; 86 | 87 | SQLLEN _row_count; 88 | bool _end_of_rows; 89 | vector _rows; 90 | 91 | friend class OdbcStatement; 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /src/StringColumn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Column.h" 8 | #include "Utility.h" 9 | #include "BoundDatumHelper.h" 10 | #include 11 | 12 | namespace mssql 13 | { 14 | using namespace std; 15 | 16 | class StringColumn : public Column 17 | { 18 | public: 19 | virtual ~StringColumn() 20 | { 21 | } 22 | 23 | StringColumn(int id, shared_ptr s, size_t size) 24 | : 25 | Column(id), 26 | size(size), 27 | storage(s->uint16vec_ptr) 28 | { 29 | } 30 | 31 | StringColumn(int id, shared_ptr s, size_t size) 32 | : 33 | Column(id), 34 | size(size), 35 | storage(s) 36 | { 37 | } 38 | 39 | StringColumn(int id, shared_ptr s, size_t offset, size_t size) 40 | : 41 | Column(id), 42 | size(size), 43 | storage(s), 44 | offset(offset) 45 | { 46 | } 47 | 48 | inline Local ToString() override 49 | { 50 | return ToValue(); 51 | } 52 | 53 | inline Local ToNative() override 54 | { 55 | auto sptr = storage->data(); 56 | auto s = Nan::Encode(sptr + offset, size * 2, Nan::UCS2); 57 | return s; 58 | } 59 | 60 | private: 61 | size_t size; 62 | shared_ptr storage; 63 | size_t offset = 0; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/UnbindOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | namespace mssql 6 | { 7 | bool UnbindOperation::TryInvokeOdbc() 8 | { 9 | if (_statement == nullptr) return false; 10 | return true; 11 | } 12 | 13 | Local UnbindOperation::CreateCompletionArg() 14 | { 15 | const auto a = _statement->unbind_params(); 16 | const auto ret = a->Clone(); 17 | return ret; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/UnbindOperation.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: OdbcOperation.h 3 | // Contents: ODBC Operation objects called on background thread 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #include "OdbcOperation.h" 23 | 24 | namespace mssql 25 | { 26 | using namespace std; 27 | using namespace v8; 28 | 29 | class OdbcConnection; 30 | 31 | class UnbindOperation : public OdbcOperation 32 | { 33 | public: 34 | 35 | UnbindOperation(shared_ptr connection, size_t queryId, Local callback) 36 | : OdbcOperation(connection, callback) 37 | { 38 | _statementId = queryId; 39 | } 40 | 41 | bool TryInvokeOdbc() override; 42 | 43 | Local CreateCompletionArg() override; 44 | }; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/Utility.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: Utility.h 3 | // Contents: Utility functions used in Microsoft Driver for Node.js for SQL Server 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace mssql 28 | { 29 | using namespace std; 30 | using namespace v8; 31 | 32 | shared_ptr> js2u16(Local str); 33 | vector wstr2wcvec(const wstring & s); 34 | vector str2wcvec(const string & cs); 35 | string swcvec2str(vector &v, size_t l); 36 | string w2sqlc(const wstring & s); 37 | wstring s2ws(const string & s); 38 | wstring FromV8String(Local input); 39 | 40 | void encode_numeric_struct(double v, int precision, int upscale_limit, SQL_NUMERIC_STRUCT & numeric); 41 | double decode_numeric_struct(const SQL_NUMERIC_STRUCT & numeric); 42 | 43 | struct nodeTypeFactory 44 | { 45 | static const int64_t NANOSECONDS_PER_MS = 1000000; 46 | 47 | Isolate *isolate; 48 | 49 | nodeTypeFactory(); 50 | Local new_long(int64_t i) const; 51 | Local new_int32(int32_t i) const; 52 | Local new_int64(int64_t i) const; 53 | Local new_number() const; 54 | Local new_string(const char *cstr) const; 55 | Local new_string(const char *cstr, int size) const; 56 | Local new_array() const; 57 | Local new_array(int count) const; 58 | Local new_buffer(int size) const; 59 | Local error(const stringstream &full_error) const; 60 | Local error(const char* full_error) const; 61 | Local new_date() const; 62 | Local new_date(double milliseconds, int32_t nanoseconds_delta) const; 63 | Local global() const; 64 | Local null() const; 65 | Local undefined() const; 66 | void throwError(const char * err) const; 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /src/addon.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Connection.h" 3 | 4 | void InitAll(v8::Local exports) { 5 | mssql::Connection::Init(exports); 6 | } 7 | 8 | NAN_MODULE_WORKER_ENABLED(addon, InitAll) -------------------------------------------------------------------------------- /src/addon.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimelordUK/node-sqlserver-v8/4b1c3a32b39a2149e417c514aa8fe91183400f35/src/addon.h -------------------------------------------------------------------------------- /src/sqlncli-linux.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | typedef unsigned char DBBIT; 9 | -------------------------------------------------------------------------------- /src/stdafx.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: sfdafx.cpp 3 | // Contents: Precompiled Header 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #include "stdafx.h" 21 | -------------------------------------------------------------------------------- /src/stdafx.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------------------------------------------------- 2 | // File: stdafx.h 3 | // Contents: Precompiled header 4 | // 5 | // Copyright Microsoft Corporation and contributors 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // 10 | // You may obtain a copy of the License at: 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | #pragma once 21 | 22 | #ifdef LINUX_BUILD 23 | #define GCC_VERSION (__GNUC__ * 10000 \ 24 | + __GNUC_MINOR__ * 100 \ 25 | + __GNUC_PATCHLEVEL__) 26 | #if GCC_VERSION > 90200 27 | #pragma GCC diagnostic ignored "-Wcast-function-type" 28 | #endif 29 | #endif 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #ifdef LINUX_BUILD 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #endif 44 | 45 | #include 46 | 47 | #ifdef WINDOWS_BUILD 48 | #include // for critical section until xplatform 49 | #endif 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include "Utility.h" 60 | #include "OdbcError.h" 61 | #include "OdbcHandle.h" 62 | 63 | // #define interface struct // for the COM interfaces in sqlncli.h and to avoid including extra files 64 | 65 | #ifdef WINDOWS_BUILD 66 | #include "sqlncli.h" // SQL Server specific constants 67 | #endif 68 | 69 | 70 | 71 | // default values filled in for a JS date object when retrieving a SQL Server time field 72 | // There is no default JS date when only a time is furnished, so we are using the SQL Server 73 | // defaults, which is Jan 1, 1900. 74 | const int SQL_SERVER_DEFAULT_YEAR = 1900; 75 | const int SQL_SERVER_DEFAULT_MONTH = 1; // JS months are 0 based, SQL Server months are 1 based 76 | const int SQL_SERVER_DEFAULT_DAY = 1; 77 | 78 | const int JS_DEFAULT_YEAR = 1970; 79 | 80 | #define ErrorIf(x) if (x) goto Error; 81 | -------------------------------------------------------------------------------- /test/connect.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* globals describe it */ 4 | 5 | const assert = require('chai').assert 6 | const expect = require('chai').expect 7 | const { TestEnv } = require('./env/test-env') 8 | const env = new TestEnv() 9 | const connectionString = env.connectionString 10 | 11 | describe('connection', function () { 12 | this.timeout(10000) 13 | 14 | this.beforeEach(done => { 15 | env.open().then(() => done()) 16 | }) 17 | 18 | this.afterEach(done => { 19 | env.close().then(() => done()) 20 | }) 21 | 22 | it('connection closes OK in sequence with query', done => { 23 | env.sql.open(connectionString, 24 | (err, conn) => { 25 | const expected = [{ 26 | n: 1 27 | }] 28 | assert(err === null || err === false) 29 | conn.query('SELECT 1 as n', (err, results) => { 30 | assert.ifError(err) 31 | expect(results).to.deep.equal(expected) 32 | conn.close(() => { 33 | done() 34 | }) 35 | }) 36 | }) 37 | }) 38 | 39 | it('verify closed connection throws an exception', done => { 40 | env.sql.open(connectionString, (err, conn) => { 41 | assert(err === null || err === false) 42 | conn.close(() => { 43 | expect(() => { 44 | conn.query('SELECT 1', err => { 45 | assert.ifError(err) 46 | }) 47 | }).throws('[msnodesql] Connection is closed.') 48 | done() 49 | }) 50 | }) 51 | }) 52 | 53 | it('verify connection is not closed prematurely until a query is complete', done => { 54 | env.sql.open(connectionString, (err, conn) => { 55 | assert(err === null || err === false) 56 | const stmt = conn.queryRaw('select 1') 57 | stmt.on('meta', () => { 58 | }) 59 | stmt.on('column', (c, d) => { 60 | assert(c === 0 && d === 1) 61 | }) 62 | stmt.on('error', err => { 63 | assert(err === null || err === false) 64 | }) 65 | stmt.on('row', r => { 66 | assert(r === 0) 67 | conn.close(() => { 68 | done() 69 | }) 70 | }) 71 | }) 72 | }) 73 | 74 | it('verify that close immediately flag only accepts booleans', done => { 75 | env.sql.open(connectionString, (err, conn) => { 76 | assert(err === null || err === false) 77 | expect(() => { 78 | conn.close('SELECT 1', err => { 79 | assert(err === null || err === false) 80 | }) 81 | }).throws('[msnodesql] Invalid parameters passed to close.') 82 | 83 | conn.close(() => { 84 | done() 85 | }) 86 | }) 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /test/env/bcp-entry.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | 3 | class BcpEntry { 4 | constructor (env, definition, factory, tester) { 5 | this.definition = definition 6 | this.factory = factory 7 | this.tester = tester 8 | this.env = env 9 | } 10 | 11 | async runner (count) { 12 | const helper = this.env.bulkTableTest(this.definition) 13 | const expected = [] 14 | const rows = count || 5000 15 | for (let i = 0; i < rows; ++i) { 16 | expected.push(this.factory(i)) 17 | } 18 | const theConnection = this.env.theConnection 19 | theConnection.setUseUTC(true) 20 | const table = await helper.create() 21 | table.setUseBcp(true) 22 | await table.promises.insert(expected) 23 | const res = await theConnection.promises.query(`select count(*) as rows from ${this.definition.tableName}`) 24 | assert.deepStrictEqual(res.first[0].rows, rows) 25 | const top = await theConnection.promises.query(`select top 100 * from ${this.definition.tableName}`) 26 | const toCheck = expected.slice(0, 100) 27 | if (this.tester) { 28 | this.tester(top.first, toCheck) 29 | } else { 30 | assert.deepStrictEqual(top.first, toCheck) 31 | } 32 | } 33 | } 34 | 35 | module.exports = { 36 | BcpEntry 37 | } 38 | -------------------------------------------------------------------------------- /test/env/builder-checker.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | 3 | class BuilderChecker { 4 | constructor (builder, env) { 5 | this.builder = builder 6 | this.env = env 7 | } 8 | 9 | async select (table, compare, vec, keys) { 10 | const s1 = await table.promises.select(keys) 11 | assert.deepStrictEqual(vec.length, s1.length) 12 | for (let i = 0; i < vec.length; ++i) { 13 | const lhs = vec[i] 14 | const rhs = s1[i] 15 | if (compare) { 16 | compare(lhs, rhs) 17 | } else { 18 | assert.deepStrictEqual(lhs, rhs) 19 | } 20 | } 21 | } 22 | 23 | getKeys (table, vec) { 24 | const builder = this.builder 25 | const primaryCols = builder.primaryColumns 26 | return vec.map(elem => { 27 | return primaryCols.reduce(function (obj, column) { 28 | if (Object.prototype.hasOwnProperty.call(elem, column.name)) { 29 | obj[column.name] = elem[column.name] 30 | } 31 | return obj 32 | }, {}) 33 | }) 34 | } 35 | 36 | make (makeOne, n) { 37 | n = n || 20 38 | const vec = [] 39 | for (let i = 0; i < n; ++i) { 40 | vec.push(makeOne(i)) 41 | } 42 | return vec 43 | } 44 | 45 | async checkTvp (makeOne, compare, rows) { 46 | rows = rows || 5 47 | const theConnection = this.env.theConnection 48 | const vec = this.make(makeOne, rows) 49 | const builder = this.builder 50 | const table = builder.toTable() 51 | await builder.drop() 52 | await builder.create() 53 | const procName = builder.insertTvpProcedureName 54 | const dropType = builder.dropTypeSql 55 | const userTypeSql = builder.userTypeTableSql 56 | const tvpProcSql = builder.insertProcedureTvpSql 57 | const env = this.env 58 | const prochelper = env.procTest({ 59 | name: procName, 60 | sql: tvpProcSql 61 | }) 62 | 63 | const promises = theConnection.promises 64 | await prochelper.drop() 65 | await promises.query(dropType) 66 | await promises.query(userTypeSql) 67 | await promises.query(tvpProcSql) 68 | const tvpTable = await promises.getUserTypeTable(builder.typeName) 69 | tvpTable.addRowsFromObjects(vec) 70 | const tp = this.env.sql.TvpFromTable(tvpTable) 71 | await promises.callProc(procName, [tp]) 72 | tvpTable.rows = [] 73 | const keys = this.getKeys(table, vec) 74 | await this.select(table, compare, vec, keys) 75 | } 76 | 77 | async check (makeOne, compare) { 78 | const builder = this.builder 79 | const table = builder.toTable() 80 | await builder.drop() 81 | await builder.create() 82 | const vec = this.make(makeOne) 83 | 84 | await table.promises.insert(vec) 85 | const keys = this.getKeys(table, vec) 86 | await this.select(table, compare, vec, keys) 87 | await builder.drop() 88 | } 89 | } 90 | 91 | module.exports = { 92 | BuilderChecker 93 | } 94 | -------------------------------------------------------------------------------- /test/env/bulk-table-test.js: -------------------------------------------------------------------------------- 1 | class BulkTableTest { 2 | constructor (c, def) { 3 | function where (list, primitive) { 4 | return list.reduce((agg, latest) => { 5 | if (primitive(latest)) { 6 | agg.push(latest) 7 | } 8 | return agg 9 | }, []) 10 | } 11 | const tableName = def.tableName 12 | const columns = def.columns.map(e => `${e.name} ${e.type}`).join(', ') 13 | const insertColumnNames = where(def.columns, c => { 14 | return !c.type.includes('identity') 15 | }).map(e => `${e.name}`).join(', ') 16 | const columnNames = def.columns.map(e => `${e.name}`).join(', ') 17 | const dropTableSql = `IF OBJECT_ID('${tableName}', 'U') IS NOT NULL DROP TABLE ${tableName};` 18 | const createTableSql = `CREATE TABLE ${tableName} (${columns})` 19 | const clusteredSql = `CREATE CLUSTERED INDEX IX_${tableName} ON ${tableName}(id)` 20 | const insertSql = `INSERT INTO ${tableName} (${insertColumnNames}) VALUES ` 21 | const selectSql = `SELECT ${columnNames} FROM ${tableName}` 22 | const trucateSql = `TRUNCATE TABLE ${tableName}` 23 | const paramsSql = `(${def.columns.map(_ => '?').join(', ')})` 24 | 25 | this.definition = def 26 | this.theConnection = c 27 | this.dropTableSql = dropTableSql 28 | this.createTableSql = createTableSql 29 | this.clusteredSql = clusteredSql 30 | this.selectSql = selectSql 31 | this.insertSql = insertSql 32 | this.truncateSql = trucateSql 33 | this.tableName = def.tableName 34 | this.paramsSql = paramsSql 35 | this.insertParamsSql = `${insertSql} ${paramsSql}` 36 | } 37 | 38 | async drop () { 39 | await this.theConnection.promises.query(this.dropTableSql) 40 | } 41 | 42 | async create () { 43 | const promises = this.theConnection.promises 44 | await promises.query(this.dropTableSql) 45 | await promises.query(this.createTableSql) 46 | return await promises.getTable(this.tableName) 47 | } 48 | } 49 | 50 | module.exports = { 51 | BulkTableTest 52 | } 53 | -------------------------------------------------------------------------------- /test/env/data/SampleJPGImage_50kbmb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimelordUK/node-sqlserver-v8/4b1c3a32b39a2149e417c514aa8fe91183400f35/test/env/data/SampleJPGImage_50kbmb.jpg -------------------------------------------------------------------------------- /test/env/encrypt-helper.js: -------------------------------------------------------------------------------- 1 | class EncryptHelper { 2 | constructor (key, algo) { 3 | this.encrptKey = key || 'CEK_Auto1' 4 | this.encrptAlgo = algo || 'AEAD_AES_256_CBC_HMAC_SHA_256' 5 | this.fieldWithEncrpyt = `ENCRYPTED WITH 6 | (COLUMN_ENCRYPTION_KEY = [${this.encrptKey}], 7 | ENCRYPTION_TYPE = Deterministic, 8 | ALGORITHM = '${this.encrptAlgo}')` 9 | this.txtWithEncrypt = `COLLATE Latin1_General_BIN2 ${this.fieldWithEncrpyt}` 10 | } 11 | } 12 | 13 | module.exports = { 14 | EncryptHelper 15 | } 16 | -------------------------------------------------------------------------------- /test/env/json/points.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [ 11 | -0.19535064697265625, 12 | 51.509249951770364 13 | ], 14 | [ 15 | -0.19148826599121094, 16 | 51.5100245354003 17 | ], 18 | [ 19 | -0.185093879699707, 20 | 51.510425176938 21 | ], 22 | [ 23 | -0.17569541931152344, 24 | 51.511600371790095 25 | ], 26 | [ 27 | -0.17045974731445312, 28 | 51.51226808263225 29 | ], 30 | [ 31 | -0.15844345092773438, 32 | 51.51344322994464 33 | ], 34 | [ 35 | -0.15672683715820312, 36 | 51.5102649207457 37 | ], 38 | [ 39 | -0.15110492706298828, 40 | 51.50569738228904 41 | ], 42 | [ 43 | -0.15106201171874997, 44 | 51.50404120260676 45 | ], 46 | [ 47 | -0.15097618103027344, 48 | 51.50337337118891 49 | ], 50 | [ 51 | -0.15552520751953125, 52 | 51.50251853269236 53 | ], 54 | [ 55 | -0.16028881072998044, 56 | 51.50193082192403 57 | ], 58 | [ 59 | -0.16427993774414062, 60 | 51.501690392607 61 | ], 62 | [ 63 | -0.17127513885498047, 64 | 51.501877393296546 65 | ], 66 | [ 67 | -0.17711162567138672, 68 | 51.501556820216095 69 | ], 70 | [ 71 | -0.18097400665283203, 72 | 51.50144996202149 73 | ], 74 | [ 75 | -0.1877117156982422, 76 | 51.502358248689035 77 | ], 78 | [ 79 | -0.19093036651611328, 80 | 51.50203767899114 81 | ], 82 | [ 83 | -0.19230365753173828, 84 | 51.50412134171919 85 | ], 86 | [ 87 | -0.19329071044921875, 88 | 51.50436175821088 89 | ], 90 | [ 91 | -0.19466400146484375, 92 | 51.506792564820394 93 | ] 94 | ] 95 | } 96 | } 97 | ] 98 | } -------------------------------------------------------------------------------- /test/env/proc-helper.js: -------------------------------------------------------------------------------- 1 | class ProcTest { 2 | dropProcedureSql 3 | 4 | constructor (theConnection, def) { 5 | this.def = def 6 | this.theConnection = theConnection 7 | this.dropProcedureSql = `IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND OBJECT_ID = OBJECT_ID('${def.name}')) 8 | begin drop PROCEDURE ${def.name} end ` 9 | } 10 | 11 | async create () { 12 | try { 13 | const promises = this.theConnection.promises 14 | await promises.query(this.dropProcedureSql) 15 | await promises.query(this.def.sql || this.def.def) 16 | } catch (e) { 17 | console.log(e) 18 | } 19 | } 20 | 21 | async drop () { 22 | try { 23 | const promises = this.theConnection.promises 24 | await promises.query(this.dropProcedureSql) 25 | } catch (e) { 26 | console.log(e) 27 | } 28 | } 29 | 30 | query (sql, params, options) { 31 | return this.theConnection.promises.query(sql, params, options) 32 | } 33 | 34 | call (o, options) { 35 | return this.theConnection.promises.callProc(this.def.name, o, options) 36 | } 37 | } 38 | 39 | module.exports = { 40 | ProcTest 41 | } 42 | -------------------------------------------------------------------------------- /test/env/table-helper.js: -------------------------------------------------------------------------------- 1 | class TableHelper { 2 | constructor (theConnection) { 3 | const tableName = 'test_bulk_table' 4 | const dropTableSql = `IF OBJECT_ID('${tableName}', 'U') IS NOT NULL 5 | DROP TABLE ${tableName};` 6 | 7 | const createTableSql = `CREATE TABLE ${tableName} ( 8 | id INT PRIMARY KEY, 9 | col_a int, 10 | col_b int, 11 | col_c int, 12 | col_d int, 13 | col_e int, 14 | col_f int, 15 | );` 16 | 17 | function getVec (count) { 18 | const v = [] 19 | for (let i = 0; i < count; ++i) { 20 | v.push({ 21 | id: i, 22 | col_a: (i + 1) * 10 + i, 23 | col_b: (i + 1) * 100 + i, 24 | col_c: (i + 1) * 1000 + i, 25 | col_d: (i + 1) * 10000 + i, 26 | col_e: (i + 1) * 100000 + i, 27 | col_f: (i + 1) * 1000000 + i 28 | }) 29 | } 30 | return v 31 | } 32 | 33 | async function create () { 34 | const promises = theConnection.promises 35 | await promises.query(dropTableSql) 36 | await promises.query(createTableSql) 37 | return await theConnection.promises.getTable(tableName) 38 | } 39 | 40 | this.create = create 41 | this.getVec = getVec 42 | } 43 | } 44 | 45 | module.exports = { 46 | TableHelper 47 | } 48 | -------------------------------------------------------------------------------- /test/env/type-table-helper.js: -------------------------------------------------------------------------------- 1 | class TypeTableHelper { 2 | constructor (theConnection, sqlType) { 3 | const tableName = 'test_bulk_table' 4 | const dropTableSql = `IF OBJECT_ID('${tableName}', 'U') IS NOT NULL 5 | DROP TABLE ${tableName};` 6 | 7 | const createTableSql = `CREATE TABLE ${tableName} ( 8 | id INT PRIMARY KEY, 9 | col_a ${sqlType} 10 | );` 11 | 12 | function getVec (count, generator) { 13 | const v = [] 14 | for (let i = 0; i < count; ++i) { 15 | const val = generator(i) 16 | v.push({ 17 | id: i, 18 | col_a: val 19 | }) 20 | } 21 | return v 22 | } 23 | 24 | async function create () { 25 | await theConnection.promises.query(dropTableSql) 26 | await theConnection.promises.query(createTableSql) 27 | return await theConnection.promises.getTable(tableName) 28 | } 29 | 30 | this.create = create 31 | this.getVec = getVec 32 | } 33 | } 34 | 35 | module.exports = { 36 | TypeTableHelper 37 | } 38 | -------------------------------------------------------------------------------- /test/json.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* globals describe it */ 4 | const chai = require('chai') 5 | const expect = chai.expect 6 | const { TestEnv } = require('./env/test-env') 7 | const env = new TestEnv() 8 | 9 | describe('json', function () { 10 | this.timeout(30000) 11 | 12 | this.beforeEach(done => { 13 | env.open().then(() => { 14 | done() 15 | }).catch(e => { 16 | console.error(e) 17 | }) 18 | }) 19 | 20 | this.afterEach(done => { 21 | env.close().then(() => { done() }).catch(e => { 22 | console.error(e) 23 | }) 24 | }) 25 | 26 | function insertRec (p, id, element) { 27 | const txt = JSON.stringify(element, null, 4) 28 | return p.promises.call({ 29 | ID: id++, 30 | json: txt 31 | }).then(() => { 32 | return txt 33 | }) 34 | } 35 | 36 | it('use proc to insert a JSON array and bulk parse on server', async function handler () { 37 | const tableName = 'employeeJson' 38 | const procName = 'AddUpdateEmployeeJsonRecord' 39 | const procNameJson = 'ParseJsonArray' 40 | const h = env.jsonHelper(tableName, procName, procNameJson) 41 | await h.create() 42 | const parsedJSON = env.helper.getJSON() 43 | const promises = env.theConnection.promises 44 | const json = JSON.stringify(parsedJSON, null, 4) 45 | const res = await promises.callProc(procNameJson, { json }) 46 | expect(Array.isArray(res.first)) 47 | const expected = parsedJSON.map(r => { 48 | const { OrganizationNode, ...rest } = r 49 | return rest 50 | }) 51 | expect(res.first).to.deep.equals(expected) 52 | }) 53 | 54 | it('use proc to insert a JSON based complex object', async function handler () { 55 | const tableName = 'employeeJson' 56 | const procName = 'AddUpdateEmployeeJsonRecord' 57 | const procNameJson = 'ParseJsonArray' 58 | const h = env.jsonHelper(tableName, procName, procNameJson) 59 | await h.create() 60 | const parsedJSON = env.helper.getJSON() 61 | const promises = env.theConnection.promises 62 | const p = await promises.getProc(procName) 63 | let id = 0 64 | const inserts = parsedJSON.map(r => insertRec(p, id++, r)) 65 | const expected = await Promise.all(inserts) 66 | const selectedRecords = await promises.query(`select * from ${tableName} order by id asc`) 67 | const selected = selectedRecords.first.map(rec => rec.json) 68 | expect(selected).to.deep.equals(expected) 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --timeout 5000 2 | --reporter spec 3 | --recursive 4 | -------------------------------------------------------------------------------- /test/sql/bulkColumn.sql: -------------------------------------------------------------------------------- 1 | SET ANSI_NULLS ON 2 | GO 3 | 4 | SET QUOTED_IDENTIFIER ON 5 | GO 6 | 7 | SET ANSI_PADDING ON 8 | GO 9 | 10 | CREATE TABLE [dbo].[]( 11 | [pkid] [int] NOT NULL, 12 | [] NULL 13 | PRIMARY KEY (pkid) 14 | ) ON [PRIMARY] 15 | 16 | GO 17 | 18 | SET ANSI_PADDING OFF 19 | GO -------------------------------------------------------------------------------- /test/sql/bulkTest.sql: -------------------------------------------------------------------------------- 1 | SET ANSI_NULLS ON 2 | GO 3 | 4 | SET QUOTED_IDENTIFIER ON 5 | GO 6 | 7 | SET ANSI_PADDING ON 8 | GO 9 | 10 | CREATE TABLE [dbo].[]( 11 | [pkid] [int] NOT NULL, 12 | [num1] [int] NOT NULL, 13 | [num2] [int] NOT NULL, 14 | [num3] [int] NULL, 15 | [st] [varchar](100) NULL, 16 | PRIMARY KEY (pkid) 17 | ) ON [PRIMARY] 18 | 19 | GO 20 | 21 | SET ANSI_PADDING OFF 22 | GO -------------------------------------------------------------------------------- /test/sql/employee.sql: -------------------------------------------------------------------------------- 1 | SET ANSI_NULLS ON 2 | GO 3 | 4 | SET QUOTED_IDENTIFIER ON 5 | GO 6 | 7 | CREATE TABLE []( 8 | [BusinessEntityID] int primary key, 9 | [NationalIDNumber] [nvarchar](15) NOT NULL, 10 | [LoginID] [nvarchar](256) NOT NULL, 11 | [OrganizationNode] [hierarchyid] NULL, 12 | [OrganizationLevel] AS ([OrganizationNode].[GetLevel]()), 13 | [JobTitle] [nvarchar](50) NOT NULL, 14 | [BirthDate] [date] NOT NULL, 15 | [MaritalStatus] [nchar](1) NOT NULL, 16 | [Gender] [nchar](1) NOT NULL, 17 | [HireDate] [date] NOT NULL, 18 | [SalariedFlag] [bit] NOT NULL, 19 | [VacationHours] [smallint] NOT NULL, 20 | [SickLeaveHours] [smallint] NOT NULL, 21 | [CurrentFlag] [bit] NOT NULL, 22 | [rowguid] [uniqueidentifier] ROWGUIDCOL NOT NULL, 23 | [ModifiedDate] [datetime] NOT NULL 24 | ) 25 | GO 26 | 27 | ALTER TABLE [] ADD CONSTRAINT [DF_Employee_SalariedFlag] DEFAULT ((1)) FOR [SalariedFlag] 28 | GO 29 | 30 | ALTER TABLE [] ADD CONSTRAINT [DF_Employee_VacationHours] DEFAULT ((0)) FOR [VacationHours] 31 | GO 32 | 33 | ALTER TABLE [] ADD CONSTRAINT [DF_Employee_SickLeaveHours] DEFAULT ((0)) FOR [SickLeaveHours] 34 | GO 35 | 36 | ALTER TABLE [] ADD CONSTRAINT [DF_Employee_CurrentFlag] DEFAULT ((1)) FOR [CurrentFlag] 37 | GO 38 | 39 | ALTER TABLE [] ADD CONSTRAINT [DF_Employee_rowguid] DEFAULT (newid()) FOR [rowguid] 40 | GO 41 | 42 | ALTER TABLE [] ADD CONSTRAINT [DF_Employee_ModifiedDate] DEFAULT (getdate()) FOR [ModifiedDate] 43 | GO 44 | 45 | ALTER TABLE [] WITH CHECK ADD CONSTRAINT [CK_Employee_BirthDate] CHECK (([BirthDate]>='1930-01-01' AND [BirthDate]<=dateadd(year,(-18),getdate()))) 46 | GO 47 | 48 | ALTER TABLE [] CHECK CONSTRAINT [CK_Employee_BirthDate] 49 | GO 50 | 51 | ALTER TABLE [] WITH CHECK ADD CONSTRAINT [CK_Employee_Gender] CHECK ((upper([Gender])='F' OR upper([Gender])='M')) 52 | GO 53 | 54 | ALTER TABLE [] CHECK CONSTRAINT [CK_Employee_Gender] 55 | GO 56 | 57 | ALTER TABLE [] WITH CHECK ADD CONSTRAINT [CK_Employee_HireDate] CHECK (([HireDate]>='1996-07-01' AND [HireDate]<=dateadd(day,(1),getdate()))) 58 | GO 59 | 60 | ALTER TABLE [] CHECK CONSTRAINT [CK_Employee_HireDate] 61 | GO 62 | 63 | ALTER TABLE [] WITH CHECK ADD CONSTRAINT [CK_Employee_MaritalStatus] CHECK ((upper([MaritalStatus])='S' OR upper([MaritalStatus])='M')) 64 | GO 65 | 66 | ALTER TABLE [] CHECK CONSTRAINT [CK_Employee_MaritalStatus] 67 | GO 68 | 69 | ALTER TABLE [] WITH CHECK ADD CONSTRAINT [CK_Employee_SickLeaveHours] CHECK (([SickLeaveHours]>=(0) AND [SickLeaveHours]<=(120))) 70 | GO 71 | 72 | ALTER TABLE [] CHECK CONSTRAINT [CK_Employee_SickLeaveHours] 73 | GO 74 | 75 | ALTER TABLE [] WITH CHECK ADD CONSTRAINT [CK_Employee_VacationHours] CHECK (([VacationHours]>=(-40) AND [VacationHours]<=(240))) 76 | GO 77 | 78 | ALTER TABLE [] CHECK CONSTRAINT [CK_Employee_VacationHours] 79 | GO -------------------------------------------------------------------------------- /test/sql/emptyString.sql: -------------------------------------------------------------------------------- 1 | SET ANSI_NULLS ON 2 | GO 3 | 4 | SET QUOTED_IDENTIFIER ON 5 | GO 6 | 7 | SET ANSI_PADDING ON 8 | GO 9 | 10 | CREATE TABLE [dbo].[]( 11 | [pkid] [int] NOT NULL, 12 | [] NOT NULL 13 | PRIMARY KEY (pkid) 14 | ) ON [PRIMARY] 15 | 16 | GO 17 | 18 | SET ANSI_PADDING OFF 19 | GO 20 | -------------------------------------------------------------------------------- /test/sql/template.sql: -------------------------------------------------------------------------------- 1 | SET ANSI_NULLS ON 2 | GO 3 | 4 | SET QUOTED_IDENTIFIER ON 5 | GO 6 | 7 | CREATE TABLE [dbo].[]( 8 | [ID] [int] NOT NULL, 9 | [c1] [nvarchar](100) NOT NULL, 10 | [c2] [nvarchar](100) NOT NULL, 11 | [c3] [date] NOT NULL, 12 | [c4] [date] NOT NULL, 13 | [c5] [bit] NOT NULL, 14 | [c6] [int] NOT NULL, 15 | [c7] [int] NOT NULL, 16 | [c8] [int] NOT NULL, 17 | [c9] [int] NOT NULL, 18 | [c10] [bit] NOT NULL, 19 | [c11] [decimal](18, 4) NOT NULL, 20 | [c12] [decimal](18, 4) NOT NULL, 21 | PRIMARY KEY ([ID]) 22 | ) ON [PRIMARY] 23 | 24 | GO 25 | -------------------------------------------------------------------------------- /tool/appveyor-win.cmd: -------------------------------------------------------------------------------- 1 | SET MSSQL_VERSION=2019 2 | 3 | call powershell tool\appveyor.ps1 SQL2019 4 | call node --version 5 | call .\node_modules\.bin\env-cmd -e appv-2019 npm run bench-comments 6 | call .\node_modules\.bin\env-cmd -e appv-2019 npm run bench-columns 7 | call .\node_modules\.bin\env-cmd -e appv-v17-2019 node_modules\.bin\mocha 2>&1 8 | call .\node_modules\.bin\env-cmd -e appv-2019 node_modules\.bin\mocha --exclude **/bcp.js 2>&1 9 | call net stop MSSQL$SQL2019 10 | 11 | SET MSSQL_VERSION=2017 12 | 13 | call powershell tool\appveyor.ps1 SQL2017 14 | call node --version 15 | call .\node_modules\.bin\env-cmd -e appv-2017 npm run bench-comments 16 | call .\node_modules\.bin\env-cmd -e appv-2017 npm run bench-columns 17 | call .\node_modules\.bin\env-cmd -e appv-v17-2017 node_modules\.bin\mocha 2>&1 18 | call .\node_modules\.bin\env-cmd -e appv-2017 node_modules\.bin\mocha --exclude **/bcp.js 2>&1 19 | call net stop MSSQL$SQL2017 20 | -------------------------------------------------------------------------------- /tool/appveyor.ps1: -------------------------------------------------------------------------------- 1 | [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null; 2 | [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.SqlWmiManagement") | Out-Null; 3 | 4 | Get-Service MSSQL* | Write-Output 5 | 6 | $instancename = $args[0]; 7 | 8 | $wmi = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer 9 | $tcp = $wmi.GetSmoObject("ManagedComputer[@Name='${env:computername}']/ServerInstance[@Name='${instancename}']/ServerProtocol[@Name='Tcp']"); 10 | $tcp.IsEnabled = $true; 11 | $tcp.Alter(); 12 | 13 | Start-Service -Name "MSSQL`$$instancename"; 14 | 15 | $ipall = $wmi.GetSmoObject("ManagedComputer[@Name='${env:computername}']/ServerInstance[@Name='${instancename}']/ServerProtocol[@Name='Tcp']/IPAddress[@Name='IPAll']"); 16 | $port = $ipall.IPAddressProperties.Item("TcpDynamicPorts").Value; 17 | -------------------------------------------------------------------------------- /tool/install-driver.ps1: -------------------------------------------------------------------------------- 1 | $src = $args[0] 2 | if ([string]::IsNullOrEmpty($src)) { 3 | $src = 'https://download.microsoft.com/download/E/6/B/E6BFDC7A-5BCD-4C51-9912-635646DA801E/en-US/17.5.2.1/x64/msodbcsql.msi' 4 | } 5 | Write-Host $src 6 | Install-Product node $env:nodejs_version $env:platform 7 | Write-Host "Installing ODBC driver..." -ForegroundColor Cyan 8 | Write-Host "Downloading..." 9 | $msiPath = "$($env:USERPROFILE)\msodbcsql.msi" 10 | $msiLog = "$($env:USERPROFILE)\msodbcsql.txt" 11 | (New-Object Net.WebClient).DownloadFile($src, $msiPath) 12 | Write-Host "Installing..." 13 | Get-Item -Path $msiPath 14 | Write-Host $msiPath 15 | msiexec /quiet /qn /norestart /log $msiLog /i $msiPath IACCEPTMSODBCSQLLICENSETERMS=YES 16 | -------------------------------------------------------------------------------- /tool/install_openssl.sh: -------------------------------------------------------------------------------- 1 | FOLDER=/home/appveyor/projects/node_modules/msnodesqlv8 2 | echo "node version $nodejs_version folder $FOLDER" 3 | if [ "$nodejs_version" -ge "18" ]; then 4 | cd $FOLDER 5 | chmod 775 tool/openssl.sh 6 | tool/openssl.sh 7 | fi 8 | -------------------------------------------------------------------------------- /tool/odbc.sh: -------------------------------------------------------------------------------- 1 | if ! [[ "18.04 20.04 22.04" == *"$(lsb_release -rs)"* ]]; 2 | then 3 | echo "Ubuntu $(lsb_release -rs) is not currently supported."; 4 | exit; 5 | fi 6 | 7 | sudo su 8 | curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - 9 | 10 | curl https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/prod.list > /etc/apt/sources.list.d/mssql-release.list 11 | 12 | exit 13 | sudo apt-get update 14 | sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 15 | # optional: for bcp and sqlcmd 16 | sudo ACCEPT_EULA=Y apt-get install -y mssql-tools18 17 | echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> ~/.bashrc 18 | source ~/.bashrc 19 | # optional: for unixODBC development headers 20 | sudo apt-get install -y unixodbc-dev 21 | -------------------------------------------------------------------------------- /tool/openssl.sh: -------------------------------------------------------------------------------- 1 | mkdir -p $HOME/projects 2 | cd $HOME/projects 3 | git clone https://github.com/openssl/openssl.git --depth 1 --branch openssl-3.1.1 4 | cd openssl 5 | ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared 6 | make 7 | # make test 8 | sudo make install 9 | cd /etc/ld.so.conf.d/ 10 | echo '/usr/local/ssl/lib64/' | sudo tee openssl-3.2.0.1s.conf 11 | sudo ldconfig -v 12 | -------------------------------------------------------------------------------- /tool/rename-release.ps1: -------------------------------------------------------------------------------- 1 | $src="v4.2.0" 2 | $dst="v4.2.1" 3 | $path="$HOME\dev\js\sql\v8\node_modules\msnodesqlv8\prebuilds" 4 | $versions=Get-ChildItem -Path $path *.gz 5 | $versions | ForEach-Object { 6 | $name = $_.FullName.Replace($src, $dst) 7 | Rename-Item $_.FullName $name 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": "./", 4 | "experimentalDecorators": true, 5 | "emitDecoratorMetadata": true, 6 | "target": "es6", 7 | "module": "commonjs", 8 | "noImplicitAny": true, 9 | "declaration": true, 10 | "removeComments": true, 11 | "preserveConstEnums": true, 12 | "sourceMap": true, 13 | "inlineSources": true, 14 | "pretty": true, 15 | "strictNullChecks": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "outDir": "./dist" 18 | }, 19 | "compileOnSave": true, 20 | "include": [ 21 | "samples/**/*", 22 | "lib/**/*", 23 | "test/**/*", 24 | "lib/*", 25 | "lib/index.d.ts" 26 | ], 27 | "exclude": [ 28 | "node_modules", 29 | "./dist" 30 | ] 31 | } 32 | --------------------------------------------------------------------------------