├── .azure-pipelines └── pipeline.yml ├── .dockerignore ├── .eslintrc.js ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ ├── feature_request.yml │ └── install_problem.yml └── workflows │ └── ci.yml ├── .gitignore ├── .yarnrc ├── LICENSE ├── README.md ├── SECURITY.md ├── binding.gyp ├── deps ├── common-sqlite.gypi ├── extract.js ├── sqlite-autoconf-3390400.tar.gz └── sqlite3.gyp ├── lib ├── sqlite3-binding.js ├── sqlite3.d.ts ├── sqlite3.js └── trace.js ├── package.json ├── scripts ├── test-electron.bat └── test-electron.sh ├── src ├── async.h ├── backup.cc ├── backup.h ├── database.cc ├── database.h ├── gcc-preinclude.h ├── macros.h ├── node_sqlite3.cc ├── statement.cc ├── statement.h └── threading.h ├── test ├── .eslintrc.js ├── affected.test.js ├── async_calls.test.js ├── backup.test.js ├── blob.test.js ├── cache.test.js ├── constants.test.js ├── database_fail.test.js ├── each.test.js ├── exec.test.js ├── extension.test.js ├── fts-content.test.js ├── interrupt.test.js ├── issue-108.test.js ├── json.test.js ├── limit.test.js ├── map.test.js ├── named_columns.test.js ├── named_params.test.js ├── null_error.test.js ├── nw │ ├── .gitignore │ ├── Makefile │ ├── index.html │ └── package.json ├── open_close.test.js ├── other_objects.test.js ├── parallel_insert.test.js ├── patching.test.js ├── prepare.test.js ├── profile.test.js ├── rerun.test.js ├── scheduling.test.js ├── serialization.test.js ├── support │ ├── createdb-electron.js │ ├── createdb.js │ ├── elmo.png │ ├── helper.js │ ├── prepare.db │ └── script.sql ├── trace.test.js ├── unicode.test.js ├── update_hook.test.js ├── upsert.test.js └── verbose.test.js └── tools ├── BinaryBuilder.Dockerfile └── benchmark ├── insert-transaction.sql ├── insert.js ├── select-data.sql └── select.js /.azure-pipelines/pipeline.yml: -------------------------------------------------------------------------------- 1 | name: $(Date:yyyyMMdd)$(Rev:.r) 2 | 3 | trigger: 4 | branches: 5 | include: 6 | - release/vscode 7 | pr: none 8 | 9 | resources: 10 | repositories: 11 | - repository: templates 12 | type: github 13 | name: microsoft/vscode-engineering 14 | ref: main 15 | endpoint: Monaco 16 | 17 | parameters: 18 | - name: publishPackage 19 | displayName: 🚀 Publish @vscode/sqlite3 20 | type: boolean 21 | default: false 22 | 23 | extends: 24 | template: azure-pipelines/npm-package/pipeline.yml@templates 25 | parameters: 26 | npmPackages: 27 | - name: sqlite3 28 | 29 | buildSteps: 30 | - script: | 31 | set -e 32 | includes=$(cat << 'EOF' 33 | { 34 | "target_defaults": { 35 | "conditions": [ 36 | ["OS=='linux'", { 37 | 'cflags_cc!': [ '-std=gnu++20' ], 38 | 'cflags_cc': [ '-std=gnu++2a' ], 39 | }] 40 | ] 41 | } 42 | } 43 | EOF 44 | ) 45 | if [ ! -d "$HOME/.gyp" ]; then 46 | mkdir -p "$HOME/.gyp" 47 | fi 48 | echo "$includes" > "$HOME/.gyp/include.gypi" 49 | displayName: Override gnu target for older sysroot 50 | condition: eq(variables['Agent.OS'], 'Linux') 51 | 52 | - script: yarn install 53 | displayName: Install and build 54 | 55 | testSteps: 56 | - script: | 57 | set -e 58 | includes=$(cat << 'EOF' 59 | { 60 | "target_defaults": { 61 | "conditions": [ 62 | ["OS=='linux'", { 63 | 'cflags_cc!': [ '-std=gnu++20' ], 64 | 'cflags_cc': [ '-std=gnu++2a' ], 65 | }] 66 | ] 67 | } 68 | } 69 | EOF 70 | ) 71 | if [ ! -d "$HOME/.gyp" ]; then 72 | mkdir -p "$HOME/.gyp" 73 | fi 74 | echo "$includes" > "$HOME/.gyp/include.gypi" 75 | displayName: Override gnu target for older sysroot 76 | condition: eq(variables['Agent.OS'], 'Linux') 77 | 78 | - script: yarn install 79 | displayName: Install and build 80 | 81 | - script: yarn test 82 | displayName: Test npm package 83 | 84 | apiScanSoftwareName: 'vscode-sqlite3' 85 | apiScanSoftwareVersion: '5.1' 86 | 87 | publishPackage: ${{ parameters.publishPackage }} 88 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | #.dockerignore 2 | *~ 3 | Dockerfile 4 | .git/ 5 | 6 | #.gitignore 7 | *.dylib 8 | *.so 9 | *.o 10 | *.lo 11 | *.Makefile 12 | *.target.gyp.mk 13 | lib/binding 14 | build 15 | out 16 | Release 17 | Debug 18 | node_modules 19 | .deps 20 | Makefile.gyp 21 | gyp-mac-tool 22 | .dirstamp 23 | npm-debug.log 24 | test/support/big.db 25 | test/tmp 26 | test/nw/app.nw 27 | .DS_Store 28 | .idea 29 | .dtps 30 | local.env 31 | .mason 32 | .eslintrc.js 33 | setup.sh -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "es2017": true, 5 | "node": true 6 | }, 7 | "rules": { 8 | "indent": ["error", 4], 9 | "linebreak-style": ["error", "unix"], 10 | "semi": ["error", "always"], 11 | "no-cond-assign": ["error", "always"] 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | General guidelines for contributing to node-sqlite3 4 | 5 | ## Install Help 6 | 7 | If you've landed here due to a failed install of `node-sqlite3` then feel free to create a [new issue](https://github.com/tryghost/node-sqlite3/issues/new) to ask for help. The most likely problem is that we do not yet provide pre-built binaries for your particular platform and so the `node-sqlite3` install attempted a source compile but failed because you are missing the [dependencies for node-gyp](https://github.com/nodejs/node-gyp#installation). Provide as much detail on your problem as possible and we'll try to help. Include: 8 | 9 | - Logs of failed install (preferably from running `npm install sqlite3 --loglevel=info`) 10 | - Version of `node-sqlite3` you tried to install 11 | - Node version you are running 12 | - Operating system and architecture you are running, e.g. `Windows 7 64 bit`. 13 | 14 | The release process is documented in the wiki: https://github.com/TryGhost/node-sqlite3/wiki/Release-process 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug report 2 | description: Report a reproducible issue 3 | labels: ["bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | ## Welcome 👋 9 | Thank you for taking the time to fill out a bug report 🙂 10 | 11 | The more information you provide, the easier & quicker it is for us to diagnose the problem. 12 | 13 | Please search the existing issues and recent releases to make sure your problem hasn't already been solved. 14 | - type: textarea 15 | id: summary 16 | attributes: 17 | label: Issue Summary 18 | description: Explain what's wrong 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: reproduction 23 | attributes: 24 | label: Steps to Reproduce 25 | description: Also tell us, what did you expect to happen? 26 | placeholder: | 27 | 1. This is the first step... 28 | 2. This is the second step, etc. 29 | validations: 30 | required: true 31 | - type: input 32 | id: version 33 | attributes: 34 | label: Version 35 | description: What version of `node-sqlite3` are you using? 36 | validations: 37 | required: true 38 | - type: input 39 | id: node 40 | attributes: 41 | label: Node.js Version 42 | validations: 43 | required: true 44 | - type: input 45 | id: os 46 | attributes: 47 | label: How did you install the library? 48 | description: Provide details of your operating system and architecture 49 | validations: 50 | required: true 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 📖 API documentation 4 | url: https://github.com/TryGhost/node-sqlite3/wiki/API 5 | about: Documentation for the `node-sqlite3` API 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: ✨ Feature request 2 | description: Request a desired feature 3 | labels: ["feature request"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | ## Welcome 👋 9 | Thank you for taking the time to fill out a feature request 10 | 11 | The more information you provide, the easier it is for us to understand your use case. 12 | - type: textarea 13 | id: summary 14 | attributes: 15 | label: Summary 16 | description: Explain your use case. How would this improve your life? 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: reproduction 21 | attributes: 22 | label: Proposed implementation 23 | description: Give an API proposal. How would the feature work? 24 | placeholder: | 25 | The Database class has a new function called... 26 | validations: 27 | required: true 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/install_problem.yml: -------------------------------------------------------------------------------- 1 | name: ⚙️ Installation problem 2 | description: Report an issue installing the library 3 | labels: ["install problem"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | ## Welcome 👋 9 | Thank you for taking the time to fill out a report with installing 🙂 10 | 11 | The more information you provide, the easier & quicker it is for us to diagnose the problem. 12 | 13 | Please search the existing issues and recent releases to make sure your problem hasn't already been solved. 14 | - type: textarea 15 | id: summary 16 | attributes: 17 | label: Issue Summary 18 | description: Explain what's wrong 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: logs 23 | attributes: 24 | label: Relevant logs or output 25 | placeholder: | 26 | Error when installing `sqlite3` due to... 27 | validations: 28 | required: true 29 | - type: input 30 | id: version 31 | attributes: 32 | label: Version 33 | description: What version of `node-sqlite3` are you using? 34 | validations: 35 | required: true 36 | - type: input 37 | id: node 38 | attributes: 39 | label: Node.js Version 40 | validations: 41 | required: true 42 | - type: input 43 | id: os 44 | attributes: 45 | label: How did you install the library? 46 | description: Provide details of your operating system and architecture 47 | validations: 48 | required: true 49 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Electron CI 2 | 3 | on: 4 | push: 5 | branches: [ release/vscode ] 6 | pull_request: 7 | 8 | jobs: 9 | posix: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | os: [macos-latest, ubuntu-latest] 14 | steps: 15 | - uses: actions/checkout@v2 16 | - run: yarn install 17 | - run: scripts/test-electron.sh 18 | windows: 19 | runs-on: ${{ matrix.os }} 20 | strategy: 21 | matrix: 22 | os: [windows-latest] 23 | steps: 24 | - uses: actions/checkout@v2 25 | - run: yarn install 26 | - run: .\scripts\test-electron.bat -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.dylib 2 | *.so 3 | *.o 4 | *.lo 5 | *.Makefile 6 | *.target.gyp.mk 7 | lib/binding 8 | build 9 | out 10 | Release 11 | Debug 12 | node_modules 13 | .deps 14 | Makefile.gyp 15 | gyp-mac-tool 16 | .dirstamp 17 | npm-debug.log 18 | test/support/big.db 19 | test/tmp 20 | test/nw/app.nw 21 | .DS_Store 22 | .idea 23 | .dtps 24 | local.env 25 | .mason 26 | setup.sh 27 | /build-tmp-napi-v3 28 | /build-tmp-napi-v6 29 | *.tgz 30 | package-lock.json 31 | yarn.lock 32 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | disturl "https://electronjs.org/headers" 2 | target "32.1.2" 3 | runtime "electron" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) MapBox 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | - Redistributions in binary form must reproduce the above copyright notice, this 10 | list of conditions and the following disclaimer in the documentation and/or 11 | other materials provided with the distribution. 12 | - Neither the name "MapBox" nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚙️ node-sqlite3 2 | 3 | Asynchronous, non-blocking [SQLite3](https://sqlite.org/) bindings for [Node.js](http://nodejs.org/). 4 | 5 | [![Latest release](https://img.shields.io/github/release/TryGhost/node-sqlite3.svg)](https://www.npmjs.com/package/sqlite3) 6 | ![Build Status](https://github.com/TryGhost/node-sqlite3/workflows/CI/badge.svg?branch=master) 7 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fnode-sqlite3.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fnode-sqlite3?ref=badge_shield) 8 | [![N-API v3 Badge](https://img.shields.io/badge/N--API-v3-green.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api) 9 | [![N-API v6 Badge](https://img.shields.io/badge/N--API-v6-green.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api) 10 | 11 | # Features 12 | 13 | - Straightforward query and parameter binding interface 14 | - Full Buffer/Blob support 15 | - Extensive [debugging support](https://github.com/tryghost/node-sqlite3/wiki/Debugging) 16 | - [Query serialization](https://github.com/tryghost/node-sqlite3/wiki/Control-Flow) API 17 | - [Extension support](https://github.com/TryGhost/node-sqlite3/wiki/API#databaseloadextensionpath-callback), including bundled support for the [json1 extension](https://www.sqlite.org/json1.html) 18 | - Big test suite 19 | - Written in modern C++ and tested for memory leaks 20 | - Bundles SQLite v3.39.4, or you can build using a local SQLite 21 | 22 | # Installing 23 | 24 | You can use [`npm`](https://github.com/npm/cli) or [`yarn`](https://github.com/yarnpkg/yarn) to install `sqlite3`: 25 | 26 | * (recommended) Latest published package: 27 | ```bash 28 | npm install sqlite3 29 | # or 30 | yarn add sqlite3 31 | ``` 32 | * GitHub's `master` branch: `npm install https://github.com/tryghost/node-sqlite3/tarball/master` 33 | 34 | ### Prebuilt binaries 35 | 36 | `sqlite3` v5+ was rewritten to use [Node-API](https://nodejs.org/api/n-api.html) so prebuilt binaries do not need to be built for specific Node versions. `sqlite3` currently builds for both Node-API v3 and v6. Check the [Node-API version matrix](https://nodejs.org/api/n-api.html#node-api-version-matrix) to ensure your Node version supports one of these. The prebuilt binaries should be supported on Node v10+. 37 | 38 | The module uses [node-pre-gyp](https://github.com/mapbox/node-pre-gyp) to download the prebuilt binary for your platform, if it exists. These binaries are hosted on GitHub Releases for `sqlite3` versions above 5.0.2, and they are hosted on S3 otherwise. The following targets are currently provided: 39 | 40 | Format: `napi-v{napi_build_version}-{platform}-{libc}-{arch}` 41 | 42 | * `napi-v3-darwin-unknown-arm64` 43 | * `napi-v3-darwin-unknown-x64` 44 | * `napi-v3-linux-glibc-arm64` 45 | * `napi-v3-linux-glibc-x64` 46 | * `napi-v3-linux-musl-arm64` 47 | * `napi-v3-linux-musl-x64` 48 | * `napi-v3-win32-unknown-ia32` 49 | * `napi-v3-win32-unknown-x64` 50 | * `napi-v6-darwin-unknown-arm64` 51 | * `napi-v6-darwin-unknown-x64` 52 | * `napi-v6-linux-glibc-arm64` 53 | * `napi-v6-linux-glibc-x64` 54 | * `napi-v6-linux-musl-arm64` 55 | * `napi-v6-linux-musl-x64` 56 | * `napi-v6-win32-unknown-ia32` 57 | * `napi-v6-win32-unknown-x64` 58 | 59 | Unfortunately, [node-pre-gyp](https://github.com/mapbox/node-pre-gyp) cannot differentiate between `armv6` and `armv7`, and instead uses `arm` as the `{arch}`. Until that is fixed, you will still need to install `sqlite3` from [source](#source-install). 60 | 61 | Support for other platforms and architectures may be added in the future if CI supports building on them. 62 | 63 | If your environment isn't supported, it'll use `node-gyp` to build SQLite but you will need to install a C++ compiler and linker. 64 | 65 | ### Other ways to install 66 | 67 | It is also possible to make your own build of `sqlite3` from its source instead of its npm package ([See below.](#source-install)). 68 | 69 | The `sqlite3` module also works with [node-webkit](https://github.com/rogerwang/node-webkit) if node-webkit contains a supported version of Node.js engine. [(See below.)](#building-for-node-webkit) 70 | 71 | SQLite's [SQLCipher extension](https://github.com/sqlcipher/sqlcipher) is also supported. [(See below.)](#building-for-sqlcipher) 72 | 73 | # API 74 | 75 | See the [API documentation](https://github.com/TryGhost/node-sqlite3/wiki/API) in the wiki. 76 | 77 | # Usage 78 | 79 | **Note:** the module must be [installed](#installing) before use. 80 | 81 | ``` js 82 | const sqlite3 = require('sqlite3').verbose(); 83 | const db = new sqlite3.Database(':memory:'); 84 | 85 | db.serialize(() => { 86 | db.run("CREATE TABLE lorem (info TEXT)"); 87 | 88 | const stmt = db.prepare("INSERT INTO lorem VALUES (?)"); 89 | for (let i = 0; i < 10; i++) { 90 | stmt.run("Ipsum " + i); 91 | } 92 | stmt.finalize(); 93 | 94 | db.each("SELECT rowid AS id, info FROM lorem", (err, row) => { 95 | console.log(row.id + ": " + row.info); 96 | }); 97 | }); 98 | 99 | db.close(); 100 | ``` 101 | 102 | ## Source install 103 | 104 | To skip searching for pre-compiled binaries, and force a build from source, use 105 | 106 | ```bash 107 | npm install --build-from-source 108 | ``` 109 | 110 | The sqlite3 module depends only on libsqlite3. However, by default, an internal/bundled copy of sqlite will be built and statically linked, so an externally installed sqlite3 is not required. 111 | 112 | If you wish to install against an external sqlite then you need to pass the `--sqlite` argument to `npm` wrapper: 113 | 114 | ```bash 115 | npm install --build-from-source --sqlite=/usr/local 116 | ``` 117 | 118 | If building against an external sqlite3 make sure to have the development headers available. Mac OS X ships with these by default. If you don't have them installed, install the `-dev` package with your package manager, e.g. `apt-get install libsqlite3-dev` for Debian/Ubuntu. Make sure that you have at least `libsqlite3` >= 3.6. 119 | 120 | Note, if building against homebrew-installed sqlite on OS X you can do: 121 | 122 | ```bash 123 | npm install --build-from-source --sqlite=/usr/local/opt/sqlite/ 124 | ``` 125 | 126 | ## Custom file header (magic) 127 | 128 | The default sqlite file header is "SQLite format 3". You can specify a different magic, though this will make standard tools and libraries unable to work with your files. 129 | 130 | ```bash 131 | npm install --build-from-source --sqlite_magic="MyCustomMagic15" 132 | ``` 133 | 134 | Note that the magic *must* be exactly 15 characters long (16 bytes including null terminator). 135 | 136 | ## Building for node-webkit 137 | 138 | Because of ABI differences, `sqlite3` must be built in a custom to be used with [node-webkit](https://github.com/rogerwang/node-webkit). 139 | 140 | To build `sqlite3` for node-webkit: 141 | 142 | 1. Install [`nw-gyp`](https://github.com/rogerwang/nw-gyp) globally: `npm install nw-gyp -g` *(unless already installed)* 143 | 144 | 2. Build the module with the custom flags of `--runtime`, `--target_arch`, and `--target`: 145 | 146 | ```bash 147 | NODE_WEBKIT_VERSION="0.8.6" # see latest version at https://github.com/rogerwang/node-webkit#downloads 148 | npm install sqlite3 --build-from-source --runtime=node-webkit --target_arch=ia32 --target=$(NODE_WEBKIT_VERSION) 149 | ``` 150 | 151 | This command internally calls out to [`node-pre-gyp`](https://github.com/mapbox/node-pre-gyp) which itself calls out to [`nw-gyp`](https://github.com/rogerwang/nw-gyp) when the `--runtime=node-webkit` option is passed. 152 | 153 | You can also run this command from within a `sqlite3` checkout: 154 | 155 | ```bash 156 | npm install --build-from-source --runtime=node-webkit --target_arch=ia32 --target=$(NODE_WEBKIT_VERSION) 157 | ``` 158 | 159 | Remember the following: 160 | 161 | * You must provide the right `--target_arch` flag. `ia32` is needed to target 32bit node-webkit builds, while `x64` will target 64bit node-webkit builds (if available for your platform). 162 | 163 | * After the `sqlite3` package is built for node-webkit it cannot run in the vanilla Node.js (and vice versa). 164 | * For example, `npm test` of the node-webkit's package would fail. 165 | 166 | Visit the “[Using Node modules](https://github.com/rogerwang/node-webkit/wiki/Using-Node-modules)” article in the node-webkit's wiki for more details. 167 | 168 | ## Building for SQLCipher 169 | 170 | For instructions on building SQLCipher, see [Building SQLCipher for Node.js](https://coolaj86.com/articles/building-sqlcipher-for-node-js-on-raspberry-pi-2/). Alternatively, you can install it with your local package manager. 171 | 172 | To run against SQLCipher, you need to compile `sqlite3` from source by passing build options like: 173 | 174 | ```bash 175 | npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=/usr/ 176 | 177 | node -e 'require("sqlite3")' 178 | ``` 179 | 180 | If your SQLCipher is installed in a custom location (if you compiled and installed it yourself), you'll need to set some environment variables: 181 | 182 | ### On OS X with Homebrew 183 | 184 | Set the location where `brew` installed it: 185 | 186 | ```bash 187 | export LDFLAGS="-L`brew --prefix`/opt/sqlcipher/lib" 188 | export CPPFLAGS="-I`brew --prefix`/opt/sqlcipher/include/sqlcipher" 189 | npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=`brew --prefix` 190 | 191 | node -e 'require("sqlite3")' 192 | ``` 193 | 194 | ### On most Linuxes (including Raspberry Pi) 195 | 196 | Set the location where `make` installed it: 197 | 198 | ```bash 199 | export LDFLAGS="-L/usr/local/lib" 200 | export CPPFLAGS="-I/usr/local/include -I/usr/local/include/sqlcipher" 201 | export CXXFLAGS="$CPPFLAGS" 202 | npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=/usr/local --verbose 203 | 204 | node -e 'require("sqlite3")' 205 | ``` 206 | 207 | ### Custom builds and Electron 208 | 209 | Running `sqlite3` through [electron-rebuild](https://github.com/electron/electron-rebuild) does not preserve the SQLCipher extension, so some additional flags are needed to make this build Electron compatible. Your `npm install sqlite3 --build-from-source` command needs these additional flags (be sure to replace the target version with the current Electron version you are working with): 210 | 211 | ```bash 212 | --runtime=electron --target=18.2.1 --dist-url=https://electronjs.org/headers 213 | ``` 214 | 215 | In the case of MacOS with Homebrew, the command should look like the following: 216 | 217 | ```bash 218 | npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=`brew --prefix` --runtime=electron --target=18.2.1 --dist-url=https://electronjs.org/headers 219 | ``` 220 | 221 | # Testing 222 | 223 | ```bash 224 | npm test 225 | ``` 226 | 227 | # Contributors 228 | 229 | * [Konstantin Käfer](https://github.com/kkaefer) 230 | * [Dane Springmeyer](https://github.com/springmeyer) 231 | * [Will White](https://github.com/willwhite) 232 | * [Orlando Vazquez](https://github.com/orlandov) 233 | * [Artem Kustikov](https://github.com/artiz) 234 | * [Eric Fredricksen](https://github.com/grumdrig) 235 | * [John Wright](https://github.com/mrjjwright) 236 | * [Ryan Dahl](https://github.com/ry) 237 | * [Tom MacWright](https://github.com/tmcw) 238 | * [Carter Thaxton](https://github.com/carter-thaxton) 239 | * [Audrius Kažukauskas](https://github.com/audriusk) 240 | * [Johannes Schauer](https://github.com/pyneo) 241 | * [Mithgol](https://github.com/Mithgol) 242 | * [Kewde](https://github.com/kewde) 243 | 244 | # Acknowledgments 245 | 246 | Thanks to [Orlando Vazquez](https://github.com/orlandov), 247 | [Eric Fredricksen](https://github.com/grumdrig) and 248 | [Ryan Dahl](https://github.com/ry) for their SQLite bindings for node, and to mraleph on Freenode's #v8 for answering questions. 249 | 250 | This module was originally created by [Mapbox](https://mapbox.com/) & is now maintained by [Ghost](https://ghost.org). 251 | 252 | # Changelog 253 | 254 | We use [GitHub releases](https://github.com/TryGhost/node-sqlite3/releases) for notes on the latest versions. See [CHANGELOG.md](https://github.com/TryGhost/node-sqlite3/blob/b05f4594cf8b0de64743561fcd2cfe6f4571754d/CHANGELOG.md) in git history for details on older versions. 255 | 256 | # License 257 | 258 | `node-sqlite3` is [BSD licensed](https://github.com/tryghost/node-sqlite3/raw/master/LICENSE). 259 | 260 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fnode-sqlite3.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fnode-sqlite3?ref=badge_large) 261 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "includes": [ "deps/common-sqlite.gypi" ], 3 | "variables": { 4 | "sqlite%":"internal", 5 | "sqlite_libname%":"sqlite3" 6 | }, 7 | "targets": [ 8 | { 9 | "target_name": "vscode-sqlite3", 10 | "xcode_settings": { 11 | "CLANG_CXX_LIBRARY": "libc++", 12 | # Target depends on 13 | # https://chromium.googlesource.com/chromium/src/+/master/build/config/mac/mac_sdk.gni#22 14 | "MACOSX_DEPLOYMENT_TARGET": "10.11", 15 | }, 16 | "dependencies": [ 17 | " 5 | 6 | import events = require("events"); 7 | 8 | export const OPEN_READONLY: number; 9 | export const OPEN_READWRITE: number; 10 | export const OPEN_CREATE: number; 11 | export const OPEN_FULLMUTEX: number; 12 | export const OPEN_SHAREDCACHE: number; 13 | export const OPEN_PRIVATECACHE: number; 14 | export const OPEN_URI: number; 15 | 16 | export const VERSION: string; 17 | export const SOURCE_ID: string; 18 | export const VERSION_NUMBER: number; 19 | 20 | export const OK: number; 21 | export const ERROR: number; 22 | export const INTERNAL: number; 23 | export const PERM: number; 24 | export const ABORT: number; 25 | export const BUSY: number; 26 | export const LOCKED: number; 27 | export const NOMEM: number; 28 | export const READONLY: number; 29 | export const INTERRUPT: number 30 | export const IOERR: number; 31 | export const CORRUPT: number 32 | export const NOTFOUND: number; 33 | export const FULL: number; 34 | export const CANTOPEN: number; 35 | export const PROTOCOL: number; 36 | export const EMPTY: number; 37 | export const SCHEMA: number; 38 | export const TOOBIG: number 39 | export const CONSTRAINT: number 40 | export const MISMATCH: number; 41 | export const MISUSE: number; 42 | export const NOLFS: number; 43 | export const AUTH: number 44 | export const FORMAT: number; 45 | export const RANGE: number 46 | export const NOTADB: number; 47 | 48 | export const LIMIT_LENGTH: number; 49 | export const LIMIT_SQL_LENGTH: number; 50 | export const LIMIT_COLUMN: number; 51 | export const LIMIT_EXPR_DEPTH: number; 52 | export const LIMIT_COMPOUND_SELECT: number; 53 | export const LIMIT_VDBE_OP: number; 54 | export const LIMIT_FUNCTION_ARG: number; 55 | export const LIMIT_ATTACHED: number; 56 | export const LIMIT_LIKE_PATTERN_LENGTH: number; 57 | export const LIMIT_VARIABLE_NUMBER: number; 58 | export const LIMIT_TRIGGER_DEPTH: number; 59 | export const LIMIT_WORKER_THREADS: number; 60 | 61 | export const cached: { 62 | Database(filename: string, callback?: (this: Database, err: Error | null) => void): Database; 63 | Database(filename: string, mode?: number, callback?: (this: Database, err: Error | null) => void): Database; 64 | }; 65 | 66 | export interface RunResult extends Statement { 67 | lastID: number; 68 | changes: number; 69 | } 70 | 71 | export class Statement extends events.EventEmitter { 72 | bind(callback?: (err: Error | null) => void): this; 73 | bind(...params: any[]): this; 74 | 75 | reset(callback?: (err: null) => void): this; 76 | 77 | finalize(callback?: (err: Error) => void): Database; 78 | 79 | run(callback?: (err: Error | null) => void): this; 80 | run(params: any, callback?: (this: RunResult, err: Error | null) => void): this; 81 | run(...params: any[]): this; 82 | 83 | get(callback?: (err: Error | null, row?: any) => void): this; 84 | get(params: any, callback?: (this: RunResult, err: Error | null, row?: any) => void): this; 85 | get(...params: any[]): this; 86 | 87 | all(callback?: (err: Error | null, rows: any[]) => void): this; 88 | all(params: any, callback?: (this: RunResult, err: Error | null, rows: any[]) => void): this; 89 | all(...params: any[]): this; 90 | 91 | each(callback?: (err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this; 92 | each(params: any, callback?: (this: RunResult, err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this; 93 | each(...params: any[]): this; 94 | } 95 | 96 | export class Database extends events.EventEmitter { 97 | constructor(filename: string, callback?: (err: Error | null) => void); 98 | constructor(filename: string, mode?: number, callback?: (err: Error | null) => void); 99 | 100 | close(callback?: (err: Error | null) => void): void; 101 | 102 | run(sql: string, callback?: (this: RunResult, err: Error | null) => void): this; 103 | run(sql: string, params: any, callback?: (this: RunResult, err: Error | null) => void): this; 104 | run(sql: string, ...params: any[]): this; 105 | 106 | get(sql: string, callback?: (this: Statement, err: Error | null, row: any) => void): this; 107 | get(sql: string, params: any, callback?: (this: Statement, err: Error | null, row: any) => void): this; 108 | get(sql: string, ...params: any[]): this; 109 | 110 | all(sql: string, callback?: (this: Statement, err: Error | null, rows: any[]) => void): this; 111 | all(sql: string, params: any, callback?: (this: Statement, err: Error | null, rows: any[]) => void): this; 112 | all(sql: string, ...params: any[]): this; 113 | 114 | each(sql: string, callback?: (this: Statement, err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this; 115 | each(sql: string, params: any, callback?: (this: Statement, err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this; 116 | each(sql: string, ...params: any[]): this; 117 | 118 | exec(sql: string, callback?: (this: Statement, err: Error | null) => void): this; 119 | 120 | prepare(sql: string, callback?: (this: Statement, err: Error | null) => void): Statement; 121 | prepare(sql: string, params: any, callback?: (this: Statement, err: Error | null) => void): Statement; 122 | prepare(sql: string, ...params: any[]): Statement; 123 | 124 | serialize(callback?: () => void): void; 125 | parallelize(callback?: () => void): void; 126 | 127 | on(event: "trace", listener: (sql: string) => void): this; 128 | on(event: "profile", listener: (sql: string, time: number) => void): this; 129 | on(event: "change", listener: (type: string, database: string, table: string, rowid: number) => void): this; 130 | on(event: "error", listener: (err: Error) => void): this; 131 | on(event: "open" | "close", listener: () => void): this; 132 | on(event: string, listener: (...args: any[]) => void): this; 133 | 134 | configure(option: "busyTimeout", value: number): void; 135 | configure(option: "limit", id: number, value: number): void; 136 | 137 | loadExtension(filename: string, callback?: (err: Error | null) => void): this; 138 | 139 | wait(callback?: (param: null) => void): this; 140 | 141 | interrupt(): void; 142 | } 143 | 144 | export function verbose(): sqlite3; 145 | 146 | export interface sqlite3 { 147 | OPEN_READONLY: number; 148 | OPEN_READWRITE: number; 149 | OPEN_CREATE: number; 150 | OPEN_FULLMUTEX: number; 151 | OPEN_SHAREDCACHE: number; 152 | OPEN_PRIVATECACHE: number; 153 | OPEN_URI: number; 154 | 155 | VERSION: string; 156 | SOURCE_ID: string; 157 | VERSION_NUMBER: number; 158 | 159 | OK: number; 160 | ERROR: number; 161 | INTERNAL: number; 162 | PERM: number; 163 | ABORT: number; 164 | BUSY: number; 165 | LOCKED: number; 166 | NOMEM: number; 167 | READONLY: number; 168 | INTERRUPT: number 169 | IOERR: number; 170 | CORRUPT: number 171 | NOTFOUND: number; 172 | FULL: number; 173 | CANTOPEN: number; 174 | PROTOCOL: number; 175 | EMPTY: number; 176 | SCHEMA: number; 177 | TOOBIG: number 178 | CONSTRAINT: number 179 | MISMATCH: number; 180 | MISUSE: number; 181 | NOLFS: number; 182 | AUTH: number 183 | FORMAT: number; 184 | RANGE: number 185 | NOTADB: number; 186 | 187 | LIMIT_LENGTH: number; 188 | LIMIT_SQL_LENGTH: number; 189 | LIMIT_COLUMN: number; 190 | LIMIT_EXPR_DEPTH: number; 191 | LIMIT_COMPOUND_SELECT: number; 192 | LIMIT_VDBE_OP: number; 193 | LIMIT_FUNCTION_ARG: number; 194 | LIMIT_ATTACHED: number; 195 | LIMIT_LIKE_PATTERN_LENGTH: number; 196 | LIMIT_VARIABLE_NUMBER: number; 197 | LIMIT_TRIGGER_DEPTH: number; 198 | LIMIT_WORKER_THREADS: number; 199 | 200 | cached: typeof cached; 201 | RunResult: RunResult; 202 | Statement: typeof Statement; 203 | Database: typeof Database; 204 | verbose(): this; 205 | } -------------------------------------------------------------------------------- /lib/sqlite3.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const sqlite3 = require('./sqlite3-binding.js'); 3 | const EventEmitter = require('events').EventEmitter; 4 | module.exports = exports = sqlite3; 5 | 6 | function normalizeMethod (fn) { 7 | return function (sql) { 8 | let errBack; 9 | const args = Array.prototype.slice.call(arguments, 1); 10 | 11 | if (typeof args[args.length - 1] === 'function') { 12 | const callback = args[args.length - 1]; 13 | errBack = function(err) { 14 | if (err) { 15 | callback(err); 16 | } 17 | }; 18 | } 19 | const statement = new Statement(this, sql, errBack); 20 | return fn.call(this, statement, args); 21 | }; 22 | } 23 | 24 | function inherits(target, source) { 25 | for (const k in source.prototype) 26 | target.prototype[k] = source.prototype[k]; 27 | } 28 | 29 | sqlite3.cached = { 30 | Database: function(file, a, b) { 31 | if (file === '' || file === ':memory:') { 32 | // Don't cache special databases. 33 | return new Database(file, a, b); 34 | } 35 | 36 | let db; 37 | file = path.resolve(file); 38 | 39 | if (!sqlite3.cached.objects[file]) { 40 | db = sqlite3.cached.objects[file] = new Database(file, a, b); 41 | } 42 | else { 43 | // Make sure the callback is called. 44 | db = sqlite3.cached.objects[file]; 45 | const callback = (typeof a === 'number') ? b : a; 46 | if (typeof callback === 'function') { 47 | function cb() { callback.call(db, null); } 48 | if (db.open) process.nextTick(cb); 49 | else db.once('open', cb); 50 | } 51 | } 52 | 53 | return db; 54 | }, 55 | objects: {} 56 | }; 57 | 58 | 59 | const Database = sqlite3.Database; 60 | const Statement = sqlite3.Statement; 61 | const Backup = sqlite3.Backup; 62 | 63 | inherits(Database, EventEmitter); 64 | inherits(Statement, EventEmitter); 65 | inherits(Backup, EventEmitter); 66 | 67 | // Database#prepare(sql, [bind1, bind2, ...], [callback]) 68 | Database.prototype.prepare = normalizeMethod(function(statement, params) { 69 | return params.length 70 | ? statement.bind.apply(statement, params) 71 | : statement; 72 | }); 73 | 74 | // Database#run(sql, [bind1, bind2, ...], [callback]) 75 | Database.prototype.run = normalizeMethod(function(statement, params) { 76 | statement.run.apply(statement, params).finalize(); 77 | return this; 78 | }); 79 | 80 | // Database#get(sql, [bind1, bind2, ...], [callback]) 81 | Database.prototype.get = normalizeMethod(function(statement, params) { 82 | statement.get.apply(statement, params).finalize(); 83 | return this; 84 | }); 85 | 86 | // Database#all(sql, [bind1, bind2, ...], [callback]) 87 | Database.prototype.all = normalizeMethod(function(statement, params) { 88 | statement.all.apply(statement, params).finalize(); 89 | return this; 90 | }); 91 | 92 | // Database#each(sql, [bind1, bind2, ...], [callback], [complete]) 93 | Database.prototype.each = normalizeMethod(function(statement, params) { 94 | statement.each.apply(statement, params).finalize(); 95 | return this; 96 | }); 97 | 98 | Database.prototype.map = normalizeMethod(function(statement, params) { 99 | statement.map.apply(statement, params).finalize(); 100 | return this; 101 | }); 102 | 103 | // Database#backup(filename, [callback]) 104 | // Database#backup(filename, destName, sourceName, filenameIsDest, [callback]) 105 | Database.prototype.backup = function() { 106 | let backup; 107 | if (arguments.length <= 2) { 108 | // By default, we write the main database out to the main database of the named file. 109 | // This is the most likely use of the backup api. 110 | backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]); 111 | } else { 112 | // Otherwise, give the user full control over the sqlite3_backup_init arguments. 113 | backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]); 114 | } 115 | // Per the sqlite docs, exclude the following errors as non-fatal by default. 116 | backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED]; 117 | return backup; 118 | }; 119 | 120 | Statement.prototype.map = function() { 121 | const params = Array.prototype.slice.call(arguments); 122 | const callback = params.pop(); 123 | params.push(function(err, rows) { 124 | if (err) return callback(err); 125 | const result = {}; 126 | if (rows.length) { 127 | const keys = Object.keys(rows[0]); 128 | const key = keys[0]; 129 | if (keys.length > 2) { 130 | // Value is an object 131 | for (let i = 0; i < rows.length; i++) { 132 | result[rows[i][key]] = rows[i]; 133 | } 134 | } else { 135 | const value = keys[1]; 136 | // Value is a plain value 137 | for (let i = 0; i < rows.length; i++) { 138 | result[rows[i][key]] = rows[i][value]; 139 | } 140 | } 141 | } 142 | callback(err, result); 143 | }); 144 | return this.all.apply(this, params); 145 | }; 146 | 147 | let isVerbose = false; 148 | 149 | const supportedEvents = [ 'trace', 'profile', 'change' ]; 150 | 151 | Database.prototype.addListener = Database.prototype.on = function(type) { 152 | const val = EventEmitter.prototype.addListener.apply(this, arguments); 153 | if (supportedEvents.indexOf(type) >= 0) { 154 | this.configure(type, true); 155 | } 156 | return val; 157 | }; 158 | 159 | Database.prototype.removeListener = function(type) { 160 | const val = EventEmitter.prototype.removeListener.apply(this, arguments); 161 | if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) { 162 | this.configure(type, false); 163 | } 164 | return val; 165 | }; 166 | 167 | Database.prototype.removeAllListeners = function(type) { 168 | const val = EventEmitter.prototype.removeAllListeners.apply(this, arguments); 169 | if (supportedEvents.indexOf(type) >= 0) { 170 | this.configure(type, false); 171 | } 172 | return val; 173 | }; 174 | 175 | // Save the stack trace over EIO callbacks. 176 | sqlite3.verbose = function() { 177 | if (!isVerbose) { 178 | const trace = require('./trace'); 179 | [ 180 | 'prepare', 181 | 'get', 182 | 'run', 183 | 'all', 184 | 'each', 185 | 'map', 186 | 'close', 187 | 'exec' 188 | ].forEach(function (name) { 189 | trace.extendTrace(Database.prototype, name); 190 | }); 191 | [ 192 | 'bind', 193 | 'get', 194 | 'run', 195 | 'all', 196 | 'each', 197 | 'map', 198 | 'reset', 199 | 'finalize', 200 | ].forEach(function (name) { 201 | trace.extendTrace(Statement.prototype, name); 202 | }); 203 | isVerbose = true; 204 | } 205 | 206 | return sqlite3; 207 | }; 208 | -------------------------------------------------------------------------------- /lib/trace.js: -------------------------------------------------------------------------------- 1 | // Inspired by https://github.com/tlrobinson/long-stack-traces 2 | const util = require('util'); 3 | 4 | function extendTrace(object, property, pos) { 5 | const old = object[property]; 6 | object[property] = function() { 7 | const error = new Error(); 8 | const name = object.constructor.name + '#' + property + '(' + 9 | Array.prototype.slice.call(arguments).map(function(el) { 10 | return util.inspect(el, false, 0); 11 | }).join(', ') + ')'; 12 | 13 | if (typeof pos === 'undefined') pos = -1; 14 | if (pos < 0) pos += arguments.length; 15 | const cb = arguments[pos]; 16 | if (typeof arguments[pos] === 'function') { 17 | arguments[pos] = function replacement() { 18 | const err = arguments[0]; 19 | if (err && err.stack && !err.__augmented) { 20 | err.stack = filter(err).join('\n'); 21 | err.stack += '\n--> in ' + name; 22 | err.stack += '\n' + filter(error).slice(1).join('\n'); 23 | err.__augmented = true; 24 | } 25 | return cb.apply(this, arguments); 26 | }; 27 | } 28 | return old.apply(this, arguments); 29 | }; 30 | } 31 | exports.extendTrace = extendTrace; 32 | 33 | 34 | function filter(error) { 35 | return error.stack.split('\n').filter(function(line) { 36 | return line.indexOf(__filename) < 0; 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vscode/sqlite3", 3 | "description": "Asynchronous, non-blocking SQLite3 bindings", 4 | "version": "5.1.8-vscode", 5 | "homepage": "https://github.com/microsoft/vscode-node-sqlite3", 6 | "author": { 7 | "name": "Mapbox", 8 | "url": "https://mapbox.com/" 9 | }, 10 | "binary": { 11 | "module_name": "node_sqlite3", 12 | "module_path": "./lib/binding/napi-v{napi_build_version}-{platform}-{libc}-{arch}", 13 | "host": "https://github.com/TryGhost/node-sqlite3/releases/download/", 14 | "remote_path": "v{version}", 15 | "package_name": "napi-v{napi_build_version}-{platform}-{libc}-{arch}.tar.gz", 16 | "napi_versions": [ 17 | 3, 18 | 6 19 | ] 20 | }, 21 | "contributors": [ 22 | "Konstantin Käfer ", 23 | "Dane Springmeyer ", 24 | "Will White ", 25 | "Orlando Vazquez ", 26 | "Artem Kustikov ", 27 | "Eric Fredricksen ", 28 | "John Wright ", 29 | "Ryan Dahl ", 30 | "Tom MacWright ", 31 | "Carter Thaxton ", 32 | "Audrius Kažukauskas ", 33 | "Johannes Schauer ", 34 | "Nathan Rajlich ", 35 | "AJ ONeal ", 36 | "Mithgol", 37 | "Ben Noordhuis " 38 | ], 39 | "files": [ 40 | "binding.gyp", 41 | "deps/", 42 | "lib/*.js", 43 | "lib/*.d.ts", 44 | "src/" 45 | ], 46 | "repository": { 47 | "type": "git", 48 | "url": "https://github.com/microsoft/vscode-node-sqlite3.git" 49 | }, 50 | "dependencies": { 51 | "node-addon-api": "^8.2.0", 52 | "tar": "^6.1.11" 53 | }, 54 | "devDependencies": { 55 | "electron": "32.1.2", 56 | "eslint": "^7.32.0", 57 | "mocha": "7.2.0" 58 | }, 59 | "scripts": { 60 | "pretest": "node test/support/createdb.js", 61 | "test": "mocha -R spec --timeout 480000", 62 | "pack": "node-pre-gyp package" 63 | }, 64 | "license": "BSD-3-Clause", 65 | "keywords": [ 66 | "sql", 67 | "sqlite", 68 | "sqlite3", 69 | "database" 70 | ], 71 | "main": "./lib/sqlite3", 72 | "types": "./lib/sqlite3.d.ts", 73 | "renovate": { 74 | "extends": [ 75 | "@tryghost:base" 76 | ] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /scripts/test-electron.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | pushd %~dp0\.. 5 | 6 | set ELECTRON_RUN_AS_NODE=1 7 | 8 | call .\node_modules\.bin\electron .\test\support/createdb.js 9 | call .\node_modules\.bin\electron .\node_modules\.bin\mocha -R spec --timeout 480000 10 | 11 | popd 12 | 13 | endlocal -------------------------------------------------------------------------------- /scripts/test-electron.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export ELECTRON_RUN_AS_NODE=1 4 | 5 | ./node_modules/.bin/electron ./test/support/createdb.js 6 | ./node_modules/.bin/electron ./node_modules/.bin/mocha -R spec --timeout 480000 -------------------------------------------------------------------------------- /src/async.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_SQLITE3_SRC_ASYNC_H 2 | #define NODE_SQLITE3_SRC_ASYNC_H 3 | 4 | #include 5 | #include 6 | 7 | #include "threading.h" 8 | 9 | // Generic uv_async handler. 10 | template class Async { 11 | typedef void (*Callback)(Parent* parent, Item* item); 12 | 13 | protected: 14 | uv_async_t watcher; 15 | NODE_SQLITE3_MUTEX_t 16 | std::vector data; 17 | Callback callback; 18 | public: 19 | Parent* parent; 20 | 21 | public: 22 | Async(Parent* parent_, Callback cb_) 23 | : callback(cb_), parent(parent_) { 24 | watcher.data = this; 25 | NODE_SQLITE3_MUTEX_INIT 26 | uv_loop_t *loop; 27 | napi_get_uv_event_loop(parent_->Env(), &loop); 28 | uv_async_init(loop, &watcher, reinterpret_cast(listener)); 29 | } 30 | 31 | static void listener(uv_async_t* handle) { 32 | Async* async = static_cast(handle->data); 33 | std::vector rows; 34 | NODE_SQLITE3_MUTEX_LOCK(&async->mutex) 35 | rows.swap(async->data); 36 | NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex) 37 | for (size_t i = 0, size = rows.size(); i < size; i++) { 38 | async->callback(async->parent, rows[i]); 39 | } 40 | } 41 | 42 | static void close(uv_handle_t* handle) { 43 | assert(handle != NULL); 44 | assert(handle->data != NULL); 45 | Async* async = static_cast(handle->data); 46 | delete async; 47 | } 48 | 49 | void finish() { 50 | // Need to call the listener again to ensure all items have been 51 | // processed. Is this a bug in uv_async? Feels like uv_close 52 | // should handle that. 53 | listener(&watcher); 54 | uv_close((uv_handle_t*)&watcher, close); 55 | } 56 | 57 | void add(Item* item) { 58 | NODE_SQLITE3_MUTEX_LOCK(&mutex); 59 | data.push_back(item); 60 | NODE_SQLITE3_MUTEX_UNLOCK(&mutex) 61 | } 62 | 63 | void send() { 64 | uv_async_send(&watcher); 65 | } 66 | 67 | void send(Item* item) { 68 | add(item); 69 | send(); 70 | } 71 | 72 | ~Async() { 73 | NODE_SQLITE3_MUTEX_DESTROY 74 | } 75 | }; 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/backup.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_SQLITE3_SRC_BACKUP_H 2 | #define NODE_SQLITE3_SRC_BACKUP_H 3 | 4 | #include "database.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | using namespace Napi; 14 | 15 | namespace node_sqlite3 { 16 | 17 | /** 18 | * 19 | * A class for managing an sqlite3_backup object. For consistency 20 | * with other node-sqlite3 classes, it maintains an internal queue 21 | * of calls. 22 | * 23 | * Intended usage from node: 24 | * 25 | * var db = new sqlite3.Database('live.db'); 26 | * var backup = db.backup('backup.db'); 27 | * ... 28 | * // in event loop, move backup forward when we have time. 29 | * if (backup.idle) { backup.step(NPAGES); } 30 | * if (backup.completed) { ... success ... } 31 | * if (backup.failed) { ... sadness ... } 32 | * // do other work in event loop - fine to modify live.db 33 | * ... 34 | * 35 | * Here is how sqlite's backup api is exposed: 36 | * 37 | * - `sqlite3_backup_init`: This is implemented as 38 | * `db.backup(filename, [callback])` or 39 | * `db.backup(filename, destDbName, sourceDbName, filenameIsDest, [callback])`. 40 | * - `sqlite3_backup_step`: `backup.step(pages, [callback])`. 41 | * - `sqlite3_backup_finish`: `backup.finish([callback])`. 42 | * - `sqlite3_backup_remaining`: `backup.remaining`. 43 | * - `sqlite3_backup_pagecount`: `backup.pageCount`. 44 | * 45 | * There are the following read-only properties: 46 | * 47 | * - `backup.completed` is set to `true` when the backup 48 | * succeeeds. 49 | * - `backup.failed` is set to `true` when the backup 50 | * has a fatal error. 51 | * - `backup.idle` is set to `true` when no operation 52 | * is currently in progress or queued for the backup. 53 | * - `backup.remaining` is an integer with the remaining 54 | * number of pages after the last call to `backup.step` 55 | * (-1 if `step` not yet called). 56 | * - `backup.pageCount` is an integer with the total number 57 | * of pages measured during the last call to `backup.step` 58 | * (-1 if `step` not yet called). 59 | * 60 | * There is the following writable property: 61 | * 62 | * - `backup.retryErrors`: an array of sqlite3 error codes 63 | * that are treated as non-fatal - meaning, if they occur, 64 | * backup.failed is not set, and the backup may continue. 65 | * By default, this is `[sqlite3.BUSY, sqlite3.LOCKED]`. 66 | * 67 | * The `db.backup(filename, [callback])` shorthand is sufficient 68 | * for making a backup of a database opened by node-sqlite3. If 69 | * using attached or temporary databases, or moving data in the 70 | * opposite direction, the more complete (but daunting) 71 | * `db.backup(filename, destDbName, sourceDbName, filenameIsDest, [callback])` 72 | * signature is provided. 73 | * 74 | * A backup will finish automatically when it succeeds or a fatal 75 | * error occurs, meaning it is not necessary to call `db.finish()`. 76 | * By default, SQLITE_LOCKED and SQLITE_BUSY errors are not 77 | * treated as failures, and the backup will continue if they 78 | * occur. The set of errors that are tolerated can be controlled 79 | * by setting `backup.retryErrors`. To disable automatic 80 | * finishing and stick strictly to sqlite's raw api, set 81 | * `backup.retryErrors` to `[]`. In that case, it is necessary 82 | * to call `backup.finish()`. 83 | * 84 | * In the same way as node-sqlite3 databases and statements, 85 | * backup methods can be called safely without callbacks, due 86 | * to an internal call queue. So for example this naive code 87 | * will correctly back up a db, if there are no errors: 88 | * 89 | * var backup = db.backup('backup.db'); 90 | * backup.step(-1); 91 | * backup.finish(); 92 | * 93 | */ 94 | class Backup : public Napi::ObjectWrap { 95 | public: 96 | static Napi::Object Init(Napi::Env env, Napi::Object exports); 97 | 98 | struct Baton { 99 | napi_async_work request = NULL; 100 | Backup* backup; 101 | Napi::FunctionReference callback; 102 | 103 | Baton(Backup* backup_, Napi::Function cb_) : backup(backup_) { 104 | backup->Ref(); 105 | callback.Reset(cb_, 1); 106 | } 107 | virtual ~Baton() { 108 | if (request) napi_delete_async_work(backup->Env(), request); 109 | backup->Unref(); 110 | callback.Reset(); 111 | } 112 | }; 113 | 114 | struct InitializeBaton : Database::Baton { 115 | Backup* backup; 116 | std::string filename; 117 | std::string sourceName; 118 | std::string destName; 119 | bool filenameIsDest; 120 | InitializeBaton(Database* db_, Napi::Function cb_, Backup* backup_) : 121 | Baton(db_, cb_), backup(backup_), filenameIsDest(true) { 122 | backup->Ref(); 123 | } 124 | virtual ~InitializeBaton() { 125 | backup->Unref(); 126 | if (!db->IsOpen() && db->IsLocked()) { 127 | // The database handle was closed before the backup could be opened. 128 | backup->FinishAll(); 129 | } 130 | } 131 | }; 132 | 133 | struct StepBaton : Baton { 134 | int pages; 135 | std::set retryErrorsSet; 136 | StepBaton(Backup* backup_, Napi::Function cb_, int pages_) : 137 | Baton(backup_, cb_), pages(pages_) {} 138 | }; 139 | 140 | typedef void (*Work_Callback)(Baton* baton); 141 | 142 | struct Call { 143 | Call(Work_Callback cb_, Baton* baton_) : callback(cb_), baton(baton_) {}; 144 | Work_Callback callback; 145 | Baton* baton; 146 | }; 147 | 148 | void init(Database* db_) { 149 | db = db_; 150 | _handle = NULL; 151 | _otherDb = NULL; 152 | _destDb = NULL; 153 | inited = false; 154 | locked = true; 155 | completed = false; 156 | failed = false; 157 | remaining = -1; 158 | pageCount = -1; 159 | finished = false; 160 | db->Ref(); 161 | } 162 | 163 | Backup(const Napi::CallbackInfo& info); 164 | 165 | ~Backup() { 166 | if (!finished) { 167 | FinishAll(); 168 | } 169 | retryErrors.Reset(); 170 | } 171 | 172 | WORK_DEFINITION(Step); 173 | WORK_DEFINITION(Finish); 174 | Napi::Value IdleGetter(const Napi::CallbackInfo& info); 175 | Napi::Value CompletedGetter(const Napi::CallbackInfo& info); 176 | Napi::Value FailedGetter(const Napi::CallbackInfo& info); 177 | Napi::Value PageCountGetter(const Napi::CallbackInfo& info); 178 | Napi::Value RemainingGetter(const Napi::CallbackInfo& info); 179 | Napi::Value FatalErrorGetter(const Napi::CallbackInfo& info); 180 | Napi::Value RetryErrorGetter(const Napi::CallbackInfo& info); 181 | 182 | void FatalErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value); 183 | void RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value); 184 | 185 | protected: 186 | static void Work_BeginInitialize(Database::Baton* baton); 187 | static void Work_Initialize(napi_env env, void* data); 188 | static void Work_AfterInitialize(napi_env env, napi_status status, void* data); 189 | 190 | void Schedule(Work_Callback callback, Baton* baton); 191 | void Process(); 192 | void CleanQueue(); 193 | template static void Error(T* baton); 194 | 195 | void FinishAll(); 196 | void FinishSqlite(); 197 | void GetRetryErrors(std::set& retryErrorsSet); 198 | 199 | Database* db; 200 | 201 | sqlite3_backup* _handle; 202 | sqlite3* _otherDb; 203 | sqlite3* _destDb; 204 | int status; 205 | std::string message; 206 | 207 | bool inited; 208 | bool locked; 209 | bool completed; 210 | bool failed; 211 | int remaining; 212 | int pageCount; 213 | bool finished; 214 | std::queue queue; 215 | 216 | Napi::Reference retryErrors; 217 | }; 218 | 219 | } 220 | 221 | #endif 222 | -------------------------------------------------------------------------------- /src/database.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef NODE_SQLITE3_SRC_DATABASE_H 3 | #define NODE_SQLITE3_SRC_DATABASE_H 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "async.h" 14 | 15 | using namespace Napi; 16 | 17 | namespace node_sqlite3 { 18 | 19 | class Database; 20 | 21 | 22 | class Database : public Napi::ObjectWrap { 23 | public: 24 | #if NAPI_VERSION < 6 25 | static Napi::FunctionReference constructor; 26 | #endif 27 | static Napi::Object Init(Napi::Env env, Napi::Object exports); 28 | 29 | static inline bool HasInstance(Napi::Value val) { 30 | Napi::Env env = val.Env(); 31 | Napi::HandleScope scope(env); 32 | if (!val.IsObject()) return false; 33 | Napi::Object obj = val.As(); 34 | #if NAPI_VERSION < 6 35 | return obj.InstanceOf(constructor.Value()); 36 | #else 37 | Napi::FunctionReference* constructor = 38 | env.GetInstanceData(); 39 | return obj.InstanceOf(constructor->Value()); 40 | #endif 41 | } 42 | 43 | struct Baton { 44 | napi_async_work request = NULL; 45 | Database* db; 46 | Napi::FunctionReference callback; 47 | int status; 48 | std::string message; 49 | 50 | Baton(Database* db_, Napi::Function cb_) : 51 | db(db_), status(SQLITE_OK) { 52 | db->Ref(); 53 | if (!cb_.IsUndefined() && cb_.IsFunction()) { 54 | callback.Reset(cb_, 1); 55 | } 56 | } 57 | virtual ~Baton() { 58 | if (request) napi_delete_async_work(db->Env(), request); 59 | db->Unref(); 60 | callback.Reset(); 61 | } 62 | }; 63 | 64 | struct OpenBaton : Baton { 65 | std::string filename; 66 | int mode; 67 | OpenBaton(Database* db_, Napi::Function cb_, const char* filename_, int mode_) : 68 | Baton(db_, cb_), filename(filename_), mode(mode_) {} 69 | }; 70 | 71 | struct ExecBaton : Baton { 72 | std::string sql; 73 | ExecBaton(Database* db_, Napi::Function cb_, const char* sql_) : 74 | Baton(db_, cb_), sql(sql_) {} 75 | }; 76 | 77 | struct LoadExtensionBaton : Baton { 78 | std::string filename; 79 | LoadExtensionBaton(Database* db_, Napi::Function cb_, const char* filename_) : 80 | Baton(db_, cb_), filename(filename_) {} 81 | }; 82 | 83 | struct LimitBaton : Baton { 84 | int id; 85 | int value; 86 | LimitBaton(Database* db_, Napi::Function cb_, int id_, int value_) : 87 | Baton(db_, cb_), id(id_), value(value_) {} 88 | }; 89 | 90 | typedef void (*Work_Callback)(Baton* baton); 91 | 92 | struct Call { 93 | Call(Work_Callback cb_, Baton* baton_, bool exclusive_ = false) : 94 | callback(cb_), exclusive(exclusive_), baton(baton_) {}; 95 | Work_Callback callback; 96 | bool exclusive; 97 | Baton* baton; 98 | }; 99 | 100 | struct ProfileInfo { 101 | std::string sql; 102 | sqlite3_int64 nsecs; 103 | }; 104 | 105 | struct UpdateInfo { 106 | int type; 107 | std::string database; 108 | std::string table; 109 | sqlite3_int64 rowid; 110 | }; 111 | 112 | bool IsOpen() { return open; } 113 | bool IsLocked() { return locked; } 114 | 115 | typedef Async AsyncTrace; 116 | typedef Async AsyncProfile; 117 | typedef Async AsyncUpdate; 118 | 119 | friend class Statement; 120 | friend class Backup; 121 | 122 | void init() { 123 | _handle = NULL; 124 | open = false; 125 | closing = false; 126 | locked = false; 127 | pending = 0; 128 | serialize = false; 129 | debug_trace = NULL; 130 | debug_profile = NULL; 131 | update_event = NULL; 132 | } 133 | 134 | Database(const Napi::CallbackInfo& info); 135 | 136 | ~Database() { 137 | RemoveCallbacks(); 138 | sqlite3_close(_handle); 139 | _handle = NULL; 140 | open = false; 141 | } 142 | 143 | protected: 144 | static void Work_BeginOpen(Baton* baton); 145 | static void Work_Open(napi_env env, void* data); 146 | static void Work_AfterOpen(napi_env env, napi_status status, void* data); 147 | 148 | Napi::Value OpenGetter(const Napi::CallbackInfo& info); 149 | 150 | void Schedule(Work_Callback callback, Baton* baton, bool exclusive = false); 151 | void Process(); 152 | 153 | Napi::Value Exec(const Napi::CallbackInfo& info); 154 | static void Work_BeginExec(Baton* baton); 155 | static void Work_Exec(napi_env env, void* data); 156 | static void Work_AfterExec(napi_env env, napi_status status, void* data); 157 | 158 | Napi::Value Wait(const Napi::CallbackInfo& info); 159 | static void Work_Wait(Baton* baton); 160 | 161 | Napi::Value Close(const Napi::CallbackInfo& info); 162 | static void Work_BeginClose(Baton* baton); 163 | static void Work_Close(napi_env env, void* data); 164 | static void Work_AfterClose(napi_env env, napi_status status, void* data); 165 | 166 | Napi::Value LoadExtension(const Napi::CallbackInfo& info); 167 | static void Work_BeginLoadExtension(Baton* baton); 168 | static void Work_LoadExtension(napi_env env, void* data); 169 | static void Work_AfterLoadExtension(napi_env env, napi_status status, void* data); 170 | 171 | Napi::Value Serialize(const Napi::CallbackInfo& info); 172 | Napi::Value Parallelize(const Napi::CallbackInfo& info); 173 | 174 | Napi::Value Configure(const Napi::CallbackInfo& info); 175 | 176 | Napi::Value Interrupt(const Napi::CallbackInfo& info); 177 | 178 | static void SetBusyTimeout(Baton* baton); 179 | static void SetLimit(Baton* baton); 180 | 181 | static void RegisterTraceCallback(Baton* baton); 182 | static void TraceCallback(void* db, const char* sql); 183 | static void TraceCallback(Database* db, std::string* sql); 184 | 185 | static void RegisterProfileCallback(Baton* baton); 186 | static void ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs); 187 | static void ProfileCallback(Database* db, ProfileInfo* info); 188 | 189 | static void RegisterUpdateCallback(Baton* baton); 190 | static void UpdateCallback(void* db, int type, const char* database, const char* table, sqlite3_int64 rowid); 191 | static void UpdateCallback(Database* db, UpdateInfo* info); 192 | 193 | void RemoveCallbacks(); 194 | 195 | protected: 196 | sqlite3* _handle; 197 | 198 | bool open; 199 | bool closing; 200 | bool locked; 201 | unsigned int pending; 202 | 203 | bool serialize; 204 | 205 | std::queue queue; 206 | 207 | AsyncTrace* debug_trace; 208 | AsyncProfile* debug_profile; 209 | AsyncUpdate* update_event; 210 | }; 211 | 212 | } 213 | 214 | #endif 215 | -------------------------------------------------------------------------------- /src/gcc-preinclude.h: -------------------------------------------------------------------------------- 1 | // http://web.archive.org/web/20140401031018/http://rjpower9000.wordpress.com:80/2012/04/09/fun-with-shared-libraries-version-glibc_2-14-not-found/ 2 | 3 | #if defined(__linux__) 4 | 5 | #define _GNU_SOURCE 6 | #include 7 | #undef _GNU_SOURCE 8 | 9 | #if defined(__USE_GNU) 10 | 11 | #if defined(__x86_64__) 12 | __asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); 13 | __asm__(".symver exp,exp@GLIBC_2.2.5"); 14 | __asm__(".symver log,log@GLIBC_2.2.5"); 15 | __asm__(".symver pow,pow@GLIBC_2.2.5"); 16 | __asm__(".symver fcntl64,fcntl@GLIBC_2.2.5"); 17 | #endif 18 | 19 | #if defined(__aarch64__) || defined(_M_ARM64) 20 | __asm__(".symver memcpy,memcpy@GLIBC_2.17"); 21 | __asm__(".symver exp,exp@GLIBC_2.17"); 22 | __asm__(".symver log,log@GLIBC_2.17"); 23 | __asm__(".symver pow,pow@GLIBC_2.17"); 24 | __asm__(".symver fcntl64,fcntl@GLIBC_2.17"); 25 | #endif 26 | 27 | #endif 28 | #endif 29 | -------------------------------------------------------------------------------- /src/macros.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_SQLITE3_SRC_MACROS_H 2 | #define NODE_SQLITE3_SRC_MACROS_H 3 | 4 | const char* sqlite_code_string(int code); 5 | const char* sqlite_authorizer_string(int type); 6 | #include 7 | 8 | // TODO: better way to work around StringConcat? 9 | #include 10 | inline Napi::String StringConcat(Napi::Value str1, Napi::Value str2) { 11 | return Napi::String::New(str1.Env(), str1.As().Utf8Value() + 12 | str2.As().Utf8Value() ); 13 | } 14 | 15 | // A Napi substitute IsInt32() 16 | inline bool OtherIsInt(Napi::Number source) { 17 | double orig_val = source.DoubleValue(); 18 | double int_val = (double)source.Int32Value(); 19 | if (orig_val == int_val) { 20 | return true; 21 | } else { 22 | return false; 23 | } 24 | } 25 | 26 | #define REQUIRE_ARGUMENTS(n) \ 27 | if (info.Length() < (n)) { \ 28 | Napi::TypeError::New(env, "Expected " #n "arguments").ThrowAsJavaScriptException(); \ 29 | return env.Null(); \ 30 | } 31 | 32 | 33 | #define REQUIRE_ARGUMENT_EXTERNAL(i, var) \ 34 | if (info.Length() <= (i) || !info[i].IsExternal()) { \ 35 | Napi::TypeError::New(env, "Argument " #i " invalid").ThrowAsJavaScriptException(); \ 36 | return env.Null(); \ 37 | } \ 38 | Napi::External var = info[i].As(); 39 | 40 | 41 | #define REQUIRE_ARGUMENT_FUNCTION(i, var) \ 42 | if (info.Length() <= (i) || !info[i].IsFunction()) { \ 43 | Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ 44 | return env.Null(); \ 45 | } \ 46 | Napi::Function var = info[i].As(); 47 | 48 | 49 | #define REQUIRE_ARGUMENT_STRING(i, var) \ 50 | if (info.Length() <= (i) || !info[i].IsString()) { \ 51 | Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); \ 52 | return env.Null(); \ 53 | } \ 54 | std::string var = info[i].As(); 55 | 56 | #define REQUIRE_ARGUMENT_INTEGER(i, var) \ 57 | if (info.Length() <= (i) || !info[i].IsNumber()) { \ 58 | Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ 59 | return env.Null(); \ 60 | } \ 61 | int var(info[i].As().Int32Value()); 62 | 63 | #define OPTIONAL_ARGUMENT_FUNCTION(i, var) \ 64 | Napi::Function var; \ 65 | if (info.Length() > i && !info[i].IsUndefined()) { \ 66 | if (!info[i].IsFunction()) { \ 67 | Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ 68 | return env.Null(); \ 69 | } \ 70 | var = info[i].As(); \ 71 | } 72 | 73 | 74 | #define OPTIONAL_ARGUMENT_INTEGER(i, var, default) \ 75 | int var; \ 76 | if (info.Length() <= (i)) { \ 77 | var = (default); \ 78 | } \ 79 | else if (info[i].IsNumber()) { \ 80 | if (OtherIsInt(info[i].As())) { \ 81 | var = info[i].As().Int32Value(); \ 82 | } \ 83 | } \ 84 | else { \ 85 | Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ 86 | return env.Null(); \ 87 | } 88 | 89 | 90 | #define DEFINE_CONSTANT_INTEGER(target, constant, name) \ 91 | Napi::PropertyDescriptor::Value(#name, Napi::Number::New(env, constant), \ 92 | static_cast(napi_enumerable | napi_configurable)), 93 | 94 | #define DEFINE_CONSTANT_STRING(target, constant, name) \ 95 | Napi::PropertyDescriptor::Value(#name, Napi::String::New(env, constant), \ 96 | static_cast(napi_enumerable | napi_configurable)), 97 | 98 | #define EXCEPTION(msg, errno, name) \ 99 | Napi::Value name = Napi::Error::New(env, \ 100 | StringConcat( \ 101 | StringConcat( \ 102 | Napi::String::New(env, sqlite_code_string(errno)), \ 103 | Napi::String::New(env, ": ") \ 104 | ), \ 105 | (msg) \ 106 | ).Utf8Value() \ 107 | ).Value(); \ 108 | Napi::Object name ##_obj = name.As(); \ 109 | (name ##_obj).Set( Napi::String::New(env, "errno"), Napi::Number::New(env, errno)); \ 110 | (name ##_obj).Set( Napi::String::New(env, "code"), \ 111 | Napi::String::New(env, sqlite_code_string(errno))); 112 | 113 | 114 | #define EMIT_EVENT(obj, argc, argv) \ 115 | TRY_CATCH_CALL((obj), \ 116 | (obj).Get("emit").As(),\ 117 | argc, argv \ 118 | ); 119 | 120 | // The Mac OS compiler complains when argv is NULL unless we 121 | // first assign it to a locally defined variable. 122 | #define TRY_CATCH_CALL(context, callback, argc, argv, ...) \ 123 | Napi::Value* passed_argv = argv;\ 124 | std::vector args;\ 125 | if ((argc != 0) && (passed_argv != NULL)) {\ 126 | args.assign(passed_argv, passed_argv + argc);\ 127 | }\ 128 | Napi::Value res = (callback).Call(Napi::Value(context), args); \ 129 | if (res.IsEmpty()) return __VA_ARGS__; 130 | 131 | #define WORK_DEFINITION(name) \ 132 | Napi::Value name(const Napi::CallbackInfo& info); \ 133 | static void Work_Begin##name(Baton* baton); \ 134 | static void Work_##name(napi_env env, void* data); \ 135 | static void Work_After##name(napi_env env, napi_status status, void* data); 136 | 137 | #define STATEMENT_BEGIN(type) \ 138 | assert(baton); \ 139 | assert(baton->stmt); \ 140 | assert(!baton->stmt->locked); \ 141 | assert(!baton->stmt->finalized); \ 142 | assert(baton->stmt->prepared); \ 143 | baton->stmt->locked = true; \ 144 | baton->stmt->db->pending++; \ 145 | Napi::Env env = baton->stmt->Env(); \ 146 | int status = napi_create_async_work( \ 147 | env, NULL, Napi::String::New(env, "sqlite3.Statement."#type), \ 148 | Work_##type, Work_After##type, baton, &baton->request \ 149 | ); \ 150 | assert(status == 0); \ 151 | napi_queue_async_work(env, baton->request); 152 | 153 | #define STATEMENT_INIT(type) \ 154 | type* baton = static_cast(data); \ 155 | Statement* stmt = baton->stmt; 156 | 157 | #define STATEMENT_MUTEX(name) \ 158 | if (!stmt->db->_handle) { \ 159 | stmt->status = SQLITE_MISUSE; \ 160 | stmt->message = "Database handle is closed"; \ 161 | return; \ 162 | } \ 163 | sqlite3_mutex* name = sqlite3_db_mutex(stmt->db->_handle); 164 | 165 | #define STATEMENT_END() \ 166 | assert(stmt->locked); \ 167 | assert(stmt->db->pending); \ 168 | stmt->locked = false; \ 169 | stmt->db->pending--; \ 170 | stmt->Process(); \ 171 | stmt->db->Process(); 172 | 173 | #define BACKUP_BEGIN(type) \ 174 | assert(baton); \ 175 | assert(baton->backup); \ 176 | assert(!baton->backup->locked); \ 177 | assert(!baton->backup->finished); \ 178 | assert(baton->backup->inited); \ 179 | baton->backup->locked = true; \ 180 | baton->backup->db->pending++; \ 181 | Napi::Env env = baton->backup->Env(); \ 182 | int status = napi_create_async_work( \ 183 | env, NULL, Napi::String::New(env, "sqlite3.Backup."#type), \ 184 | Work_##type, Work_After##type, baton, &baton->request \ 185 | ); \ 186 | assert(status == 0); \ 187 | napi_queue_async_work(env, baton->request); 188 | 189 | #define BACKUP_INIT(type) \ 190 | type* baton = static_cast(data); \ 191 | Backup* backup = baton->backup; 192 | 193 | #define BACKUP_END() \ 194 | assert(backup->locked); \ 195 | assert(backup->db->pending); \ 196 | backup->locked = false; \ 197 | backup->db->pending--; \ 198 | backup->Process(); \ 199 | backup->db->Process(); 200 | 201 | #define DELETE_FIELD(field) \ 202 | if (field != NULL) { \ 203 | switch ((field)->type) { \ 204 | case SQLITE_INTEGER: delete (Values::Integer*)(field); break; \ 205 | case SQLITE_FLOAT: delete (Values::Float*)(field); break; \ 206 | case SQLITE_TEXT: delete (Values::Text*)(field); break; \ 207 | case SQLITE_BLOB: delete (Values::Blob*)(field); break; \ 208 | case SQLITE_NULL: delete (Values::Null*)(field); break; \ 209 | } \ 210 | } 211 | 212 | #endif 213 | -------------------------------------------------------------------------------- /src/node_sqlite3.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "macros.h" 8 | #include "database.h" 9 | #include "statement.h" 10 | #include "backup.h" 11 | 12 | using namespace node_sqlite3; 13 | 14 | namespace { 15 | 16 | Napi::Object RegisterModule(Napi::Env env, Napi::Object exports) { 17 | Napi::HandleScope scope(env); 18 | 19 | Database::Init(env, exports); 20 | Statement::Init(env, exports); 21 | Backup::Init(env, exports); 22 | 23 | exports.DefineProperties({ 24 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_READONLY, OPEN_READONLY) 25 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_READWRITE, OPEN_READWRITE) 26 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_CREATE, OPEN_CREATE) 27 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_FULLMUTEX, OPEN_FULLMUTEX) 28 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_URI, OPEN_URI) 29 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_SHAREDCACHE, OPEN_SHAREDCACHE) 30 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_PRIVATECACHE, OPEN_PRIVATECACHE) 31 | DEFINE_CONSTANT_STRING(exports, SQLITE_VERSION, VERSION) 32 | #ifdef SQLITE_SOURCE_ID 33 | DEFINE_CONSTANT_STRING(exports, SQLITE_SOURCE_ID, SOURCE_ID) 34 | #endif 35 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_VERSION_NUMBER, VERSION_NUMBER) 36 | 37 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_OK, OK) 38 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_ERROR, ERROR) 39 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_INTERNAL, INTERNAL) 40 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_PERM, PERM) 41 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_ABORT, ABORT) 42 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_BUSY, BUSY) 43 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LOCKED, LOCKED) 44 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOMEM, NOMEM) 45 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_READONLY, READONLY) 46 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_INTERRUPT, INTERRUPT) 47 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_IOERR, IOERR) 48 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_CORRUPT, CORRUPT) 49 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOTFOUND, NOTFOUND) 50 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_FULL, FULL) 51 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_CANTOPEN, CANTOPEN) 52 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_PROTOCOL, PROTOCOL) 53 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_EMPTY, EMPTY) 54 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_SCHEMA, SCHEMA) 55 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_TOOBIG, TOOBIG) 56 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_CONSTRAINT, CONSTRAINT) 57 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_MISMATCH, MISMATCH) 58 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_MISUSE, MISUSE) 59 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOLFS, NOLFS) 60 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_AUTH, AUTH) 61 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_FORMAT, FORMAT) 62 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_RANGE, RANGE) 63 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOTADB, NOTADB) 64 | 65 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_LENGTH, LIMIT_LENGTH) 66 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_SQL_LENGTH, LIMIT_SQL_LENGTH) 67 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_COLUMN, LIMIT_COLUMN) 68 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_EXPR_DEPTH, LIMIT_EXPR_DEPTH) 69 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_COMPOUND_SELECT, LIMIT_COMPOUND_SELECT) 70 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_VDBE_OP, LIMIT_VDBE_OP) 71 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_FUNCTION_ARG, LIMIT_FUNCTION_ARG) 72 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_ATTACHED, LIMIT_ATTACHED) 73 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, LIMIT_LIKE_PATTERN_LENGTH) 74 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_VARIABLE_NUMBER, LIMIT_VARIABLE_NUMBER) 75 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_TRIGGER_DEPTH, LIMIT_TRIGGER_DEPTH) 76 | DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_WORKER_THREADS, LIMIT_WORKER_THREADS) 77 | }); 78 | 79 | return exports; 80 | } 81 | 82 | } 83 | 84 | const char* sqlite_code_string(int code) { 85 | switch (code) { 86 | 87 | // Basic Codes 88 | case SQLITE_OK: return "SQLITE_OK"; 89 | case SQLITE_ERROR: return "SQLITE_ERROR"; 90 | case SQLITE_INTERNAL: return "SQLITE_INTERNAL"; 91 | case SQLITE_PERM: return "SQLITE_PERM"; 92 | case SQLITE_ABORT: return "SQLITE_ABORT"; 93 | case SQLITE_BUSY: return "SQLITE_BUSY"; 94 | case SQLITE_LOCKED: return "SQLITE_LOCKED"; 95 | case SQLITE_NOMEM: return "SQLITE_NOMEM"; 96 | case SQLITE_READONLY: return "SQLITE_READONLY"; 97 | case SQLITE_INTERRUPT: return "SQLITE_INTERRUPT"; 98 | case SQLITE_IOERR: return "SQLITE_IOERR"; 99 | case SQLITE_CORRUPT: return "SQLITE_CORRUPT"; 100 | case SQLITE_NOTFOUND: return "SQLITE_NOTFOUND"; 101 | case SQLITE_FULL: return "SQLITE_FULL"; 102 | case SQLITE_CANTOPEN: return "SQLITE_CANTOPEN"; 103 | case SQLITE_PROTOCOL: return "SQLITE_PROTOCOL"; 104 | case SQLITE_EMPTY: return "SQLITE_EMPTY"; 105 | case SQLITE_SCHEMA: return "SQLITE_SCHEMA"; 106 | case SQLITE_TOOBIG: return "SQLITE_TOOBIG"; 107 | case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT"; 108 | case SQLITE_MISMATCH: return "SQLITE_MISMATCH"; 109 | case SQLITE_MISUSE: return "SQLITE_MISUSE"; 110 | case SQLITE_NOLFS: return "SQLITE_NOLFS"; 111 | case SQLITE_AUTH: return "SQLITE_AUTH"; 112 | case SQLITE_FORMAT: return "SQLITE_FORMAT"; 113 | case SQLITE_RANGE: return "SQLITE_RANGE"; 114 | case SQLITE_NOTADB: return "SQLITE_NOTADB"; 115 | case SQLITE_ROW: return "SQLITE_ROW"; 116 | case SQLITE_DONE: return "SQLITE_DONE"; 117 | 118 | // Extended Codes 119 | case SQLITE_ERROR_MISSING_COLLSEQ: return "SQLITE_ERROR_MISSING_COLLSEQ"; 120 | case SQLITE_ERROR_RETRY: return "SQLITE_ERROR_RETRY"; 121 | case SQLITE_IOERR_READ: return "SQLITE_IOERR_READ"; 122 | case SQLITE_IOERR_SHORT_READ: return "SQLITE_IOERR_SHORT_READ"; 123 | case SQLITE_IOERR_WRITE: return "SQLITE_IOERR_WRITE"; 124 | case SQLITE_IOERR_FSYNC: return "SQLITE_IOERR_FSYNC"; 125 | case SQLITE_IOERR_DIR_FSYNC: return "SQLITE_IOERR_DIR_FSYNC"; 126 | case SQLITE_IOERR_TRUNCATE: return "SQLITE_IOERR_TRUNCATE"; 127 | case SQLITE_IOERR_FSTAT: return "SQLITE_IOERR_FSTAT"; 128 | case SQLITE_IOERR_UNLOCK: return "SQLITE_IOERR_UNLOCK"; 129 | case SQLITE_IOERR_RDLOCK: return "SQLITE_IOERR_RDLOCK"; 130 | case SQLITE_IOERR_DELETE: return "SQLITE_IOERR_DELETE"; 131 | case SQLITE_IOERR_BLOCKED: return "SQLITE_IOERR_BLOCKED"; 132 | case SQLITE_IOERR_NOMEM: return "SQLITE_IOERR_NOMEM"; 133 | case SQLITE_IOERR_ACCESS: return "SQLITE_IOERR_ACCESS"; 134 | case SQLITE_IOERR_CHECKRESERVEDLOCK: return "SQLITE_IOERR_CHECKRESERVEDLOCK"; 135 | case SQLITE_IOERR_LOCK: return "SQLITE_IOERR_LOCK"; 136 | case SQLITE_IOERR_CLOSE: return "SQLITE_IOERR_CLOSE"; 137 | case SQLITE_IOERR_DIR_CLOSE: return "SQLITE_IOERR_DIR_CLOSE"; 138 | case SQLITE_IOERR_SHMOPEN: return "SQLITE_IOERR_SHMOPEN"; 139 | case SQLITE_IOERR_SHMSIZE: return "SQLITE_IOERR_SHMSIZE"; 140 | case SQLITE_IOERR_SHMLOCK: return "SQLITE_IOERR_SHMLOCK"; 141 | case SQLITE_IOERR_SHMMAP: return "SQLITE_IOERR_SHMMAP"; 142 | case SQLITE_IOERR_SEEK: return "SQLITE_IOERR_SEEK"; 143 | case SQLITE_IOERR_DELETE_NOENT: return "SQLITE_IOERR_DELETE_NOENT"; 144 | case SQLITE_IOERR_MMAP: return "SQLITE_IOERR_MMAP"; 145 | case SQLITE_IOERR_GETTEMPPATH: return "SQLITE_IOERR_GETTEMPPATH"; 146 | case SQLITE_IOERR_CONVPATH: return "SQLITE_IOERR_CONVPATH"; 147 | case SQLITE_IOERR_VNODE: return "SQLITE_IOERR_VNODE"; 148 | case SQLITE_IOERR_AUTH: return "SQLITE_IOERR_AUTH"; 149 | case SQLITE_IOERR_BEGIN_ATOMIC: return "SQLITE_IOERR_BEGIN_ATOMIC"; 150 | case SQLITE_IOERR_COMMIT_ATOMIC: return "SQLITE_IOERR_COMMIT_ATOMIC"; 151 | case SQLITE_IOERR_ROLLBACK_ATOMIC: return "SQLITE_IOERR_ROLLBACK_ATOMIC"; 152 | case SQLITE_LOCKED_SHAREDCACHE: return "SQLITE_LOCKED_SHAREDCACHE"; 153 | case SQLITE_LOCKED_VTAB: return "SQLITE_LOCKED_VTAB"; 154 | case SQLITE_BUSY_RECOVERY: return "SQLITE_BUSY_RECOVERY"; 155 | case SQLITE_BUSY_SNAPSHOT: return "SQLITE_BUSY_SNAPSHOT"; 156 | case SQLITE_CANTOPEN_NOTEMPDIR: return "SQLITE_CANTOPEN_NOTEMPDIR"; 157 | case SQLITE_CANTOPEN_ISDIR: return "SQLITE_CANTOPEN_ISDIR"; 158 | case SQLITE_CANTOPEN_FULLPATH: return "SQLITE_CANTOPEN_FULLPATH"; 159 | case SQLITE_CANTOPEN_CONVPATH: return "SQLITE_CANTOPEN_CONVPATH"; 160 | case SQLITE_CORRUPT_VTAB: return "SQLITE_CORRUPT_VTAB"; 161 | case SQLITE_CORRUPT_SEQUENCE: return "SQLITE_CORRUPT_SEQUENCE"; 162 | case SQLITE_READONLY_RECOVERY: return "SQLITE_READONLY_RECOVERY"; 163 | case SQLITE_READONLY_CANTLOCK: return "SQLITE_READONLY_CANTLOCK"; 164 | case SQLITE_READONLY_ROLLBACK: return "SQLITE_READONLY_ROLLBACK"; 165 | case SQLITE_READONLY_DBMOVED: return "SQLITE_READONLY_DBMOVED"; 166 | case SQLITE_READONLY_CANTINIT: return "SQLITE_READONLY_CANTINIT"; 167 | case SQLITE_READONLY_DIRECTORY: return "SQLITE_READONLY_DIRECTORY"; 168 | case SQLITE_ABORT_ROLLBACK: return "SQLITE_ABORT_ROLLBACK"; 169 | case SQLITE_CONSTRAINT_CHECK: return "SQLITE_CONSTRAINT_CHECK"; 170 | case SQLITE_CONSTRAINT_COMMITHOOK: return "SQLITE_CONSTRAINT_COMMITHOOK"; 171 | case SQLITE_CONSTRAINT_FOREIGNKEY: return "SQLITE_CONSTRAINT_FOREIGNKEY"; 172 | case SQLITE_CONSTRAINT_FUNCTION: return "SQLITE_CONSTRAINT_FUNCTION"; 173 | case SQLITE_CONSTRAINT_NOTNULL: return "SQLITE_CONSTRAINT_NOTNULL"; 174 | case SQLITE_CONSTRAINT_PRIMARYKEY: return "SQLITE_CONSTRAINT_PRIMARYKEY"; 175 | case SQLITE_CONSTRAINT_TRIGGER: return "SQLITE_CONSTRAINT_TRIGGER"; 176 | case SQLITE_CONSTRAINT_UNIQUE: return "SQLITE_CONSTRAINT_UNIQUE"; 177 | case SQLITE_CONSTRAINT_VTAB: return "SQLITE_CONSTRAINT_VTAB"; 178 | case SQLITE_CONSTRAINT_ROWID: return "SQLITE_CONSTRAINT_ROWID"; 179 | case SQLITE_NOTICE_RECOVER_WAL: return "SQLITE_NOTICE_RECOVER_WAL"; 180 | case SQLITE_NOTICE_RECOVER_ROLLBACK: return "SQLITE_NOTICE_RECOVER_ROLLBACK"; 181 | case SQLITE_WARNING_AUTOINDEX: return "SQLITE_WARNING_AUTOINDEX"; 182 | case SQLITE_AUTH_USER: return "SQLITE_AUTH_USER"; 183 | case SQLITE_OK_LOAD_PERMANENTLY: return "SQLITE_OK_LOAD_PERMANENTLY"; 184 | 185 | default: return "UNKNOWN"; 186 | } 187 | } 188 | 189 | const char* sqlite_authorizer_string(int type) { 190 | switch (type) { 191 | case SQLITE_INSERT: return "insert"; 192 | case SQLITE_UPDATE: return "update"; 193 | case SQLITE_DELETE: return "delete"; 194 | default: return ""; 195 | } 196 | } 197 | 198 | NODE_API_MODULE(node_sqlite3, RegisterModule) 199 | -------------------------------------------------------------------------------- /src/statement.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_SQLITE3_SRC_STATEMENT_H 2 | #define NODE_SQLITE3_SRC_STATEMENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "database.h" 15 | #include "threading.h" 16 | 17 | using namespace Napi; 18 | 19 | namespace node_sqlite3 { 20 | 21 | namespace Values { 22 | struct Field { 23 | inline Field(int _index, unsigned short _type = SQLITE_NULL) : 24 | type(_type), index(_index) {} 25 | inline Field(const char* _name, unsigned short _type = SQLITE_NULL) : 26 | type(_type), index(0), name(_name) {} 27 | 28 | unsigned short type; 29 | int index; 30 | std::string name; 31 | }; 32 | 33 | struct Integer : Field { 34 | template inline Integer(T _name, int64_t val) : 35 | Field(_name, SQLITE_INTEGER), value(val) {} 36 | int64_t value; 37 | }; 38 | 39 | struct Float : Field { 40 | template inline Float(T _name, double val) : 41 | Field(_name, SQLITE_FLOAT), value(val) {} 42 | double value; 43 | }; 44 | 45 | struct Text : Field { 46 | template inline Text(T _name, size_t len, const char* val) : 47 | Field(_name, SQLITE_TEXT), value(val, len) {} 48 | std::string value; 49 | }; 50 | 51 | struct Blob : Field { 52 | template inline Blob(T _name, size_t len, const void* val) : 53 | Field(_name, SQLITE_BLOB), length(len) { 54 | value = (char*)malloc(len); 55 | memcpy(value, val, len); 56 | } 57 | inline ~Blob() { 58 | free(value); 59 | } 60 | size_t length; 61 | char* value; 62 | }; 63 | 64 | typedef Field Null; 65 | } 66 | 67 | typedef std::vector Row; 68 | typedef std::vector Rows; 69 | typedef Row Parameters; 70 | 71 | 72 | 73 | class Statement : public Napi::ObjectWrap { 74 | public: 75 | static Napi::Object Init(Napi::Env env, Napi::Object exports); 76 | static Napi::Value New(const Napi::CallbackInfo& info); 77 | 78 | struct Baton { 79 | napi_async_work request = NULL; 80 | Statement* stmt; 81 | Napi::FunctionReference callback; 82 | Parameters parameters; 83 | 84 | Baton(Statement* stmt_, Napi::Function cb_) : stmt(stmt_) { 85 | stmt->Ref(); 86 | callback.Reset(cb_, 1); 87 | } 88 | virtual ~Baton() { 89 | for (size_t i = 0; i < parameters.size(); i++) { 90 | Values::Field* field = parameters[i]; 91 | DELETE_FIELD(field); 92 | } 93 | if (request) napi_delete_async_work(stmt->Env(), request); 94 | stmt->Unref(); 95 | callback.Reset(); 96 | } 97 | }; 98 | 99 | struct RowBaton : Baton { 100 | RowBaton(Statement* stmt_, Napi::Function cb_) : 101 | Baton(stmt_, cb_) {} 102 | Row row; 103 | }; 104 | 105 | struct RunBaton : Baton { 106 | RunBaton(Statement* stmt_, Napi::Function cb_) : 107 | Baton(stmt_, cb_), inserted_id(0), changes(0) {} 108 | sqlite3_int64 inserted_id; 109 | int changes; 110 | }; 111 | 112 | struct RowsBaton : Baton { 113 | RowsBaton(Statement* stmt_, Napi::Function cb_) : 114 | Baton(stmt_, cb_) {} 115 | Rows rows; 116 | }; 117 | 118 | struct Async; 119 | 120 | struct EachBaton : Baton { 121 | Napi::FunctionReference completed; 122 | Async* async; // Isn't deleted when the baton is deleted. 123 | 124 | EachBaton(Statement* stmt_, Napi::Function cb_) : 125 | Baton(stmt_, cb_) {} 126 | virtual ~EachBaton() { 127 | completed.Reset(); 128 | } 129 | }; 130 | 131 | struct PrepareBaton : Database::Baton { 132 | Statement* stmt; 133 | std::string sql; 134 | PrepareBaton(Database* db_, Napi::Function cb_, Statement* stmt_) : 135 | Baton(db_, cb_), stmt(stmt_) { 136 | stmt->Ref(); 137 | } 138 | virtual ~PrepareBaton() { 139 | stmt->Unref(); 140 | if (!db->IsOpen() && db->IsLocked()) { 141 | // The database handle was closed before the statement could be 142 | // prepared. 143 | stmt->Finalize_(); 144 | } 145 | } 146 | }; 147 | 148 | typedef void (*Work_Callback)(Baton* baton); 149 | 150 | struct Call { 151 | Call(Work_Callback cb_, Baton* baton_) : callback(cb_), baton(baton_) {}; 152 | Work_Callback callback; 153 | Baton* baton; 154 | }; 155 | 156 | struct Async { 157 | uv_async_t watcher; 158 | Statement* stmt; 159 | Rows data; 160 | NODE_SQLITE3_MUTEX_t; 161 | bool completed; 162 | int retrieved; 163 | 164 | // Store the callbacks here because we don't have 165 | // access to the baton in the async callback. 166 | Napi::FunctionReference item_cb; 167 | Napi::FunctionReference completed_cb; 168 | 169 | Async(Statement* st, uv_async_cb async_cb) : 170 | stmt(st), completed(false), retrieved(0) { 171 | watcher.data = this; 172 | NODE_SQLITE3_MUTEX_INIT 173 | stmt->Ref(); 174 | uv_loop_t *loop; 175 | napi_get_uv_event_loop(stmt->Env(), &loop); 176 | uv_async_init(loop, &watcher, async_cb); 177 | } 178 | 179 | ~Async() { 180 | stmt->Unref(); 181 | item_cb.Reset(); 182 | completed_cb.Reset(); 183 | NODE_SQLITE3_MUTEX_DESTROY 184 | } 185 | }; 186 | 187 | void init(Database* db_) { 188 | db = db_; 189 | _handle = NULL; 190 | status = SQLITE_OK; 191 | prepared = false; 192 | locked = true; 193 | finalized = false; 194 | db->Ref(); 195 | } 196 | 197 | Statement(const Napi::CallbackInfo& info); 198 | 199 | ~Statement() { 200 | if (!finalized) Finalize_(); 201 | } 202 | 203 | WORK_DEFINITION(Bind); 204 | WORK_DEFINITION(Get); 205 | WORK_DEFINITION(Run); 206 | WORK_DEFINITION(All); 207 | WORK_DEFINITION(Each); 208 | WORK_DEFINITION(Reset); 209 | 210 | Napi::Value Finalize_(const Napi::CallbackInfo& info); 211 | 212 | protected: 213 | static void Work_BeginPrepare(Database::Baton* baton); 214 | static void Work_Prepare(napi_env env, void* data); 215 | static void Work_AfterPrepare(napi_env env, napi_status status, void* data); 216 | 217 | static void AsyncEach(uv_async_t* handle); 218 | static void CloseCallback(uv_handle_t* handle); 219 | 220 | static void Finalize_(Baton* baton); 221 | void Finalize_(); 222 | 223 | template inline Values::Field* BindParameter(const Napi::Value source, T pos); 224 | template T* Bind(const Napi::CallbackInfo& info, size_t start, size_t end); 225 | bool Bind(const Parameters ¶meters); 226 | 227 | static void GetRow(Row* row, sqlite3_stmt* stmt); 228 | static Napi::Value RowToJS(Napi::Env env, Row* row); 229 | void Schedule(Work_Callback callback, Baton* baton); 230 | void Process(); 231 | void CleanQueue(); 232 | template static void Error(T* baton); 233 | 234 | protected: 235 | Database* db; 236 | 237 | sqlite3_stmt* _handle; 238 | int status; 239 | std::string message; 240 | 241 | bool prepared; 242 | bool locked; 243 | bool finalized; 244 | std::queue queue; 245 | }; 246 | 247 | } 248 | 249 | #endif 250 | -------------------------------------------------------------------------------- /src/threading.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_SQLITE3_SRC_THREADING_H 2 | #define NODE_SQLITE3_SRC_THREADING_H 3 | 4 | #define NODE_SQLITE3_MUTEX_t uv_mutex_t mutex; 5 | #define NODE_SQLITE3_MUTEX_INIT uv_mutex_init(&mutex); 6 | #define NODE_SQLITE3_MUTEX_LOCK(m) uv_mutex_lock(m); 7 | #define NODE_SQLITE3_MUTEX_UNLOCK(m) uv_mutex_unlock(m); 8 | #define NODE_SQLITE3_MUTEX_DESTROY uv_mutex_destroy(&mutex); 9 | 10 | #endif // NODE_SQLITE3_SRC_THREADING_H 11 | -------------------------------------------------------------------------------- /test/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | mocha: true, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /test/affected.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('query properties', function() { 5 | var db; 6 | before(function(done) { 7 | db = new sqlite3.Database(':memory:'); 8 | db.run("CREATE TABLE foo (id INT, txt TEXT)", done); 9 | }); 10 | 11 | it('should return the correct lastID', function(done) { 12 | var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); 13 | var j = 1; 14 | for (var i = 0; i < 5000; i++) { 15 | stmt.run(i, "demo", function(err) { 16 | if (err) throw err; 17 | // Relies on SQLite's row numbering to be gapless and starting 18 | // from 1. 19 | assert.equal(j++, this.lastID); 20 | }); 21 | } 22 | db.wait(done); 23 | }); 24 | 25 | it('should return the correct changes count', function(done) { 26 | db.run("UPDATE foo SET id = id + 1 WHERE id % 2 = 0", function(err) { 27 | if (err) throw err; 28 | assert.equal(2500, this.changes); 29 | done(); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/async_calls.test.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | var sqlite3 = require('..'); 4 | const assert = require("assert"); 5 | const { createHook, executionAsyncId } = require("async_hooks"); 6 | 7 | 8 | describe('async_hooks', function() { 9 | let db; 10 | let dbId; 11 | let asyncHook; 12 | 13 | beforeEach(function() { 14 | db = new sqlite3.Database(':memory:'); 15 | 16 | asyncHook = createHook({ 17 | init(asyncId, type) { 18 | if (dbId == null && type.startsWith("sqlite3.")) { 19 | dbId = asyncId; 20 | } 21 | } 22 | }).enable(); 23 | }); 24 | 25 | it('should support performance measuring with async hooks', function(done) { 26 | db.run("DROP TABLE user", () => { 27 | const cbId = executionAsyncId(); 28 | assert.strictEqual(cbId, dbId); 29 | done(); 30 | }); 31 | }); 32 | 33 | afterEach(function() { 34 | if (asyncHook != null) { 35 | asyncHook.disable(); 36 | } 37 | dbId = null; 38 | if (db != null) { 39 | db.close(); 40 | } 41 | }); 42 | }); -------------------------------------------------------------------------------- /test/backup.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | var fs = require('fs'); 4 | var helper = require('./support/helper'); 5 | 6 | // Check that the number of rows in two tables matches. 7 | function assertRowsMatchDb(db1, table1, db2, table2, done) { 8 | db1.get("SELECT COUNT(*) as count FROM " + table1, function(err, row) { 9 | if (err) throw err; 10 | db2.get("SELECT COUNT(*) as count FROM " + table2, function(err, row2) { 11 | if (err) throw err; 12 | assert.equal(row.count, row2.count); 13 | done(); 14 | }); 15 | }); 16 | } 17 | 18 | // Check that the number of rows in the table "foo" is preserved in a backup. 19 | function assertRowsMatchFile(db, backupName, done) { 20 | var db2 = new sqlite3.Database(backupName, sqlite3.OPEN_READONLY, function(err) { 21 | if (err) throw err; 22 | assertRowsMatchDb(db, 'foo', db2, 'foo', function() { 23 | db2.close(done); 24 | }); 25 | }); 26 | } 27 | 28 | describe('backup', function() { 29 | before(function() { 30 | helper.ensureExists('test/tmp'); 31 | }); 32 | 33 | var db; 34 | beforeEach(function(done) { 35 | helper.deleteFile('test/tmp/backup.db'); 36 | helper.deleteFile('test/tmp/backup2.db'); 37 | db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); 38 | }); 39 | 40 | afterEach(function(done) { 41 | if (!db) { done(); } 42 | db.close(done); 43 | }); 44 | 45 | it ('output db created once step is called', function(done) { 46 | var backup = db.backup('test/tmp/backup.db', function(err) { 47 | if (err) throw err; 48 | backup.step(1, function(err) { 49 | if (err) throw err; 50 | assert.fileExists('test/tmp/backup.db'); 51 | backup.finish(done); 52 | }); 53 | }); 54 | }); 55 | 56 | it ('copies source fully with step(-1)', function(done) { 57 | var backup = db.backup('test/tmp/backup.db'); 58 | backup.step(-1, function(err) { 59 | if (err) throw err; 60 | assert.fileExists('test/tmp/backup.db'); 61 | backup.finish(function(err) { 62 | if (err) throw err; 63 | assertRowsMatchFile(db, 'test/tmp/backup.db', done); 64 | }); 65 | }); 66 | }); 67 | 68 | it ('backup db not created if finished immediately', function(done) { 69 | var backup = db.backup('test/tmp/backup.db'); 70 | backup.finish(function(err) { 71 | if (err) throw err; 72 | assert.fileDoesNotExist('test/tmp/backup.db'); 73 | done(); 74 | }); 75 | }); 76 | 77 | it ('error closing db if backup not finished', function(done) { 78 | var backup = db.backup('test/tmp/backup.db'); 79 | db.close(function(err) { 80 | db = null; 81 | if (!err) throw new Error('should have an error'); 82 | if (err.errno == sqlite3.BUSY) { 83 | done(); 84 | } 85 | else throw err; 86 | }); 87 | }); 88 | 89 | it ('using the backup after finished is an error', function(done) { 90 | var backup = db.backup('test/tmp/backup.db'); 91 | backup.finish(function(err) { 92 | if (err) throw err; 93 | backup.step(1, function(err) { 94 | if (!err) throw new Error('should have an error'); 95 | if (err.errno == sqlite3.MISUSE && 96 | err.message === 'SQLITE_MISUSE: Backup is already finished') { 97 | done(); 98 | } 99 | else throw err; 100 | }); 101 | }); 102 | }); 103 | 104 | it ('remaining/pageCount are available after call to step', function(done) { 105 | var backup = db.backup('test/tmp/backup.db'); 106 | backup.step(0, function(err) { 107 | if (err) throw err; 108 | assert.equal(typeof this.pageCount, 'number'); 109 | assert.equal(typeof this.remaining, 'number'); 110 | assert.equal(this.remaining, this.pageCount); 111 | var prevRemaining = this.remaining; 112 | var prevPageCount = this.pageCount; 113 | backup.step(1, function(err) { 114 | if (err) throw err; 115 | assert.notEqual(this.remaining, prevRemaining); 116 | assert.equal(this.pageCount, prevPageCount); 117 | backup.finish(done); 118 | }); 119 | }); 120 | }); 121 | 122 | it ('backup works if database is modified half-way through', function(done) { 123 | var backup = db.backup('test/tmp/backup.db'); 124 | backup.step(-1, function(err) { 125 | if (err) throw err; 126 | backup.finish(function(err) { 127 | if (err) throw err; 128 | var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) { 129 | if (err) throw err; 130 | var backup2 = db2.backup('test/tmp/backup2.db'); 131 | backup2.step(1, function(err, completed) { 132 | if (err) throw err; 133 | assert.equal(completed, false); // Page size for the test db 134 | // should not be raised to high. 135 | db2.exec("insert into foo(txt) values('hello')", function(err) { 136 | if (err) throw err; 137 | backup2.step(-1, function(err, completed) { 138 | if (err) throw err; 139 | assert.equal(completed, true); 140 | assertRowsMatchFile(db2, 'test/tmp/backup2.db', function() { 141 | backup2.finish(function(err) { 142 | if (err) throw err; 143 | db2.close(done); 144 | }); 145 | }); 146 | }); 147 | }); 148 | }); 149 | }); 150 | }); 151 | }); 152 | }); 153 | 154 | (sqlite3.VERSION_NUMBER < 3026000 ? it.skip : it) ('can backup from temp to main', function(done) { 155 | db.exec("CREATE TEMP TABLE space (txt TEXT)", function(err) { 156 | if (err) throw err; 157 | db.exec("INSERT INTO space(txt) VALUES('monkey')", function(err) { 158 | if (err) throw err; 159 | var backup = db.backup('test/tmp/backup.db', 'temp', 'main', true, function(err) { 160 | if (err) throw err; 161 | backup.step(-1, function(err) { 162 | if (err) throw err; 163 | backup.finish(function(err) { 164 | if (err) throw err; 165 | var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) { 166 | if (err) throw err; 167 | db2.get("SELECT * FROM space", function(err, row) { 168 | if (err) throw err; 169 | assert.equal(row.txt, 'monkey'); 170 | db2.close(done); 171 | }); 172 | }); 173 | }); 174 | }); 175 | }); 176 | }); 177 | }); 178 | }); 179 | 180 | (sqlite3.VERSION_NUMBER < 3026000 ? it.skip : it) ('can backup from main to temp', function(done) { 181 | var backup = db.backup('test/support/prepare.db', 'main', 'temp', false, function(err) { 182 | if (err) throw err; 183 | backup.step(-1, function(err) { 184 | if (err) throw err; 185 | backup.finish(function(err) { 186 | if (err) throw err; 187 | assertRowsMatchDb(db, 'temp.foo', db, 'main.foo', done); 188 | }); 189 | }); 190 | }); 191 | }); 192 | 193 | it ('cannot backup to a locked db', function(done) { 194 | var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) { 195 | db2.exec("PRAGMA locking_mode = EXCLUSIVE"); 196 | db2.exec("BEGIN EXCLUSIVE", function(err) { 197 | if (err) throw err; 198 | var backup = db.backup('test/tmp/backup.db'); 199 | backup.step(-1, function(stepErr) { 200 | db2.close(function(err) { 201 | if (err) throw err; 202 | if (stepErr.errno == sqlite3.BUSY) { 203 | backup.finish(done); 204 | } 205 | else throw stepErr; 206 | }); 207 | }); 208 | }); 209 | }); 210 | }); 211 | 212 | it ('fuss-free incremental backups work', function(done) { 213 | var backup = db.backup('test/tmp/backup.db'); 214 | var timer; 215 | function makeProgress() { 216 | if (backup.idle) { 217 | backup.step(1); 218 | } 219 | if (backup.completed || backup.failed) { 220 | clearInterval(timer); 221 | assert.equal(backup.completed, true); 222 | assert.equal(backup.failed, false); 223 | done(); 224 | } 225 | } 226 | timer = setInterval(makeProgress, 2); 227 | }); 228 | 229 | it ('setting retryErrors to empty disables automatic finishing', function(done) { 230 | var backup = db.backup('test/tmp/backup.db'); 231 | backup.retryErrors = []; 232 | backup.step(-1, function(err) { 233 | if (err) throw err; 234 | db.close(function(err) { 235 | db = null; 236 | if (!err) throw new Error('should have an error'); 237 | assert.equal(err.errno, sqlite3.BUSY); 238 | done(); 239 | }); 240 | }); 241 | }); 242 | 243 | it ('setting retryErrors enables automatic finishing', function(done) { 244 | var backup = db.backup('test/tmp/backup.db'); 245 | backup.retryErrors = [sqlite3.OK]; 246 | backup.step(-1, function(err) { 247 | if (err) throw err; 248 | db.close(function(err) { 249 | if (err) throw err; 250 | db = null; 251 | done(); 252 | }); 253 | }); 254 | }); 255 | 256 | it ('default retryErrors will retry on a locked/busy db', function(done) { 257 | var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) { 258 | db2.exec("PRAGMA locking_mode = EXCLUSIVE"); 259 | db2.exec("BEGIN EXCLUSIVE", function(err) { 260 | if (err) throw err; 261 | var backup = db.backup('test/tmp/backup.db'); 262 | backup.step(-1, function(stepErr) { 263 | db2.close(function(err) { 264 | if (err) throw err; 265 | assert.equal(stepErr.errno, sqlite3.BUSY); 266 | assert.equal(backup.completed, false); 267 | assert.equal(backup.failed, false); 268 | backup.step(-1, function(err) { 269 | if (err) throw err; 270 | assert.equal(backup.completed, true); 271 | assert.equal(backup.failed, false); 272 | done(); 273 | }); 274 | }); 275 | }); 276 | }); 277 | }); 278 | }); 279 | }); 280 | -------------------------------------------------------------------------------- /test/blob.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'), 2 | fs = require('fs'), 3 | assert = require('assert'), 4 | Buffer = require('buffer').Buffer; 5 | 6 | // lots of elmo 7 | var elmo = fs.readFileSync(__dirname + '/support/elmo.png'); 8 | 9 | describe('blob', function() { 10 | var db; 11 | before(function(done) { 12 | db = new sqlite3.Database(':memory:'); 13 | db.run("CREATE TABLE elmos (id INT, image BLOB)", done); 14 | }); 15 | 16 | var total = 10; 17 | var inserted = 0; 18 | var retrieved = 0; 19 | 20 | 21 | it('should insert blobs', function(done) { 22 | for (var i = 0; i < total; i++) { 23 | db.run('INSERT INTO elmos (id, image) VALUES (?, ?)', i, elmo, function(err) { 24 | if (err) throw err; 25 | inserted++; 26 | }); 27 | } 28 | db.wait(function() { 29 | assert.equal(inserted, total); 30 | done(); 31 | }); 32 | }); 33 | 34 | it('should retrieve the blobs', function(done) { 35 | db.all('SELECT id, image FROM elmos ORDER BY id', function(err, rows) { 36 | if (err) throw err; 37 | for (var i = 0; i < rows.length; i++) { 38 | assert.ok(Buffer.isBuffer(rows[i].image)); 39 | assert.ok(elmo.length, rows[i].image); 40 | 41 | for (var j = 0; j < elmo.length; j++) { 42 | if (elmo[j] !== rows[i].image[j]) { 43 | assert.ok(false, "Wrong byte"); 44 | } 45 | } 46 | 47 | retrieved++; 48 | } 49 | 50 | assert.equal(retrieved, total); 51 | done(); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/cache.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | var helper = require('./support/helper'); 4 | 5 | describe('cache', function() { 6 | before(function() { 7 | helper.ensureExists('test/tmp'); 8 | }); 9 | 10 | it('should cache Database objects while opening', function(done) { 11 | var filename = 'test/tmp/test_cache.db'; 12 | helper.deleteFile(filename); 13 | var opened1 = false, opened2 = false; 14 | var db1 = new sqlite3.cached.Database(filename, function(err) { 15 | if (err) throw err; 16 | opened1 = true; 17 | if (opened1 && opened2) done(); 18 | }); 19 | var db2 = new sqlite3.cached.Database(filename, function(err) { 20 | if (err) throw err; 21 | opened2 = true; 22 | if (opened1 && opened2) done(); 23 | }); 24 | assert.equal(db1, db2); 25 | }); 26 | 27 | it('should cache Database objects after they are open', function(done) { 28 | var filename = 'test/tmp/test_cache2.db'; 29 | helper.deleteFile(filename); 30 | var db1, db2; 31 | db1 = new sqlite3.cached.Database(filename, function(err) { 32 | if (err) throw err; 33 | process.nextTick(function() { 34 | db2 = new sqlite3.cached.Database(filename, function(err) { 35 | done(); 36 | 37 | }); 38 | assert.equal(db1, db2); 39 | }); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/constants.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('constants', function() { 5 | it('should have the right OPEN_* flags', function() { 6 | assert.ok(sqlite3.OPEN_READONLY === 1); 7 | assert.ok(sqlite3.OPEN_READWRITE === 2); 8 | assert.ok(sqlite3.OPEN_CREATE === 4); 9 | assert.ok(sqlite3.OPEN_URI === 0x00000040); 10 | assert.ok(sqlite3.OPEN_FULLMUTEX === 0x00010000); 11 | assert.ok(sqlite3.OPEN_SHAREDCACHE === 0x00020000); 12 | assert.ok(sqlite3.OPEN_PRIVATECACHE === 0x00040000); 13 | }); 14 | 15 | it('should have the right error flags', function() { 16 | assert.ok(sqlite3.OK === 0); 17 | assert.ok(sqlite3.ERROR === 1); 18 | assert.ok(sqlite3.INTERNAL === 2); 19 | assert.ok(sqlite3.PERM === 3); 20 | assert.ok(sqlite3.ABORT === 4); 21 | assert.ok(sqlite3.BUSY === 5); 22 | assert.ok(sqlite3.LOCKED === 6); 23 | assert.ok(sqlite3.NOMEM === 7); 24 | assert.ok(sqlite3.READONLY === 8); 25 | assert.ok(sqlite3.INTERRUPT === 9); 26 | assert.ok(sqlite3.IOERR === 10); 27 | assert.ok(sqlite3.CORRUPT === 11); 28 | assert.ok(sqlite3.NOTFOUND === 12); 29 | assert.ok(sqlite3.FULL === 13); 30 | assert.ok(sqlite3.CANTOPEN === 14); 31 | assert.ok(sqlite3.PROTOCOL === 15); 32 | assert.ok(sqlite3.EMPTY === 16); 33 | assert.ok(sqlite3.SCHEMA === 17); 34 | assert.ok(sqlite3.TOOBIG === 18); 35 | assert.ok(sqlite3.CONSTRAINT === 19); 36 | assert.ok(sqlite3.MISMATCH === 20); 37 | assert.ok(sqlite3.MISUSE === 21); 38 | assert.ok(sqlite3.NOLFS === 22); 39 | assert.ok(sqlite3.AUTH === 23); 40 | assert.ok(sqlite3.FORMAT === 24); 41 | assert.ok(sqlite3.RANGE === 25); 42 | assert.ok(sqlite3.NOTADB === 26); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/database_fail.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('error handling', function() { 5 | var db; 6 | before(function(done) { 7 | db = new sqlite3.Database(':memory:', done); 8 | }); 9 | 10 | it('throw when calling Database() without new', function() { 11 | assert.throws(function() { 12 | sqlite3.Database(':memory:'); 13 | }, (/Class constructors cannot be invoked without 'new'/)); 14 | 15 | assert.throws(function() { 16 | sqlite3.Statement(); 17 | }, (/Class constructors cannot be invoked without 'new'/)); 18 | }); 19 | 20 | it('should error when calling Database#get on a missing table', function(done) { 21 | db.get('SELECT id, txt FROM foo', function(err, row) { 22 | if (err) { 23 | assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); 24 | assert.equal(err.errno, sqlite3.ERROR); 25 | assert.equal(err.code, 'SQLITE_ERROR'); 26 | done(); 27 | } else { 28 | done(new Error('Completed query without error, but expected error')); 29 | } 30 | }); 31 | }); 32 | 33 | it('Database#all prepare fail', function(done) { 34 | db.all('SELECT id, txt FROM foo', function(err, row) { 35 | if (err) { 36 | assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); 37 | assert.equal(err.errno, sqlite3.ERROR); 38 | assert.equal(err.code, 'SQLITE_ERROR'); 39 | done(); 40 | } else { 41 | done(new Error('Completed query without error, but expected error')); 42 | } 43 | }); 44 | }); 45 | 46 | it('Database#run prepare fail', function(done) { 47 | db.run('SELECT id, txt FROM foo', function(err, row) { 48 | if (err) { 49 | assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); 50 | assert.equal(err.errno, sqlite3.ERROR); 51 | assert.equal(err.code, 'SQLITE_ERROR'); 52 | done(); 53 | } else { 54 | done(new Error('Completed query without error, but expected error')); 55 | } 56 | }); 57 | }); 58 | 59 | it('Database#each prepare fail', function(done) { 60 | db.each('SELECT id, txt FROM foo', function(err, row) { 61 | assert.ok(false, "this should not be called"); 62 | }, function(err, num) { 63 | if (err) { 64 | assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); 65 | assert.equal(err.errno, sqlite3.ERROR); 66 | assert.equal(err.code, 'SQLITE_ERROR'); 67 | done(); 68 | } else { 69 | done(new Error('Completed query without error, but expected error')); 70 | } 71 | }); 72 | }); 73 | 74 | it('Database#each prepare fail without completion handler', function(done) { 75 | db.each('SELECT id, txt FROM foo', function(err, row) { 76 | if (err) { 77 | assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); 78 | assert.equal(err.errno, sqlite3.ERROR); 79 | assert.equal(err.code, 'SQLITE_ERROR'); 80 | done(); 81 | } else { 82 | done(new Error('Completed query without error, but expected error')); 83 | } 84 | }); 85 | }); 86 | 87 | it('Database#get prepare fail with param binding', function(done) { 88 | db.get('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) { 89 | if (err) { 90 | assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); 91 | assert.equal(err.errno, sqlite3.ERROR); 92 | assert.equal(err.code, 'SQLITE_ERROR'); 93 | done(); 94 | } else { 95 | done(new Error('Completed query without error, but expected error')); 96 | } 97 | }); 98 | }); 99 | 100 | it('Database#all prepare fail with param binding', function(done) { 101 | db.all('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) { 102 | if (err) { 103 | assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); 104 | assert.equal(err.errno, sqlite3.ERROR); 105 | assert.equal(err.code, 'SQLITE_ERROR'); 106 | done(); 107 | } else { 108 | done(new Error('Completed query without error, but expected error')); 109 | } 110 | }); 111 | }); 112 | 113 | it('Database#run prepare fail with param binding', function(done) { 114 | db.run('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) { 115 | if (err) { 116 | assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); 117 | assert.equal(err.errno, sqlite3.ERROR); 118 | assert.equal(err.code, 'SQLITE_ERROR'); 119 | done(); 120 | } else { 121 | done(new Error('Completed query without error, but expected error')); 122 | } 123 | }); 124 | }); 125 | 126 | it('Database#each prepare fail with param binding', function(done) { 127 | db.each('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) { 128 | assert.ok(false, "this should not be called"); 129 | }, function(err, num) { 130 | if (err) { 131 | assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); 132 | assert.equal(err.errno, sqlite3.ERROR); 133 | assert.equal(err.code, 'SQLITE_ERROR'); 134 | done(); 135 | } else { 136 | done(new Error('Completed query without error, but expected error')); 137 | } 138 | }); 139 | }); 140 | 141 | it('Database#each prepare fail with param binding without completion handler', function(done) { 142 | db.each('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) { 143 | if (err) { 144 | assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); 145 | assert.equal(err.errno, sqlite3.ERROR); 146 | assert.equal(err.code, 'SQLITE_ERROR'); 147 | done(); 148 | } else { 149 | done(new Error('Completed query without error, but expected error')); 150 | } 151 | }); 152 | }); 153 | }); 154 | -------------------------------------------------------------------------------- /test/each.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('each', function() { 5 | var db; 6 | before(function(done) { 7 | db = new sqlite3.Database('test/support/big.db', sqlite3.OPEN_READONLY, done); 8 | }); 9 | 10 | it('retrieve 100,000 rows with Statement#each', function(done) { 11 | var total = 100000; 12 | var retrieved = 0; 13 | 14 | 15 | db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) { 16 | if (err) throw err; 17 | retrieved++; 18 | 19 | if(retrieved === total) { 20 | assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows."); 21 | done(); 22 | } 23 | }); 24 | }); 25 | 26 | it('Statement#each with complete callback', function(done) { 27 | var total = 10000; 28 | var retrieved = 0; 29 | 30 | db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) { 31 | if (err) throw err; 32 | retrieved++; 33 | }, function(err, num) { 34 | assert.equal(retrieved, num); 35 | assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows."); 36 | done(); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/exec.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | var fs = require('fs'); 4 | 5 | describe('exec', function() { 6 | var db; 7 | before(function(done) { 8 | db = new sqlite3.Database(':memory:', done); 9 | }); 10 | 11 | it('Database#exec', function(done) { 12 | var sql = fs.readFileSync('test/support/script.sql', 'utf8'); 13 | db.exec(sql, done); 14 | }); 15 | 16 | it('retrieve database structure', function(done) { 17 | db.all("SELECT type, name FROM sqlite_master ORDER BY type, name", function(err, rows) { 18 | if (err) throw err; 19 | assert.deepEqual(rows, [ 20 | { type: 'index', name: 'grid_key_lookup' }, 21 | { type: 'index', name: 'grid_utfgrid_lookup' }, 22 | { type: 'index', name: 'images_id' }, 23 | { type: 'index', name: 'keymap_lookup' }, 24 | { type: 'index', name: 'map_index' }, 25 | { type: 'index', name: 'name' }, 26 | { type: 'table', name: 'grid_key' }, 27 | { type: 'table', name: 'grid_utfgrid' }, 28 | { type: 'table', name: 'images' }, 29 | { type: 'table', name: 'keymap' }, 30 | { type: 'table', name: 'map' }, 31 | { type: 'table', name: 'metadata' }, 32 | { type: 'view', name: 'grid_data' }, 33 | { type: 'view', name: 'grids' }, 34 | { type: 'view', name: 'tiles' } 35 | ]); 36 | done(); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/extension.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | var exists = require('fs').existsSync || require('path').existsSync; 4 | 5 | /* 6 | 7 | // disabled because this is not a generically safe test to run on all systems 8 | 9 | var spatialite_ext = '/usr/local/lib/libspatialite.dylib'; 10 | 11 | describe('loadExtension', function(done) { 12 | var db; 13 | before(function(done) { 14 | db = new sqlite3.Database(':memory:', done); 15 | }); 16 | 17 | if (exists(spatialite_ext)) { 18 | it('libspatialite', function(done) { 19 | db.loadExtension(spatialite_ext, done); 20 | }); 21 | } else { 22 | it('libspatialite'); 23 | } 24 | }); 25 | 26 | */ -------------------------------------------------------------------------------- /test/fts-content.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('fts', function() { 5 | var db; 6 | before(function(done) { 7 | db = new sqlite3.Database(':memory:', done); 8 | }); 9 | 10 | it('should create a new fts4 table', function(done) { 11 | db.exec('CREATE VIRTUAL TABLE t1 USING fts4(content="", a, b, c);', done); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/interrupt.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('interrupt', function() { 5 | it('should interrupt queries', function(done) { 6 | var interrupted = false; 7 | var saved = null; 8 | 9 | var db = new sqlite3.Database(':memory:', function() { 10 | db.serialize(); 11 | 12 | var setup = 'create table t (n int);'; 13 | for (var i = 0; i < 8; i += 1) { 14 | setup += 'insert into t values (' + i + ');'; 15 | } 16 | 17 | db.exec(setup, function(err) { 18 | if (err) { 19 | return done(err); 20 | } 21 | 22 | var query = 'select last.n ' + 23 | 'from t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t as last'; 24 | 25 | db.each(query, function(err) { 26 | if (err) { 27 | saved = err; 28 | } else if (!interrupted) { 29 | interrupted = true; 30 | db.interrupt(); 31 | } 32 | }); 33 | 34 | db.close(function() { 35 | if (saved) { 36 | assert.equal(saved.message, 'SQLITE_INTERRUPT: interrupted'); 37 | assert.equal(saved.errno, sqlite3.INTERRUPT); 38 | assert.equal(saved.code, 'SQLITE_INTERRUPT'); 39 | done(); 40 | } else { 41 | done(new Error('Completed query without error, but expected error')); 42 | } 43 | }); 44 | }); 45 | }); 46 | }); 47 | 48 | it('should throw if interrupt is called before open', function(done) { 49 | var db = new sqlite3.Database(':memory:'); 50 | 51 | assert.throws(function() { 52 | db.interrupt(); 53 | }, (/Database is not open/)); 54 | 55 | db.close(); 56 | done(); 57 | }); 58 | 59 | it('should throw if interrupt is called after close', function(done) { 60 | var db = new sqlite3.Database(':memory:'); 61 | 62 | db.close(function() { 63 | assert.throws(function() { 64 | db.interrupt(); 65 | }, (/Database is not open/)); 66 | 67 | done(); 68 | }); 69 | }); 70 | 71 | it('should throw if interrupt is called during close', function(done) { 72 | var db = new sqlite3.Database(':memory:', function() { 73 | db.close(); 74 | assert.throws(function() { 75 | db.interrupt(); 76 | }, (/Database is closing/)); 77 | done(); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/issue-108.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'), 2 | assert = require('assert'); 3 | 4 | describe('buffer', function() { 5 | var db; 6 | // before(function() { 7 | // }); 8 | 9 | it('should insert blobs', function(done) { 10 | db = new sqlite3.Database(':memory:'); 11 | db.serialize(function () { 12 | 13 | db.run("CREATE TABLE lorem (info BLOB)"); 14 | var stmt = db.prepare("INSERT INTO lorem VALUES (?)"); 15 | 16 | stmt.on('error', function (err) { 17 | throw err; 18 | }); 19 | 20 | var buff = Buffer.alloc(2); 21 | stmt.run(buff); 22 | stmt.finalize(); 23 | }); 24 | 25 | db.close(done); 26 | 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/json.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | 3 | if( process.env.NODE_SQLITE3_JSON1 === 'no' ){ 4 | describe('json', function() { 5 | it( 6 | 'skips JSON tests when --sqlite=/usr (or similar) is tested', 7 | function(){} 8 | ); 9 | }); 10 | } else { 11 | describe('json', function() { 12 | var db; 13 | 14 | before(function(done) { 15 | db = new sqlite3.Database(':memory:', done); 16 | }); 17 | 18 | it('should select JSON', function(done) { 19 | db.run('SELECT json(?)', JSON.stringify({ok:true}), done); 20 | }); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /test/limit.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | 3 | describe('limit', function() { 4 | var db; 5 | 6 | before(function(done) { 7 | db = new sqlite3.Database(':memory:', done); 8 | }); 9 | 10 | it('should support applying limits via configure', function(done) { 11 | db.configure('limit', sqlite3.LIMIT_ATTACHED, 0); 12 | db.exec("ATTACH 'test/support/prepare.db' AS zing", function(err) { 13 | if (!err) { 14 | throw new Error('ATTACH should not succeed'); 15 | } 16 | if (err.errno === sqlite3.ERROR && 17 | err.message === 'SQLITE_ERROR: too many attached databases - max 0') { 18 | db.configure('limit', sqlite3.LIMIT_ATTACHED, 1); 19 | db.exec("ATTACH 'test/support/prepare.db' AS zing", function(err) { 20 | if (err) throw err; 21 | db.close(done); 22 | }); 23 | } else { 24 | throw err; 25 | } 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/map.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('map', function() { 5 | it('test Database#map() with two columns', function(done) { 6 | var count = 10; 7 | var inserted = 0; 8 | 9 | var db = new sqlite3.Database(':memory:'); 10 | db.serialize(function() { 11 | db.run("CREATE TABLE foo (id INT, value TEXT)"); 12 | 13 | var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); 14 | for (var i = 5; i < count; i++) { 15 | stmt.run(i, 'Value for ' + i, function(err) { 16 | if (err) throw err; 17 | inserted++; 18 | }); 19 | } 20 | stmt.finalize(); 21 | 22 | db.map("SELECT * FROM foo", function(err, map) { 23 | if (err) throw err; 24 | assert.deepEqual(map, { 5: 'Value for 5', 6: 'Value for 6', 7: 'Value for 7', 8: 'Value for 8', 9: 'Value for 9' }); 25 | assert.equal(inserted, 5); 26 | done(); 27 | }); 28 | }); 29 | }); 30 | 31 | it('test Database#map() with three columns', function(done) { 32 | var db = new sqlite3.Database(':memory:'); 33 | 34 | var count = 10; 35 | var inserted = 0; 36 | 37 | db.serialize(function() { 38 | db.run("CREATE TABLE foo (id INT, value TEXT, other TEXT)"); 39 | 40 | var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?)"); 41 | for (var i = 5; i < count; i++) { 42 | stmt.run(i, 'Value for ' + i, null, function(err) { 43 | if (err) throw err; 44 | inserted++; 45 | }); 46 | } 47 | stmt.finalize(); 48 | 49 | db.map("SELECT * FROM foo", function(err, map) { 50 | if (err) throw err; 51 | assert.deepEqual(map, { 52 | 5: { id: 5, value: 'Value for 5', other: null }, 53 | 6: { id: 6, value: 'Value for 6', other: null }, 54 | 7: { id: 7, value: 'Value for 7', other: null }, 55 | 8: { id: 8, value: 'Value for 8', other: null }, 56 | 9: { id: 9, value: 'Value for 9', other: null } 57 | }); 58 | assert.equal(inserted, 5); 59 | done(); 60 | }); 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/named_columns.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('named columns', function() { 5 | var db; 6 | before(function(done) { 7 | db = new sqlite3.Database(':memory:', done); 8 | }); 9 | 10 | it('should create the table', function(done) { 11 | db.run("CREATE TABLE foo (txt TEXT, num INT)", done); 12 | }); 13 | 14 | it('should insert a value', function(done) { 15 | db.run("INSERT INTO foo VALUES($text, $id)", { 16 | $id: 1, 17 | $text: "Lorem Ipsum" 18 | }, done); 19 | }); 20 | 21 | it('should retrieve the values', function(done) { 22 | db.get("SELECT txt, num FROM foo ORDER BY num", function(err, row) { 23 | if (err) throw err; 24 | assert.equal(row.txt, "Lorem Ipsum"); 25 | assert.equal(row.num, 1); 26 | done(); 27 | }); 28 | }); 29 | 30 | it('should be able to retrieve rowid of last inserted value', function(done) { 31 | db.get("SELECT last_insert_rowid() as last_id FROM foo", function(err, row) { 32 | if (err) throw err; 33 | assert.equal(row.last_id, 1); 34 | done(); 35 | }); 36 | }); 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /test/named_params.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('named parameters', function() { 5 | var db; 6 | before(function(done) { 7 | db = new sqlite3.Database(':memory:', done); 8 | }); 9 | 10 | it('should create the table', function(done) { 11 | db.run("CREATE TABLE foo (txt TEXT, num INT)", done); 12 | }); 13 | 14 | it('should insert a value with $ placeholders', function(done) { 15 | db.run("INSERT INTO foo VALUES($text, $id)", { 16 | $id: 1, 17 | $text: "Lorem Ipsum" 18 | }, done); 19 | }); 20 | 21 | it('should insert a value with : placeholders', function(done) { 22 | db.run("INSERT INTO foo VALUES(:text, :id)", { 23 | ':id': 2, 24 | ':text': "Dolor Sit Amet" 25 | }, done); 26 | }); 27 | 28 | it('should insert a value with @ placeholders', function(done) { 29 | db.run("INSERT INTO foo VALUES(@txt, @id)", { 30 | "@id": 3, 31 | "@txt": "Consectetur Adipiscing Elit" 32 | }, done); 33 | }); 34 | 35 | it('should insert a value with @ placeholders using an array', function(done) { 36 | db.run("INSERT INTO foo VALUES(@txt, @id)", [ 'Sed Do Eiusmod', 4 ], done); 37 | }); 38 | 39 | it('should insert a value with indexed placeholders', function(done) { 40 | db.run("INSERT INTO foo VALUES(?2, ?4)", 41 | [ null, 'Tempor Incididunt', null, 5 ], done); 42 | }); 43 | 44 | it('should insert a value with autoindexed placeholders', function(done) { 45 | db.run("INSERT INTO foo VALUES(?, ?)", { 46 | 2: 6, 47 | 1: "Ut Labore Et Dolore" 48 | }, done); 49 | }); 50 | 51 | it('should retrieve all inserted values', function(done) { 52 | db.all("SELECT txt, num FROM foo ORDER BY num", function(err, rows) { 53 | if (err) throw err; 54 | assert.equal(rows[0].txt, "Lorem Ipsum"); 55 | assert.equal(rows[0].num, 1); 56 | assert.equal(rows[1].txt, "Dolor Sit Amet"); 57 | assert.equal(rows[1].num, 2); 58 | assert.equal(rows[2].txt, "Consectetur Adipiscing Elit"); 59 | assert.equal(rows[2].num, 3); 60 | assert.equal(rows[3].txt, "Sed Do Eiusmod"); 61 | assert.equal(rows[3].num, 4); 62 | assert.equal(rows[4].txt, "Tempor Incididunt"); 63 | assert.equal(rows[4].num, 5); 64 | assert.equal(rows[5].txt, "Ut Labore Et Dolore"); 65 | assert.equal(rows[5].num, 6); 66 | done(); 67 | }); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/null_error.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | var helper = require('./support/helper'); 4 | 5 | describe('null error', function() { 6 | var filename = 'test/tmp/test_sqlite_ok_error.db'; 7 | var db; 8 | 9 | before(function(done) { 10 | helper.ensureExists('test/tmp'); 11 | helper.deleteFile(filename); 12 | db = new sqlite3.Database(filename, done); 13 | }); 14 | 15 | it('should create a table', function(done) { 16 | db.run("CREATE TABLE febp_data (leacode TEXT, leaname TEXT, state TEXT, postcode TEXT, fips TEXT, titleistim TEXT, ideastim TEXT, ideapool TEXT, ideapoolname TEXT, localebasis TEXT, localetype2 TEXT, version TEXT, leacount_2006 TEXT, ppexpend_2005 TEXT, ppexpend_2006 TEXT, ppexpend_2007 TEXT, ppexpend_2008 TEXT, ppexpendrank_2006 TEXT, ppexpendrank_2007 TEXT, ppexpendrank_2008 TEXT, rankppexpend_2005 TEXT, opbud_2004 TEXT, opbud_2006 TEXT, opbud_2007 TEXT, opbud_2008 TEXT, titlei_2004 TEXT, titlei_2006 TEXT, titlei_2007 TEXT, titlei_2008 TEXT, titlei_2009 TEXT, titlei_2010 TEXT, idea_2004 TEXT, idea_2005 TEXT, idea_2006 TEXT, idea_2007 TEXT, idea_2008 TEXT, idea_2009 TEXT, ideaest_2010 TEXT, impact_2007 TEXT, impact_2008 TEXT, impact_2009 TEXT, impact_2010 TEXT, fedrev_2006 TEXT, fedrev_2007 TEXT, fedrev_2008 TEXT, schonut_2006 TEXT, schonut_2007 TEXT, schomeal_2006 TEXT, schomeal_2007 TEXT, schoco_2006 TEXT, schocom_2007 TEXT, medicaid_2006 TEXT, medicaid_2007 TEXT, medicaid_2008 TEXT, cenpov_2004 TEXT, cenpov_2007 TEXT, cenpov_2008 TEXT, rankcenpov_2004 TEXT, rankcenpov_2007 TEXT, rankcenpov_2008 TEXT, enroll_2006 TEXT, enroll_2007 TEXT, enroll_2008 TEXT, white_2006 TEXT, white_2007 TEXT, white_2008 TEXT, afam_2006 TEXT, afam_2007 TEXT, afam_2008 TEXT, amin_2006 TEXT, amin_2007 TEXT, amin_2008 TEXT, asian_2006 TEXT, asian_2007 TEXT, asian_2008 TEXT, hisp_2006 TEXT, hisp_2007 TEXT, hisp_2008 TEXT, frpl_2006 TEXT, frpl_2007 TEXT, frpl_2008 TEXT, ell_2006 TEXT, ell_2007 TEXT, ell_2008 TEXT, sped_2006 TEXT, sped_2007 TEXT, sped_2008 TEXT, state4read_2005 TEXT, state4read_2006 TEXT, state4read_2007 TEXT, state4read_2008 TEXT, state4read_2009 TEXT, state4math_2005 TEXT, state4math_2006 TEXT, state4math_2007 TEXT, state4math_2008 TEXT, state4math_2009 TEXT, minor_2007 TEXT, minor_2008 TEXT, state8math_2006 TEXT, state8math_2007 TEXT, state8math_2008 TEXT, state8math_2009 TEXT, state8read_2006 TEXT, state8read_2007 TEXT, state8read_2008 TEXT, state8read_2009 TEXT, statehsmath_2006 TEXT, statehsmath_2007 TEXT, statehsmath_2008 TEXT, statehsmath_2009 TEXT, statehsread_2006 TEXT, statehsread_2007 TEXT, statehsread_2008 TEXT, statehsread_2009 TEXT)", done); 17 | }); 18 | 19 | it('should insert rows with lots of null values', function(done) { 20 | var stmt = db.prepare('INSERT INTO febp_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', function(err) { 21 | if (err) throw err; 22 | 23 | for (var i = 0; i < 100; i++) { 24 | stmt.run([ '100005', 'Albertville City School District', 'ALABAMA', 'AL', '1', '856031', '753000', 'NULL', 'NULL', '6-Small Town', 'Town', 21, '130', '6624', '7140', '8731', '8520', '102', '88', '100', '94', '23352000', '27280000', '30106000', '33028000', '768478', '845886', '782696', '1096819', '1279663', '1168521', '561522', '657649', '684366', '687531', '710543', '727276', '726647', 'N/A', 'N/A', 'N/A', 'N/A', '986', '977', '1006', '1080250', '1202325', '1009962', '1109310', '70287', '93015', '14693.56', '13634.58', 'N/A', '0.230', '0.301', '0.268882175', '73', '26', '29', '3718', '3747', '3790', '2663', '2615', '2575', '75', '82', '89', '3', '2', '6', '11', '9', '8', '955', '1028', '1102', '1991', '2061', '2146', '649', '729', '770', '443', '278', '267', '0.860', '0.86', '0.8474', '0.84', '0.8235', '0.810', '0.84', '0.7729', '0.75', '0.7843', '1121', '1205', '0.74', '0.6862', '0.72', '0.7317', '0.78', '0.7766', '0.79', '0.7387', '0.84', '0.9255', '0.86', '0.9302', '0.88', '0.9308', '0.84', '0.8605' ]); 25 | } 26 | 27 | stmt.finalize(function(err) { 28 | if (err) throw err; 29 | done(); 30 | }); 31 | }); 32 | }); 33 | 34 | it('should have created the database', function() { 35 | assert.fileExists(filename); 36 | }); 37 | 38 | after(function() { 39 | helper.deleteFile(filename); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/nw/.gitignore: -------------------------------------------------------------------------------- 1 | node-webkit.app 2 | node-webkit-v0.8.4-osx-ia32.zip 3 | node_modules -------------------------------------------------------------------------------- /test/nw/Makefile: -------------------------------------------------------------------------------- 1 | NODE_WEBKIT_VERSION=0.8.4 2 | 3 | all: app.nw 4 | 5 | node_modules/sqlite3: 6 | npm install https://github.com/mapbox/node-sqlite3/tarball/master --build-from-source --runtime=node-webkit --target_arch=ia32 --target=$(NODE_WEBKIT_VERSION) 7 | 8 | rebuild: 9 | cd node_modules/sqlite3 && ./node_modules/.bin/node-pre-gyp rebuild --runtime=node-webkit --target_arch=ia32 --target=$(NODE_WEBKIT_VERSION) 10 | 11 | node-webkit-v$(NODE_WEBKIT_VERSION)-osx-ia32.zip: 12 | wget https://s3.amazonaws.com/node-webkit/v$(NODE_WEBKIT_VERSION)/node-webkit-v$(NODE_WEBKIT_VERSION)-osx-ia32.zip 13 | 14 | ./node-webkit.app: node-webkit-v$(NODE_WEBKIT_VERSION)-osx-ia32.zip 15 | unzip -o node-webkit-v$(NODE_WEBKIT_VERSION)-osx-ia32.zip 16 | 17 | app.nw: ./node-webkit.app Makefile package.json index.html node_modules/sqlite3 18 | zip app.nw index.html package.json node_modules 19 | 20 | test: ./node-webkit.app app.nw 21 | ./node-webkit.app/Contents/MacOS/node-webkit app.nw 22 | 23 | package: ./node-webkit.app Makefile package.json index.html node_modules/sqlite3 24 | rm -rf node-sqlite-test.app 25 | cp -r ./node-webkit.app node-sqlite-test.app 26 | mkdir ./node-sqlite-test.app/Contents/Resources/app.nw/ 27 | cp package.json ./node-sqlite-test.app/Contents/Resources/app.nw/ 28 | cp index.html ./node-sqlite-test.app/Contents/Resources/app.nw/ 29 | cp -r node_modules/ ./node-sqlite-test.app/Contents/Resources/app.nw/ 30 | ./node-sqlite-test.app/Contents/MacOS/node-webkit 31 | 32 | clean: 33 | rm -rf ./node_modules/sqlite3 34 | rm -f ./app.nw 35 | rm -rf node-sqlite-test.app 36 | rm -f credits.html 37 | rm -f nwsnapshot 38 | 39 | .PHONY: test 40 | -------------------------------------------------------------------------------- /test/nw/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello World! 5 | 6 | 7 |

Hello World!

8 | Using node-sqlite3: 9 | . 13 | 14 | -------------------------------------------------------------------------------- /test/nw/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nw-demo", 3 | "main": "index.html", 4 | "window": { 5 | "toolbar": false, 6 | "width": 800, 7 | "height": 600 8 | } 9 | } -------------------------------------------------------------------------------- /test/open_close.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | var fs = require('fs'); 4 | var helper = require('./support/helper'); 5 | 6 | describe('open/close', function() { 7 | before(function() { 8 | helper.ensureExists('test/tmp'); 9 | }); 10 | 11 | describe('open and close non-existant database', function() { 12 | before(function() { 13 | helper.deleteFile('test/tmp/test_create.db'); 14 | }); 15 | 16 | var db; 17 | it('should open the database', function(done) { 18 | db = new sqlite3.Database('test/tmp/test_create.db', done); 19 | }); 20 | 21 | it('should close the database', function(done) { 22 | db.close(done); 23 | }); 24 | 25 | it('should have created the file', function() { 26 | assert.fileExists('test/tmp/test_create.db'); 27 | }); 28 | 29 | after(function() { 30 | helper.deleteFile('test/tmp/test_create.db'); 31 | }); 32 | }); 33 | 34 | describe('open and close non-existant shared database', function() { 35 | before(function() { 36 | helper.deleteFile('test/tmp/test_create_shared.db'); 37 | }); 38 | 39 | var db; 40 | it('should open the database', function(done) { 41 | db = new sqlite3.Database('file:./test/tmp/test_create_shared.db', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done); 42 | }); 43 | 44 | it('should close the database', function(done) { 45 | db.close(done); 46 | }); 47 | 48 | it('should have created the file', function() { 49 | assert.fileExists('test/tmp/test_create_shared.db'); 50 | }); 51 | 52 | after(function() { 53 | helper.deleteFile('test/tmp/test_create_shared.db'); 54 | }); 55 | }); 56 | 57 | 58 | (sqlite3.VERSION_NUMBER < 3008000 ? describe.skip : describe)('open and close shared memory database', function() { 59 | 60 | var db1; 61 | var db2; 62 | 63 | it('should open the first database', function(done) { 64 | db1 = new sqlite3.Database('file:./test/tmp/test_memory.db?mode=memory', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done); 65 | }); 66 | 67 | it('should open the second database', function(done) { 68 | db2 = new sqlite3.Database('file:./test/tmp/test_memory.db?mode=memory', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done); 69 | }); 70 | 71 | it('first database should set the user_version', function(done) { 72 | db1.exec('PRAGMA user_version=42', done); 73 | }); 74 | 75 | it('second database should get the user_version', function(done) { 76 | db2.get('PRAGMA user_version', function(err, row) { 77 | if (err) throw err; 78 | assert.equal(row.user_version, 42); 79 | done(); 80 | }); 81 | }); 82 | 83 | it('should close the first database', function(done) { 84 | db1.close(done); 85 | }); 86 | 87 | it('should close the second database', function(done) { 88 | db2.close(done); 89 | }); 90 | }); 91 | 92 | it('should not be unable to open an inaccessible database', function(done) { 93 | // NOTE: test assumes that the user is not allowed to create new files 94 | // in /usr/bin. 95 | var db = new sqlite3.Database('/test/tmp/directory-does-not-exist/test.db', function(err) { 96 | if (err && err.errno === sqlite3.CANTOPEN) { 97 | done(); 98 | } else if (err) { 99 | done(err); 100 | } else { 101 | done('Opened database that should be inaccessible'); 102 | } 103 | }); 104 | }); 105 | 106 | 107 | describe('creating database without create flag', function() { 108 | before(function() { 109 | helper.deleteFile('test/tmp/test_readonly.db'); 110 | }); 111 | 112 | it('should fail to open the database', function(done) { 113 | new sqlite3.Database('tmp/test_readonly.db', sqlite3.OPEN_READONLY, function(err) { 114 | if (err && err.errno === sqlite3.CANTOPEN) { 115 | done(); 116 | } else if (err) { 117 | done(err); 118 | } else { 119 | done('Created database without create flag'); 120 | } 121 | }); 122 | }); 123 | 124 | it('should not have created the file', function() { 125 | assert.fileDoesNotExist('test/tmp/test_readonly.db'); 126 | }); 127 | 128 | after(function() { 129 | helper.deleteFile('test/tmp/test_readonly.db'); 130 | }); 131 | }); 132 | 133 | describe('open and close memory database queuing', function() { 134 | var db; 135 | it('should open the database', function(done) { 136 | db = new sqlite3.Database(':memory:', done); 137 | }); 138 | 139 | it('should close the database', function(done) { 140 | db.close(done); 141 | }); 142 | 143 | it('shouldn\'t close the database again', function(done) { 144 | db.close(function(err) { 145 | assert.ok(err, 'No error object received on second close'); 146 | assert.ok(err.errno === sqlite3.MISUSE); 147 | done(); 148 | }); 149 | }); 150 | }); 151 | 152 | describe('closing with unfinalized statements', function(done) { 153 | var completed = false; 154 | var completedSecond = false; 155 | var closed = false; 156 | 157 | var db; 158 | before(function() { 159 | db = new sqlite3.Database(':memory:', done); 160 | }); 161 | 162 | it('should create a table', function(done) { 163 | db.run("CREATE TABLE foo (id INT, num INT)", done); 164 | }); 165 | 166 | var stmt; 167 | it('should prepare/run a statement', function(done) { 168 | stmt = db.prepare('INSERT INTO foo VALUES (?, ?)'); 169 | stmt.run(1, 2, done); 170 | }); 171 | 172 | it('should fail to close the database', function(done) { 173 | db.close(function(err) { 174 | assert.ok(err.message, 175 | "SQLITE_BUSY: unable to close due to unfinalised statements"); 176 | done(); 177 | }); 178 | }); 179 | 180 | it('should succeed to close the database after finalizing', function(done) { 181 | stmt.run(3, 4, function() { 182 | stmt.finalize(); 183 | db.close(done); 184 | }); 185 | }); 186 | }); 187 | }); 188 | -------------------------------------------------------------------------------- /test/other_objects.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('data types', function() { 5 | var db; 6 | before(function(done) { 7 | db = new sqlite3.Database(':memory:'); 8 | db.run("CREATE TABLE txt_table (txt TEXT)"); 9 | db.run("CREATE TABLE int_table (int INTEGER)"); 10 | db.run("CREATE TABLE flt_table (flt FLOAT)"); 11 | db.wait(done); 12 | }); 13 | 14 | beforeEach(function(done) { 15 | db.exec('DELETE FROM txt_table; DELETE FROM int_table; DELETE FROM flt_table;', done); 16 | }); 17 | 18 | it('should serialize Date()', function(done) { 19 | var date = new Date(); 20 | db.run("INSERT INTO int_table VALUES(?)", date, function (err) { 21 | if (err) throw err; 22 | db.get("SELECT int FROM int_table", function(err, row) { 23 | if (err) throw err; 24 | assert.equal(row.int, +date); 25 | done(); 26 | }); 27 | }); 28 | }); 29 | 30 | it('should serialize RegExp()', function(done) { 31 | var regexp = /^f\noo/; 32 | db.run("INSERT INTO txt_table VALUES(?)", regexp, function (err) { 33 | if (err) throw err; 34 | db.get("SELECT txt FROM txt_table", function(err, row) { 35 | if (err) throw err; 36 | assert.equal(row.txt, String(regexp)); 37 | done(); 38 | }); 39 | }); 40 | }); 41 | 42 | [ 43 | 4294967296.249, 44 | Math.PI, 45 | 3924729304762836.5, 46 | new Date().valueOf(), 47 | 912667.394828365, 48 | 2.3948728634826374e+83, 49 | 9.293476892934982e+300, 50 | Infinity, 51 | -9.293476892934982e+300, 52 | -2.3948728634826374e+83, 53 | -Infinity 54 | ].forEach(function(flt) { 55 | it('should serialize float ' + flt, function(done) { 56 | db.run("INSERT INTO flt_table VALUES(?)", flt, function (err) { 57 | if (err) throw err; 58 | db.get("SELECT flt FROM flt_table", function(err, row) { 59 | if (err) throw err; 60 | assert.equal(row.flt, flt); 61 | done(); 62 | }); 63 | }); 64 | }); 65 | }); 66 | 67 | [ 68 | 4294967299, 69 | 3924729304762836, 70 | new Date().valueOf(), 71 | 2.3948728634826374e+83, 72 | 9.293476892934982e+300, 73 | Infinity, 74 | -9.293476892934982e+300, 75 | -2.3948728634826374e+83, 76 | -Infinity 77 | ].forEach(function(integer) { 78 | it('should serialize integer ' + integer, function(done) { 79 | db.run("INSERT INTO int_table VALUES(?)", integer, function (err) { 80 | if (err) throw err; 81 | db.get("SELECT int AS integer FROM int_table", function(err, row) { 82 | if (err) throw err; 83 | assert.equal(row.integer, integer); 84 | done(); 85 | }); 86 | }); 87 | }); 88 | }); 89 | 90 | it('should ignore faulty toString', function(done) { 91 | const faulty = { toString: 23 }; 92 | db.run("INSERT INTO txt_table VALUES(?)", faulty, function (err) { 93 | assert.notEqual(err, undefined); 94 | done(); 95 | }); 96 | }); 97 | 98 | }); 99 | -------------------------------------------------------------------------------- /test/parallel_insert.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | var helper = require('./support/helper'); 4 | 5 | describe('parallel', function() { 6 | var db; 7 | before(function(done) { 8 | helper.deleteFile('test/tmp/test_parallel_inserts.db'); 9 | helper.ensureExists('test/tmp'); 10 | db = new sqlite3.Database('test/tmp/test_parallel_inserts.db', done); 11 | }); 12 | 13 | var columns = []; 14 | for (var i = 0; i < 128; i++) { 15 | columns.push('id' + i); 16 | } 17 | 18 | it('should create the table', function(done) { 19 | db.run("CREATE TABLE foo (" + columns + ")", done); 20 | }); 21 | 22 | it('should insert in parallel', function(done) { 23 | for (var i = 0; i < 1000; i++) { 24 | for (var values = [], j = 0; j < columns.length; j++) { 25 | values.push(i * j); 26 | } 27 | db.run("INSERT INTO foo VALUES (" + values + ")"); 28 | } 29 | 30 | db.wait(done); 31 | }); 32 | 33 | it('should close the database', function(done) { 34 | db.close(done); 35 | }); 36 | 37 | it('should verify that the database exists', function() { 38 | assert.fileExists('test/tmp/test_parallel_inserts.db'); 39 | }); 40 | 41 | after(function() { 42 | helper.deleteFile('test/tmp/test_parallel_inserts.db'); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/patching.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('patching', function() { 5 | describe("Database", function() { 6 | var db; 7 | var originalFunctions = {}; 8 | 9 | before(function() { 10 | originalFunctions.close = sqlite3.Database.prototype.close; 11 | originalFunctions.exec = sqlite3.Database.prototype.exec; 12 | originalFunctions.wait = sqlite3.Database.prototype.wait; 13 | originalFunctions.loadExtension = sqlite3.Database.prototype.loadExtension; 14 | originalFunctions.serialize = sqlite3.Database.prototype.serialize; 15 | originalFunctions.parallelize = sqlite3.Database.prototype.parallelize; 16 | originalFunctions.configure = sqlite3.Database.prototype.configure; 17 | originalFunctions.interrupt = sqlite3.Database.prototype.interrupt; 18 | }); 19 | 20 | it('allow patching native functions', function() { 21 | var myFun = function myFunction() { 22 | return "Success"; 23 | } 24 | 25 | assert.doesNotThrow(() => { 26 | sqlite3.Database.prototype.close = myFun; 27 | }); 28 | assert.doesNotThrow(() => { 29 | sqlite3.Database.prototype.exec = myFun; 30 | }); 31 | assert.doesNotThrow(() => { 32 | sqlite3.Database.prototype.wait = myFun; 33 | }); 34 | assert.doesNotThrow(() => { 35 | sqlite3.Database.prototype.loadExtension = myFun; 36 | }); 37 | assert.doesNotThrow(() => { 38 | sqlite3.Database.prototype.serialize = myFun; 39 | }); 40 | assert.doesNotThrow(() => { 41 | sqlite3.Database.prototype.parallelize = myFun; 42 | }); 43 | assert.doesNotThrow(() => { 44 | sqlite3.Database.prototype.configure = myFun; 45 | }); 46 | assert.doesNotThrow(() => { 47 | sqlite3.Database.prototype.interrupt = myFun; 48 | }); 49 | 50 | db = new sqlite3.Database(':memory:'); 51 | assert.strictEqual(db.close(), "Success"); 52 | assert.strictEqual(db.exec(), "Success"); 53 | assert.strictEqual(db.wait(), "Success"); 54 | assert.strictEqual(db.loadExtension(), "Success"); 55 | assert.strictEqual(db.serialize(), "Success"); 56 | assert.strictEqual(db.parallelize(), "Success"); 57 | assert.strictEqual(db.configure(), "Success"); 58 | assert.strictEqual(db.interrupt(), "Success"); 59 | }); 60 | 61 | after(function() { 62 | if(db != null) { 63 | sqlite3.Database.prototype.close = originalFunctions.close; 64 | sqlite3.Database.prototype.exec = originalFunctions.exec; 65 | sqlite3.Database.prototype.wait = originalFunctions.wait; 66 | sqlite3.Database.prototype.loadExtension = originalFunctions.loadExtension; 67 | sqlite3.Database.prototype.serialize = originalFunctions.serialize; 68 | sqlite3.Database.prototype.parallelize = originalFunctions.parallelize; 69 | sqlite3.Database.prototype.configure = originalFunctions.configure; 70 | sqlite3.Database.prototype.interrupt = originalFunctions.interrupt; 71 | db.close(); 72 | } 73 | }); 74 | }); 75 | 76 | describe('Statement', function() { 77 | var db; 78 | var statement; 79 | var originalFunctions = {}; 80 | 81 | before(function() { 82 | originalFunctions.bind = sqlite3.Statement.prototype.bind; 83 | originalFunctions.get = sqlite3.Statement.prototype.get; 84 | originalFunctions.run = sqlite3.Statement.prototype.run; 85 | originalFunctions.all = sqlite3.Statement.prototype.all; 86 | originalFunctions.each = sqlite3.Statement.prototype.each; 87 | originalFunctions.reset = sqlite3.Statement.prototype.reset; 88 | originalFunctions.finalize = sqlite3.Statement.prototype.finalize; 89 | }); 90 | 91 | it('allow patching native functions', function() { 92 | var myFun = function myFunction() { 93 | return "Success"; 94 | } 95 | 96 | assert.doesNotThrow(() => { 97 | sqlite3.Statement.prototype.bind = myFun; 98 | }); 99 | assert.doesNotThrow(() => { 100 | sqlite3.Statement.prototype.get = myFun; 101 | }); 102 | assert.doesNotThrow(() => { 103 | sqlite3.Statement.prototype.run = myFun; 104 | }); 105 | assert.doesNotThrow(() => { 106 | sqlite3.Statement.prototype.all = myFun; 107 | }); 108 | assert.doesNotThrow(() => { 109 | sqlite3.Statement.prototype.each = myFun; 110 | }); 111 | assert.doesNotThrow(() => { 112 | sqlite3.Statement.prototype.reset = myFun; 113 | }); 114 | assert.doesNotThrow(() => { 115 | sqlite3.Statement.prototype.finalize = myFun; 116 | }); 117 | 118 | db = new sqlite3.Database(':memory:'); 119 | statement = db.prepare(""); 120 | assert.strictEqual(statement.bind(), "Success"); 121 | assert.strictEqual(statement.get(), "Success"); 122 | assert.strictEqual(statement.run(), "Success"); 123 | assert.strictEqual(statement.all(), "Success"); 124 | assert.strictEqual(statement.each(), "Success"); 125 | assert.strictEqual(statement.reset(), "Success"); 126 | assert.strictEqual(statement.finalize(), "Success"); 127 | }); 128 | 129 | after(function() { 130 | if(statement != null) { 131 | sqlite3.Statement.prototype.bind = originalFunctions.bind; 132 | sqlite3.Statement.prototype.get = originalFunctions.get; 133 | sqlite3.Statement.prototype.run = originalFunctions.run; 134 | sqlite3.Statement.prototype.all = originalFunctions.all; 135 | sqlite3.Statement.prototype.each = originalFunctions.each; 136 | sqlite3.Statement.prototype.reset = originalFunctions.reset; 137 | sqlite3.Statement.prototype.finalize = originalFunctions.finalize; 138 | } 139 | if(db != null) { 140 | db.close(); 141 | } 142 | }); 143 | }); 144 | 145 | describe('Backup', function() { 146 | var db; 147 | var backup; 148 | var originalFunctions = {}; 149 | 150 | before(function() { 151 | originalFunctions.step = sqlite3.Backup.prototype.step; 152 | originalFunctions.finish = sqlite3.Backup.prototype.finish; 153 | }); 154 | 155 | it('allow patching native functions', function() { 156 | var myFun = function myFunction() { 157 | return "Success"; 158 | } 159 | 160 | assert.doesNotThrow(() => { 161 | sqlite3.Backup.prototype.step = myFun; 162 | }); 163 | assert.doesNotThrow(() => { 164 | sqlite3.Backup.prototype.finish = myFun; 165 | }); 166 | 167 | db = new sqlite3.Database(':memory:'); 168 | backup = db.backup("somefile", myFun); 169 | assert.strictEqual(backup.step(), "Success"); 170 | assert.strictEqual(backup.finish(), "Success"); 171 | }); 172 | 173 | after(function() { 174 | if(backup != null) { 175 | sqlite3.Backup.prototype.step = originalFunctions.step; 176 | sqlite3.Backup.prototype.finish = originalFunctions.finish; 177 | backup.finish(); 178 | } 179 | if(db != null) { 180 | db.close(); 181 | } 182 | }); 183 | }); 184 | }); 185 | -------------------------------------------------------------------------------- /test/profile.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('profiling', function() { 5 | var create = false; 6 | var select = false; 7 | 8 | var db; 9 | before(function(done) { 10 | db = new sqlite3.Database(':memory:', done); 11 | 12 | db.on('profile', function(sql, nsecs) { 13 | assert.ok(typeof nsecs === "number"); 14 | if (sql.match(/^SELECT/)) { 15 | assert.ok(!select); 16 | assert.equal(sql, "SELECT * FROM foo"); 17 | select = true; 18 | } 19 | else if (sql.match(/^CREATE/)) { 20 | assert.ok(!create); 21 | assert.equal(sql, "CREATE TABLE foo (id int)"); 22 | create = true; 23 | } 24 | else { 25 | assert.ok(false); 26 | } 27 | }); 28 | }); 29 | 30 | it('should profile a create table', function(done) { 31 | assert.ok(!create); 32 | db.run("CREATE TABLE foo (id int)", function(err) { 33 | if (err) throw err; 34 | setImmediate(function() { 35 | assert.ok(create); 36 | done(); 37 | }); 38 | }); 39 | }); 40 | 41 | 42 | it('should profile a select', function(done) { 43 | assert.ok(!select); 44 | db.run("SELECT * FROM foo", function(err) { 45 | if (err) throw err; 46 | setImmediate(function() { 47 | assert.ok(select); 48 | done(); 49 | }, 0); 50 | }); 51 | }); 52 | 53 | after(function(done) { 54 | db.close(done); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/rerun.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('rerunning statements', function() { 5 | var db; 6 | before(function(done) { db = new sqlite3.Database(':memory:', done); }); 7 | 8 | var count = 10; 9 | var inserted = 0; 10 | var retrieved = 0; 11 | 12 | it('should create the table', function(done) { 13 | db.run("CREATE TABLE foo (id int)", done); 14 | }); 15 | 16 | it('should insert repeatedly, reusing the same statement', function(done) { 17 | var stmt = db.prepare("INSERT INTO foo VALUES(?)"); 18 | for (var i = 5; i < count; i++) { 19 | stmt.run(i, function(err) { 20 | if (err) throw err; 21 | inserted++; 22 | }); 23 | } 24 | stmt.finalize(done); 25 | }); 26 | 27 | it('should retrieve repeatedly, resuing the same statement', function(done) { 28 | var collected = []; 29 | var stmt = db.prepare("SELECT id FROM foo WHERE id = ?"); 30 | for (var i = 0; i < count; i++) { 31 | stmt.get(i, function(err, row) { 32 | if (err) throw err; 33 | if (row) collected.push(row); 34 | }); 35 | } 36 | stmt.finalize(function(err) { 37 | if (err) throw err; 38 | retrieved += collected.length; 39 | assert.deepEqual(collected, [ { id: 5 }, { id: 6 }, { id: 7 }, { id: 8 }, { id: 9 } ]); 40 | done(); 41 | }); 42 | }); 43 | 44 | it('should have inserted and retrieved the right amount', function() { 45 | assert.equal(inserted, 5); 46 | assert.equal(retrieved, 5); 47 | }); 48 | 49 | after(function(done) { db.close(done); }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/scheduling.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('scheduling', function() { 5 | it('scheduling after the database was closed', function(done) { 6 | var db = new sqlite3.Database(':memory:'); 7 | db.on('error', function(err) { 8 | assert.ok(err.message && err.message.indexOf("SQLITE_MISUSE: Database handle is closed") > -1); 9 | done(); 10 | }); 11 | 12 | db.close(); 13 | db.run("CREATE TABLE foo (id int)"); 14 | }); 15 | 16 | 17 | it('scheduling a query with callback after the database was closed', function(done) { 18 | var db = new sqlite3.Database(':memory:'); 19 | db.on('error', function(err) { 20 | assert.ok(false, 'Event was accidentally triggered'); 21 | }); 22 | 23 | db.close(); 24 | db.run("CREATE TABLE foo (id int)", function(err) { 25 | assert.ok(err.message && err.message.indexOf("SQLITE_MISUSE: Database handle is closed") > -1); 26 | done(); 27 | }); 28 | }); 29 | 30 | it('running a query after the database was closed', function(done) { 31 | var db = new sqlite3.Database(':memory:'); 32 | 33 | var stmt = db.prepare("SELECT * FROM sqlite_master", function(err) { 34 | if (err) throw err; 35 | db.close(function(err) { 36 | assert.ok(err); 37 | assert.ok(err.message && err.message.indexOf("SQLITE_BUSY: unable to close due to") > -1); 38 | 39 | // Running a statement now should not fail. 40 | stmt.run(done); 41 | }); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/serialization.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | 5 | describe('serialize() and parallelize()', function() { 6 | var db; 7 | before(function(done) { db = new sqlite3.Database(':memory:', done); }); 8 | 9 | var inserted1 = 0; 10 | var inserted2 = 0; 11 | var retrieved = 0; 12 | 13 | var count = 1000; 14 | 15 | it('should toggle', function(done) { 16 | db.serialize(); 17 | db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)"); 18 | db.parallelize(done); 19 | }); 20 | 21 | it('should insert rows', function() { 22 | var stmt1 = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)"); 23 | var stmt2 = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)"); 24 | for (var i = 0; i < count; i++) { 25 | // Interleaved inserts with two statements. 26 | stmt1.run('String ' + i, i, i * Math.PI, function(err) { 27 | if (err) throw err; 28 | inserted1++; 29 | }); 30 | i++; 31 | stmt2.run('String ' + i, i, i * Math.PI, function(err) { 32 | if (err) throw err; 33 | inserted2++; 34 | }); 35 | } 36 | stmt1.finalize(); 37 | stmt2.finalize(); 38 | }); 39 | 40 | it('should have inserted all the rows after synchronizing with serialize()', function(done) { 41 | db.serialize(); 42 | db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) { 43 | if (err) throw err; 44 | for (var i = 0; i < rows.length; i++) { 45 | assert.equal(rows[i].txt, 'String ' + i); 46 | assert.equal(rows[i].num, i); 47 | assert.equal(rows[i].flt, i * Math.PI); 48 | assert.equal(rows[i].blb, null); 49 | retrieved++; 50 | } 51 | 52 | assert.equal(count, inserted1 + inserted2, "Didn't insert all rows"); 53 | assert.equal(count, retrieved, "Didn't retrieve all rows"); 54 | done(); 55 | }); 56 | }); 57 | 58 | after(function(done) { db.close(done); }); 59 | }); 60 | 61 | describe('serialize(fn)', function() { 62 | var db; 63 | before(function(done) { db = new sqlite3.Database(':memory:', done); }); 64 | 65 | var inserted = 0; 66 | var retrieved = 0; 67 | 68 | var count = 1000; 69 | 70 | it('should call the callback', function(done) { 71 | db.serialize(function() { 72 | db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)"); 73 | 74 | var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)"); 75 | for (var i = 0; i < count; i++) { 76 | stmt.run('String ' + i, i, i * Math.PI, function(err) { 77 | if (err) throw err; 78 | inserted++; 79 | }); 80 | } 81 | stmt.finalize(); 82 | 83 | db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) { 84 | if (err) throw err; 85 | for (var i = 0; i < rows.length; i++) { 86 | assert.equal(rows[i].txt, 'String ' + i); 87 | assert.equal(rows[i].num, i); 88 | assert.equal(rows[i].flt, i * Math.PI); 89 | assert.equal(rows[i].blb, null); 90 | retrieved++; 91 | } 92 | done(); 93 | }); 94 | }); 95 | }); 96 | 97 | 98 | it('should have inserted and retrieved all rows', function() { 99 | assert.equal(count, inserted, "Didn't insert all rows"); 100 | assert.equal(count, retrieved, "Didn't retrieve all rows"); 101 | }); 102 | 103 | after(function(done) { db.close(done); }); 104 | }); 105 | -------------------------------------------------------------------------------- /test/support/createdb-electron.js: -------------------------------------------------------------------------------- 1 | 2 | var {app} = require('electron'); 3 | var createdb = require('./createdb.js'); 4 | 5 | createdb(function () { 6 | setTimeout(function () { 7 | app.quit(); 8 | }, 20000); 9 | }); 10 | 11 | -------------------------------------------------------------------------------- /test/support/createdb.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | function createdb(callback) { 4 | var existsSync = require('fs').existsSync || require('path').existsSync; 5 | var path = require('path'); 6 | 7 | var sqlite3 = require('../../lib/sqlite3'); 8 | 9 | var count = 1000000; 10 | var db_path = path.join(__dirname,'big.db'); 11 | 12 | function randomString() { 13 | var str = ''; 14 | var chars = 'abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY0123456789 '; 15 | for (var i = Math.random() * 100; i > 0; i--) { 16 | str += chars[Math.floor(Math.random() * chars.length)]; 17 | } 18 | return str; 19 | } 20 | 21 | 22 | if (existsSync(db_path)) { 23 | console.log('okay: database already created (' + db_path + ')'); 24 | if (callback) callback(); 25 | } else { 26 | console.log("Creating test database... This may take several minutes."); 27 | var db = new sqlite3.Database(db_path); 28 | db.serialize(function() { 29 | db.run("CREATE TABLE foo (id INT, txt TEXT)"); 30 | db.run("BEGIN TRANSACTION"); 31 | var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); 32 | for (var i = 0; i < count; i++) { 33 | stmt.run(i, randomString()); 34 | } 35 | stmt.finalize(); 36 | db.run("COMMIT TRANSACTION", [], function () { 37 | db.close(callback); 38 | }); 39 | }); 40 | } 41 | } 42 | 43 | if (require.main === module) { 44 | createdb(); 45 | } 46 | 47 | module.exports = createdb; 48 | -------------------------------------------------------------------------------- /test/support/elmo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-node-sqlite3/61db6fcfac21dcf9708a7e0e74e3a988f0134d11/test/support/elmo.png -------------------------------------------------------------------------------- /test/support/helper.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var fs = require('fs'); 3 | var pathExists = require('fs').existsSync || require('path').existsSync; 4 | 5 | exports.deleteFile = function(name) { 6 | try { 7 | fs.unlinkSync(name); 8 | } catch(err) { 9 | if (err.errno !== process.ENOENT && err.code !== 'ENOENT' && err.syscall !== 'unlink') { 10 | throw err; 11 | } 12 | } 13 | }; 14 | 15 | exports.ensureExists = function(name,cb) { 16 | if (!pathExists(name)) { 17 | fs.mkdirSync(name); 18 | } 19 | }; 20 | 21 | assert.fileDoesNotExist = function(name) { 22 | try { 23 | fs.statSync(name); 24 | } catch(err) { 25 | if (err.errno !== process.ENOENT && err.code !== 'ENOENT' && err.syscall !== 'unlink') { 26 | throw err; 27 | } 28 | } 29 | }; 30 | 31 | assert.fileExists = function(name) { 32 | try { 33 | fs.statSync(name); 34 | } catch(err) { 35 | throw err; 36 | } 37 | }; -------------------------------------------------------------------------------- /test/support/prepare.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-node-sqlite3/61db6fcfac21dcf9708a7e0e74e3a988f0134d11/test/support/prepare.db -------------------------------------------------------------------------------- /test/support/script.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS map ( 2 | zoom_level INTEGER, 3 | tile_column INTEGER, 4 | tile_row INTEGER, 5 | tile_id TEXT, 6 | grid_id TEXT 7 | ); 8 | 9 | CREATE TABLE IF NOT EXISTS grid_key ( 10 | grid_id TEXT, 11 | key_name TEXT 12 | ); 13 | 14 | CREATE TABLE IF NOT EXISTS keymap ( 15 | key_name TEXT, 16 | key_json TEXT 17 | ); 18 | 19 | CREATE TABLE IF NOT EXISTS grid_utfgrid ( 20 | grid_id TEXT, 21 | grid_utfgrid TEXT 22 | ); 23 | 24 | CREATE TABLE IF NOT EXISTS images ( 25 | tile_data blob, 26 | tile_id text 27 | ); 28 | 29 | CREATE TABLE IF NOT EXISTS metadata ( 30 | name text, 31 | value text 32 | ); 33 | 34 | 35 | CREATE UNIQUE INDEX IF NOT EXISTS map_index ON map (zoom_level, tile_column, tile_row); 36 | CREATE UNIQUE INDEX IF NOT EXISTS grid_key_lookup ON grid_key (grid_id, key_name); 37 | CREATE UNIQUE INDEX IF NOT EXISTS keymap_lookup ON keymap (key_name); 38 | CREATE UNIQUE INDEX IF NOT EXISTS grid_utfgrid_lookup ON grid_utfgrid (grid_id); 39 | CREATE UNIQUE INDEX IF NOT EXISTS images_id ON images (tile_id); 40 | CREATE UNIQUE INDEX IF NOT EXISTS name ON metadata (name); 41 | 42 | 43 | CREATE VIEW IF NOT EXISTS tiles AS 44 | SELECT 45 | map.zoom_level AS zoom_level, 46 | map.tile_column AS tile_column, 47 | map.tile_row AS tile_row, 48 | images.tile_data AS tile_data 49 | FROM map 50 | JOIN images ON images.tile_id = map.tile_id; 51 | 52 | CREATE VIEW IF NOT EXISTS grids AS 53 | SELECT 54 | map.zoom_level AS zoom_level, 55 | map.tile_column AS tile_column, 56 | map.tile_row AS tile_row, 57 | grid_utfgrid.grid_utfgrid AS grid 58 | FROM map 59 | JOIN grid_utfgrid ON grid_utfgrid.grid_id = map.grid_id; 60 | 61 | CREATE VIEW IF NOT EXISTS grid_data AS 62 | SELECT 63 | map.zoom_level AS zoom_level, 64 | map.tile_column AS tile_column, 65 | map.tile_row AS tile_row, 66 | keymap.key_name AS key_name, 67 | keymap.key_json AS key_json 68 | FROM map 69 | JOIN grid_key ON map.grid_id = grid_key.grid_id 70 | JOIN keymap ON grid_key.key_name = keymap.key_name; 71 | -------------------------------------------------------------------------------- /test/trace.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('tracing', function() { 5 | it('Database tracing', function(done) { 6 | var db = new sqlite3.Database(':memory:'); 7 | var create = false; 8 | var select = false; 9 | 10 | db.on('trace', function(sql) { 11 | if (sql.match(/^SELECT/)) { 12 | assert.ok(!select); 13 | assert.equal(sql, "SELECT * FROM foo"); 14 | select = true; 15 | } 16 | else if (sql.match(/^CREATE/)) { 17 | assert.ok(!create); 18 | assert.equal(sql, "CREATE TABLE foo (id int)"); 19 | create = true; 20 | } 21 | else { 22 | assert.ok(false); 23 | } 24 | }); 25 | 26 | db.serialize(function() { 27 | db.run("CREATE TABLE foo (id int)"); 28 | db.run("SELECT * FROM foo"); 29 | }); 30 | 31 | db.close(function(err) { 32 | if (err) throw err; 33 | assert.ok(create); 34 | assert.ok(select); 35 | done(); 36 | }); 37 | }); 38 | 39 | 40 | it('test disabling tracing #1', function(done) { 41 | var db = new sqlite3.Database(':memory:'); 42 | 43 | db.on('trace', function(sql) {}); 44 | db.removeAllListeners('trace'); 45 | db._events['trace'] = function(sql) { 46 | assert.ok(false); 47 | }; 48 | 49 | db.run("CREATE TABLE foo (id int)"); 50 | db.close(done); 51 | }); 52 | 53 | 54 | it('test disabling tracing #2', function(done) { 55 | var db = new sqlite3.Database(':memory:'); 56 | 57 | var trace = function(sql) {}; 58 | db.on('trace', trace); 59 | db.removeListener('trace', trace); 60 | db._events['trace'] = function(sql) { 61 | assert.ok(false); 62 | }; 63 | 64 | db.run("CREATE TABLE foo (id int)"); 65 | db.close(done); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/unicode.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('unicode', function() { 5 | var first_values = [], 6 | trailing_values = [], 7 | chars = [], 8 | subranges = new Array(2), 9 | len = subranges.length, 10 | db, 11 | i; 12 | 13 | before(function(done) { db = new sqlite3.Database(':memory:', done); }); 14 | 15 | for (i = 0x20; i < 0x80; i++) { 16 | first_values.push(i); 17 | } 18 | 19 | for (i = 0xc2; i < 0xf0; i++) { 20 | first_values.push(i); 21 | } 22 | 23 | for (i = 0x80; i < 0xc0; i++) { 24 | trailing_values.push(i); 25 | } 26 | 27 | for (i = 0; i < len; i++) { 28 | subranges[i] = []; 29 | } 30 | 31 | for (i = 0xa0; i < 0xc0; i++) { 32 | subranges[0].push(i); 33 | } 34 | 35 | for (i = 0x80; i < 0xa0; i++) { 36 | subranges[1].push(i); 37 | } 38 | 39 | function random_choice(arr) { 40 | return arr[Math.random() * arr.length | 0]; 41 | } 42 | 43 | function random_utf8() { 44 | var first = random_choice(first_values); 45 | 46 | if (first < 0x80) { 47 | return String.fromCharCode(first); 48 | } else if (first < 0xe0) { 49 | return String.fromCharCode((first & 0x1f) << 0x6 | random_choice(trailing_values) & 0x3f); 50 | } else if (first == 0xe0) { 51 | return String.fromCharCode(((first & 0xf) << 0xc) | ((random_choice(subranges[0]) & 0x3f) << 6) | random_choice(trailing_values) & 0x3f); 52 | } else if (first == 0xed) { 53 | return String.fromCharCode(((first & 0xf) << 0xc) | ((random_choice(subranges[1]) & 0x3f) << 6) | random_choice(trailing_values) & 0x3f); 54 | } else if (first < 0xf0) { 55 | return String.fromCharCode(((first & 0xf) << 0xc) | ((random_choice(trailing_values) & 0x3f) << 6) | random_choice(trailing_values) & 0x3f); 56 | } 57 | } 58 | 59 | function randomString() { 60 | var str = '', 61 | i; 62 | 63 | for (i = Math.random() * 300; i > 0; i--) { 64 | str += random_utf8(); 65 | } 66 | 67 | return str; 68 | } 69 | 70 | 71 | // Generate random data. 72 | var data = []; 73 | var length = Math.floor(Math.random() * 1000) + 200; 74 | for (var i = 0; i < length; i++) { 75 | data.push(randomString()); 76 | } 77 | 78 | var inserted = 0; 79 | var retrieved = 0; 80 | 81 | it('should create the table', function(done) { 82 | db.run("CREATE TABLE foo (id int, txt text)", done); 83 | }); 84 | 85 | it('should insert all values', function(done) { 86 | var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); 87 | for (var i = 0; i < data.length; i++) { 88 | stmt.run(i, data[i], function(err) { 89 | if (err) throw err; 90 | inserted++; 91 | }); 92 | } 93 | stmt.finalize(done); 94 | }); 95 | 96 | it('should retrieve all values', function(done) { 97 | db.all("SELECT txt FROM foo ORDER BY id", function(err, rows) { 98 | if (err) throw err; 99 | 100 | for (var i = 0; i < rows.length; i++) { 101 | assert.equal(rows[i].txt, data[i]); 102 | retrieved++; 103 | } 104 | done(); 105 | }); 106 | }); 107 | 108 | it('should have inserted and retrieved the correct amount', function() { 109 | assert.equal(inserted, length); 110 | assert.equal(retrieved, length); 111 | }); 112 | 113 | after(function(done) { db.close(done); }); 114 | }); 115 | -------------------------------------------------------------------------------- /test/update_hook.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('update_hook', function() { 5 | var db; 6 | 7 | beforeEach(function(done) { 8 | db = new sqlite3.Database(':memory:', function(err) { 9 | if (err) return done(err); 10 | 11 | db.run("CREATE TABLE update_hooks_test (id int PRIMARY KEY, value text)", done); 12 | }); 13 | }); 14 | 15 | it('emits insert event on inserting data to table', function(done) { 16 | db.addListener('change', function(eventType, database, table, rowId) { 17 | assert.equal(eventType, 'insert'); 18 | assert.equal(database, 'main'); 19 | assert.equal(table, 'update_hooks_test'); 20 | assert.equal(rowId, 1); 21 | 22 | return done(); 23 | }); 24 | 25 | db.run("INSERT INTO update_hooks_test VALUES (1, 'value')", function(err) { 26 | if (err) return done(err); 27 | }); 28 | }); 29 | 30 | it('emits update event on row modification in table', function(done) { 31 | db.run("INSERT INTO update_hooks_test VALUES (2, 'value'), (3, 'value4')", function(err) { 32 | if (err) return done(err); 33 | 34 | db.addListener('change', function(eventType, database, table, rowId) { 35 | assert.equal(eventType, 'update'); 36 | assert.equal(database, 'main'); 37 | assert.equal(table, 'update_hooks_test'); 38 | assert.equal(rowId, 1); 39 | 40 | db.all("SELECT * FROM update_hooks_test WHERE rowid = ?", rowId, function(err, rows) { 41 | assert.deepEqual(rows, [{ id: 2, value: 'new_val' }]); 42 | 43 | return done(err); 44 | }); 45 | }); 46 | 47 | db.run("UPDATE update_hooks_test SET value = 'new_val' WHERE id = 2", function(err) { 48 | if (err) return done(err); 49 | }); 50 | }); 51 | }); 52 | 53 | it('emits delete event on row was deleted from table', function(done) { 54 | db.run("INSERT INTO update_hooks_test VALUES (2, 'value')", function(err) { 55 | if (err) return done(err); 56 | 57 | db.addListener('change', function(eventType, database, table, rowId) { 58 | assert.equal(eventType, 'delete'); 59 | assert.equal(database, 'main'); 60 | assert.equal(table, 'update_hooks_test'); 61 | assert.equal(rowId, 1); 62 | 63 | return done(); 64 | }); 65 | 66 | db.run("DELETE FROM update_hooks_test WHERE id = 2", function(err) { 67 | if (err) return done(err); 68 | }); 69 | }); 70 | }); 71 | 72 | afterEach(function(done) { 73 | db.close(done); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/upsert.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | describe('query properties', function() { 5 | var db; 6 | before(function(done) { 7 | db = new sqlite3.Database(':memory:'); 8 | db.run("CREATE TABLE foo (id INT PRIMARY KEY, count INT)", done); 9 | }); 10 | 11 | (sqlite3.VERSION_NUMBER < 3024000 ? it.skip : it)('should upsert', function(done) { 12 | var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); 13 | stmt.run(1, 1, function(err) { // insert 1 14 | if (err) throw err; 15 | var upsert_stmt = db.prepare("INSERT INTO foo VALUES(?, ?) ON CONFLICT (id) DO UPDATE SET count = count + excluded.count"); 16 | upsert_stmt.run(1, 2, function(err) { // add 2 17 | if (err) throw err; 18 | var select_stmt = db.prepare("SELECT count FROM foo WHERE id = ?"); 19 | select_stmt.get(1, function(err, row) { 20 | if (err) throw err; 21 | assert.equal(row.count, 3); // equals 3 22 | }); 23 | }); 24 | }); 25 | db.wait(done); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/verbose.test.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('..'); 2 | var assert = require('assert'); 3 | 4 | var invalid_sql = 'update non_existent_table set id=1'; 5 | 6 | var originalMethods = { 7 | Database: {}, 8 | Statement: {}, 9 | }; 10 | 11 | function backupOriginalMethods() { 12 | for (var obj in originalMethods) { 13 | for (var attr in sqlite3[obj].prototype) { 14 | originalMethods[obj][attr] = sqlite3[obj].prototype[attr]; 15 | } 16 | } 17 | } 18 | 19 | function resetVerbose() { 20 | for (var obj in originalMethods) { 21 | for (var attr in originalMethods[obj]) { 22 | sqlite3[obj].prototype[attr] = originalMethods[obj][attr]; 23 | } 24 | } 25 | } 26 | 27 | describe('verbose', function() { 28 | it('Shoud add trace info to error when verbose is called', function(done) { 29 | var db = new sqlite3.Database(':memory:'); 30 | backupOriginalMethods(); 31 | sqlite3.verbose(); 32 | 33 | db.run(invalid_sql, function(err) { 34 | assert(err instanceof Error); 35 | 36 | assert( 37 | err.stack.indexOf(`Database#run('${invalid_sql}'`) > -1, 38 | `Stack shoud contain trace info, stack = ${err.stack}` 39 | ); 40 | 41 | done(); 42 | resetVerbose(); 43 | }); 44 | }); 45 | 46 | it('Shoud not add trace info to error when verbose is not called', function(done) { 47 | var db = new sqlite3.Database(':memory:'); 48 | 49 | db.run(invalid_sql, function(err) { 50 | assert(err instanceof Error); 51 | 52 | assert( 53 | err.stack.indexOf(invalid_sql) === -1, 54 | `Stack shoud not contain trace info, stack = ${err.stack}` 55 | ); 56 | 57 | done(); 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /tools/BinaryBuilder.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG NODE_VERSION=16 2 | ARG VARIANT=bullseye 3 | 4 | FROM node:$NODE_VERSION-$VARIANT 5 | 6 | ARG VARIANT 7 | 8 | RUN if [[ "$VARIANT" =~ alpine* ]] ; then apk add build-base python3 --update-cache ; fi 9 | 10 | WORKDIR /usr/src/build 11 | 12 | COPY . . 13 | RUN npm install --ignore-scripts 14 | 15 | # Workaround for https://github.com/mapbox/node-pre-gyp/issues/644 16 | RUN cd node_modules/\@mapbox/node-pre-gyp \ 17 | && npm install fs-extra@10.0.1 \ 18 | && sed -i -e s/\'fs/\'fs-extra/ -e s/fs\.renameSync/fs.moveSync/ ./lib/util/napi.js 19 | 20 | ENV CFLAGS="${CFLAGS:-} -include ../src/gcc-preinclude.h" 21 | ENV CXXFLAGS="${CXXFLAGS:-} -include ../src/gcc-preinclude.h" 22 | RUN npx node-pre-gyp configure 23 | RUN npx node-pre-gyp build 24 | 25 | RUN if [[ ! "$VARIANT" =~ alpine* ]] ; then ldd lib/binding/*/node_sqlite3.node; nm lib/binding/*/node_sqlite3.node | grep "GLIBC_" | c++filt || true ; fi 26 | 27 | RUN npm run test 28 | RUN npx node-pre-gyp package 29 | 30 | CMD ["sh"] 31 | -------------------------------------------------------------------------------- /tools/benchmark/insert.js: -------------------------------------------------------------------------------- 1 | var sqlite3 = require('../../lib/sqlite3'); 2 | var fs = require('fs'); 3 | 4 | var iterations = 10000; 5 | 6 | exports.compare = { 7 | 'insert literal file': function(finished) { 8 | var db = new sqlite3.Database(''); 9 | var file = fs.readFileSync('benchmark/insert-transaction.sql', 'utf8'); 10 | db.exec(file); 11 | db.close(finished); 12 | }, 13 | 14 | 'insert with transaction and two statements': function(finished) { 15 | var db = new sqlite3.Database(''); 16 | 17 | db.serialize(function() { 18 | db.run("CREATE TABLE foo (id INT, txt TEXT)"); 19 | db.run("BEGIN"); 20 | 21 | db.parallelize(function() { 22 | var stmt1 = db.prepare("INSERT INTO foo VALUES (?, ?)"); 23 | var stmt2 = db.prepare("INSERT INTO foo VALUES (?, ?)"); 24 | for (var i = 0; i < iterations; i++) { 25 | stmt1.run(i, 'Row ' + i); 26 | i++; 27 | stmt2.run(i, 'Row ' + i); 28 | } 29 | stmt1.finalize(); 30 | stmt2.finalize(); 31 | }); 32 | 33 | db.run("COMMIT"); 34 | }); 35 | 36 | db.close(finished); 37 | }, 38 | 'insert with transaction': function(finished) { 39 | var db = new sqlite3.Database(''); 40 | 41 | db.serialize(function() { 42 | db.run("CREATE TABLE foo (id INT, txt TEXT)"); 43 | db.run("BEGIN"); 44 | var stmt = db.prepare("INSERT INTO foo VALUES (?, ?)"); 45 | for (var i = 0; i < iterations; i++) { 46 | stmt.run(i, 'Row ' + i); 47 | } 48 | stmt.finalize(); 49 | db.run("COMMIT"); 50 | }); 51 | 52 | db.close(finished); 53 | }, 54 | 'insert without transaction': function(finished) { 55 | var db = new sqlite3.Database(''); 56 | 57 | db.serialize(function() { 58 | db.run("CREATE TABLE foo (id INT, txt TEXT)"); 59 | var stmt = db.prepare("INSERT INTO foo VALUES (?, ?)"); 60 | for (var i = 0; i < iterations; i++) { 61 | stmt.run(i, 'Row ' + i); 62 | } 63 | stmt.finalize(); 64 | }); 65 | 66 | db.close(finished); 67 | } 68 | }; -------------------------------------------------------------------------------- /tools/benchmark/select-data.sql: -------------------------------------------------------------------------------- 1 | CREATE TEMP TABLE Bits (Bit INTEGER PRIMARY KEY); 2 | INSERT INTO Bits VALUES (0); 3 | INSERT INTO Bits VALUES (1); 4 | 5 | CREATE TEMP TABLE Nums AS SELECT 6 | b9.Bit * 512 + b8.Bit * 256 + b7.Bit * 128 + b6.Bit * 64 + b5.Bit * 32 + 7 | b4.Bit * 16 + b3.Bit * 8 + b2.Bit * 4 + b1.Bit * 2 + b0.Bit 8 | AS Num 9 | FROM Bits b9, Bits b8, Bits b7, Bits b6, Bits b5, 10 | Bits b4, Bits b3, Bits b2, Bits b1, Bits b0; 11 | 12 | CREATE TABLE foo (id INT, txt TEXT); 13 | BEGIN; 14 | INSERT INTO foo SELECT 0 + Num, 'Row ' || ( 0 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 15 | INSERT INTO foo SELECT 1000 + Num, 'Row ' || ( 1000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 16 | INSERT INTO foo SELECT 2000 + Num, 'Row ' || ( 2000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 17 | INSERT INTO foo SELECT 3000 + Num, 'Row ' || ( 3000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 18 | INSERT INTO foo SELECT 4000 + Num, 'Row ' || ( 4000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 19 | INSERT INTO foo SELECT 5000 + Num, 'Row ' || ( 5000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 20 | INSERT INTO foo SELECT 6000 + Num, 'Row ' || ( 6000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 21 | INSERT INTO foo SELECT 7000 + Num, 'Row ' || ( 7000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 22 | INSERT INTO foo SELECT 8000 + Num, 'Row ' || ( 8000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 23 | INSERT INTO foo SELECT 9000 + Num, 'Row ' || ( 9000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 24 | INSERT INTO foo SELECT 10000 + Num, 'Row ' || (10000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 25 | INSERT INTO foo SELECT 11000 + Num, 'Row ' || (11000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 26 | INSERT INTO foo SELECT 12000 + Num, 'Row ' || (12000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 27 | INSERT INTO foo SELECT 13000 + Num, 'Row ' || (13000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 28 | INSERT INTO foo SELECT 14000 + Num, 'Row ' || (14000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 29 | INSERT INTO foo SELECT 15000 + Num, 'Row ' || (15000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 30 | INSERT INTO foo SELECT 16000 + Num, 'Row ' || (16000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 31 | INSERT INTO foo SELECT 17000 + Num, 'Row ' || (17000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 32 | INSERT INTO foo SELECT 18000 + Num, 'Row ' || (18000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 33 | INSERT INTO foo SELECT 19000 + Num, 'Row ' || (19000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 34 | INSERT INTO foo SELECT 20000 + Num, 'Row ' || (20000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 35 | INSERT INTO foo SELECT 21000 + Num, 'Row ' || (21000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 36 | INSERT INTO foo SELECT 22000 + Num, 'Row ' || (22000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 37 | INSERT INTO foo SELECT 23000 + Num, 'Row ' || (23000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 38 | INSERT INTO foo SELECT 24000 + Num, 'Row ' || (24000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 39 | INSERT INTO foo SELECT 25000 + Num, 'Row ' || (25000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 40 | INSERT INTO foo SELECT 26000 + Num, 'Row ' || (26000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 41 | INSERT INTO foo SELECT 27000 + Num, 'Row ' || (27000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 42 | INSERT INTO foo SELECT 28000 + Num, 'Row ' || (28000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 43 | INSERT INTO foo SELECT 29000 + Num, 'Row ' || (29000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 44 | INSERT INTO foo SELECT 30000 + Num, 'Row ' || (30000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 45 | INSERT INTO foo SELECT 31000 + Num, 'Row ' || (31000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 46 | INSERT INTO foo SELECT 32000 + Num, 'Row ' || (32000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 47 | INSERT INTO foo SELECT 33000 + Num, 'Row ' || (33000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 48 | INSERT INTO foo SELECT 34000 + Num, 'Row ' || (34000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 49 | INSERT INTO foo SELECT 35000 + Num, 'Row ' || (35000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 50 | INSERT INTO foo SELECT 36000 + Num, 'Row ' || (36000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 51 | INSERT INTO foo SELECT 37000 + Num, 'Row ' || (37000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 52 | INSERT INTO foo SELECT 38000 + Num, 'Row ' || (38000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 53 | INSERT INTO foo SELECT 39000 + Num, 'Row ' || (39000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 54 | INSERT INTO foo SELECT 40000 + Num, 'Row ' || (40000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 55 | INSERT INTO foo SELECT 41000 + Num, 'Row ' || (41000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 56 | INSERT INTO foo SELECT 42000 + Num, 'Row ' || (42000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 57 | INSERT INTO foo SELECT 43000 + Num, 'Row ' || (43000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 58 | INSERT INTO foo SELECT 44000 + Num, 'Row ' || (44000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 59 | INSERT INTO foo SELECT 45000 + Num, 'Row ' || (45000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 60 | INSERT INTO foo SELECT 46000 + Num, 'Row ' || (46000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 61 | INSERT INTO foo SELECT 47000 + Num, 'Row ' || (47000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 62 | INSERT INTO foo SELECT 48000 + Num, 'Row ' || (48000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 63 | INSERT INTO foo SELECT 49000 + Num, 'Row ' || (49000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 64 | INSERT INTO foo SELECT 50000 + Num, 'Row ' || (50000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 65 | INSERT INTO foo SELECT 51000 + Num, 'Row ' || (51000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 66 | INSERT INTO foo SELECT 52000 + Num, 'Row ' || (52000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 67 | INSERT INTO foo SELECT 53000 + Num, 'Row ' || (53000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 68 | INSERT INTO foo SELECT 54000 + Num, 'Row ' || (54000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 69 | INSERT INTO foo SELECT 55000 + Num, 'Row ' || (55000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 70 | INSERT INTO foo SELECT 56000 + Num, 'Row ' || (56000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 71 | INSERT INTO foo SELECT 57000 + Num, 'Row ' || (57000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 72 | INSERT INTO foo SELECT 58000 + Num, 'Row ' || (58000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 73 | INSERT INTO foo SELECT 59000 + Num, 'Row ' || (59000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 74 | INSERT INTO foo SELECT 60000 + Num, 'Row ' || (60000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 75 | INSERT INTO foo SELECT 61000 + Num, 'Row ' || (61000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 76 | INSERT INTO foo SELECT 62000 + Num, 'Row ' || (62000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 77 | INSERT INTO foo SELECT 63000 + Num, 'Row ' || (63000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 78 | INSERT INTO foo SELECT 64000 + Num, 'Row ' || (64000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 79 | INSERT INTO foo SELECT 65000 + Num, 'Row ' || (65000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 80 | INSERT INTO foo SELECT 66000 + Num, 'Row ' || (66000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 81 | INSERT INTO foo SELECT 67000 + Num, 'Row ' || (67000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 82 | INSERT INTO foo SELECT 68000 + Num, 'Row ' || (68000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 83 | INSERT INTO foo SELECT 69000 + Num, 'Row ' || (69000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 84 | INSERT INTO foo SELECT 70000 + Num, 'Row ' || (70000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 85 | INSERT INTO foo SELECT 71000 + Num, 'Row ' || (71000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 86 | INSERT INTO foo SELECT 72000 + Num, 'Row ' || (72000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 87 | INSERT INTO foo SELECT 73000 + Num, 'Row ' || (73000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 88 | INSERT INTO foo SELECT 74000 + Num, 'Row ' || (74000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 89 | INSERT INTO foo SELECT 75000 + Num, 'Row ' || (75000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 90 | INSERT INTO foo SELECT 76000 + Num, 'Row ' || (76000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 91 | INSERT INTO foo SELECT 77000 + Num, 'Row ' || (77000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 92 | INSERT INTO foo SELECT 78000 + Num, 'Row ' || (78000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 93 | INSERT INTO foo SELECT 79000 + Num, 'Row ' || (79000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 94 | INSERT INTO foo SELECT 80000 + Num, 'Row ' || (80000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 95 | INSERT INTO foo SELECT 81000 + Num, 'Row ' || (81000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 96 | INSERT INTO foo SELECT 82000 + Num, 'Row ' || (82000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 97 | INSERT INTO foo SELECT 83000 + Num, 'Row ' || (83000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 98 | INSERT INTO foo SELECT 84000 + Num, 'Row ' || (84000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 99 | INSERT INTO foo SELECT 85000 + Num, 'Row ' || (85000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 100 | INSERT INTO foo SELECT 86000 + Num, 'Row ' || (86000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 101 | INSERT INTO foo SELECT 87000 + Num, 'Row ' || (87000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 102 | INSERT INTO foo SELECT 88000 + Num, 'Row ' || (88000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 103 | INSERT INTO foo SELECT 89000 + Num, 'Row ' || (89000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 104 | INSERT INTO foo SELECT 90000 + Num, 'Row ' || (90000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 105 | INSERT INTO foo SELECT 91000 + Num, 'Row ' || (91000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 106 | INSERT INTO foo SELECT 92000 + Num, 'Row ' || (92000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 107 | INSERT INTO foo SELECT 93000 + Num, 'Row ' || (93000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 108 | INSERT INTO foo SELECT 94000 + Num, 'Row ' || (94000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 109 | INSERT INTO foo SELECT 95000 + Num, 'Row ' || (95000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 110 | INSERT INTO foo SELECT 96000 + Num, 'Row ' || (96000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 111 | INSERT INTO foo SELECT 97000 + Num, 'Row ' || (97000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 112 | INSERT INTO foo SELECT 98000 + Num, 'Row ' || (98000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 113 | INSERT INTO foo SELECT 99000 + Num, 'Row ' || (99000 + Num) FROM Nums WHERE Num BETWEEN 1 AND 1000; 114 | COMMIT; 115 | -------------------------------------------------------------------------------- /tools/benchmark/select.js: -------------------------------------------------------------------------------- 1 | const sqlite3 = require('../../'); 2 | const { readFileSync } = require('fs'); 3 | const db = new sqlite3.Database(':memory:'); 4 | 5 | db.serialize(() => { 6 | db.exec(readFileSync(`${__dirname}/select-data.sql`, 'utf8'), (err) => { 7 | if (err) throw err; 8 | console.time('db.each'); 9 | }); 10 | 11 | { 12 | const results = []; 13 | db.each('SELECT * FROM foo', (err, row) => { 14 | if (err) throw err; 15 | results.push(row); 16 | }, () => { 17 | console.timeEnd('db.each'); 18 | console.time('db.all'); 19 | }); 20 | } 21 | 22 | db.all('SELECT * FROM foo', (err, rows) => { 23 | console.timeEnd('db.all'); 24 | if (err) throw err; 25 | }); 26 | 27 | db.close(); 28 | }); 29 | --------------------------------------------------------------------------------