The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .dockerignore
├── .eslintrc.js
├── .github
    ├── CONTRIBUTING.md
    ├── ISSUE_TEMPLATE
    │   ├── bug_report.yml
    │   ├── config.yml
    │   ├── feature_request.yml
    │   └── install_problem.yml
    └── workflows
    │   └── ci.yml
├── .gitignore
├── LICENSE
├── README.md
├── binding.gyp
├── deps
    ├── common-sqlite.gypi
    ├── extract.js
    ├── sqlite-autoconf-3450000.tar.gz
    └── sqlite3.gyp
├── lib
    ├── sqlite3-binding.js
    ├── sqlite3.d.ts
    ├── sqlite3.js
    └── trace.js
├── package.json
├── 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
    └── semver-check.js


/.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: CI
  2 | on:
  3 |   workflow_dispatch:
  4 |   pull_request:
  5 |   push:
  6 |     branches:
  7 |       - master
  8 |     tags:
  9 |       - '*'
 10 | env:
 11 |   FORCE_COLOR: 1
 12 | concurrency:
 13 |   group: ${{ github.head_ref || github.run_id }}
 14 |   cancel-in-progress: true
 15 | jobs:
 16 |   build:
 17 |     runs-on: ${{ matrix.os }}
 18 |     strategy:
 19 |       fail-fast: false
 20 |       matrix:
 21 |         os:
 22 |           - macos-latest
 23 |           - ubuntu-20.04
 24 |           - windows-latest
 25 |         host:
 26 |           - x64
 27 |         target:
 28 |           - x64
 29 |         node:
 30 |           - 18
 31 |         include:
 32 |           - os: windows-latest
 33 |             node: 18
 34 |             host: x86
 35 |             target: x86
 36 |           - os: macos-m1
 37 |             node: 18
 38 |             host: arm64
 39 |             target: arm64
 40 |     name: ${{ matrix.os }} (host=${{ matrix.host }}, target=${{ matrix.target }})
 41 |     steps:
 42 |       - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
 43 |       - uses: actions/setup-node@v4
 44 |         with:
 45 |           node-version: ${{ matrix.node }}
 46 |           architecture: ${{ matrix.host }}
 47 | 
 48 |       - name: Add yarn (self-hosted)
 49 |         if: matrix.os == 'macos-m1'
 50 |         run: npm install -g yarn
 51 | 
 52 |       - name: Add setuptools for Python 3.12 (temp)
 53 |         if: matrix.os != 'macos-m1'
 54 |         run: pip install setuptools
 55 | 
 56 |       - name: Add msbuild to PATH
 57 |         uses: microsoft/setup-msbuild@v1.3
 58 |         if: contains(matrix.os, 'windows')
 59 |         with:
 60 |           msbuild-architecture: ${{ matrix.target }}
 61 | 
 62 |       - name: Install dependencies
 63 |         run: yarn install --ignore-scripts
 64 | 
 65 |       - name: Check Node compatibility
 66 |         run: node tools/semver-check.js
 67 | 
 68 |       - name: Add env vars
 69 |         shell: bash
 70 |         run: |
 71 |           echo "V=1" >> $GITHUB_ENV
 72 | 
 73 |           if [ "${{ matrix.target }}" = "x86" ]; then
 74 |             echo "TARGET=ia32" >> $GITHUB_ENV
 75 |           else
 76 |             echo "TARGET=${{ matrix.target }}" >> $GITHUB_ENV
 77 |           fi
 78 | 
 79 |       - name: Add Linux env vars
 80 |         if: contains(matrix.os, 'ubuntu')
 81 |         run: |
 82 |           echo "CFLAGS=${CFLAGS:-} -include ../src/gcc-preinclude.h" >> $GITHUB_ENV
 83 |           echo "CXXFLAGS=${CXXFLAGS:-} -include ../src/gcc-preinclude.h" >> $GITHUB_ENV
 84 | 
 85 |       - name: Build binaries
 86 |         run: yarn prebuild -a ${{ env.TARGET }}
 87 | 
 88 |       - name: Print binary info
 89 |         if: contains(matrix.os, 'ubuntu')
 90 |         run: |
 91 |           ldd build/**/node_sqlite3.node
 92 |           echo "---"
 93 |           nm build/**/node_sqlite3.node | grep "GLIBC_" | c++filt || true
 94 |           echo "---"
 95 |           file build/**/node_sqlite3.node
 96 | 
 97 |       - name: Run tests
 98 |         run: yarn test
 99 | 
100 |       - name: Upload binaries to commit artifacts
101 |         uses: actions/upload-artifact@v4
102 |         if: matrix.node == 18
103 |         with:
104 |           name: prebuilt-binaries
105 |           path: prebuilds/*
106 |           retention-days: 7
107 | 
108 |       - name: Upload binaries to GitHub Release
109 |         run: yarn upload --upload-all ${{ github.token }}
110 |         if: matrix.node == 18 && startsWith(github.ref, 'refs/tags/')
111 | 
112 |   build-qemu:
113 |     runs-on: ubuntu-latest
114 |     if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/')
115 |     strategy:
116 |       fail-fast: false
117 |       matrix:
118 |         node:
119 |           - 18
120 |         target:
121 |           - linux/arm64
122 |         variant:
123 |           - bullseye
124 |           - alpine3.15
125 |         include:
126 |           # musl x64 builds
127 |           - target: linux/amd64
128 |             variant: alpine3.15
129 |             node: 18
130 |     name: ${{ matrix.variant }} (node=${{ matrix.node }}, target=${{ matrix.target }})
131 |     steps:
132 |       - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
133 | 
134 |       - name: Set up QEMU
135 |         uses: docker/setup-qemu-action@v3
136 | 
137 |       - name: Setup Docker Buildx
138 |         uses: docker/setup-buildx-action@v3
139 | 
140 |       - name: Build binaries and test
141 |         run: |
142 |           docker buildx build \
143 |             --file ./tools/BinaryBuilder.Dockerfile \
144 |             --load \
145 |             --tag sqlite-builder \
146 |             --platform ${{ matrix.target }} \
147 |             --no-cache \
148 |             --build-arg VARIANT=${{ matrix.variant }} \
149 |             --build-arg NODE_VERSION=${{ matrix.node }} \
150 |             .
151 |           CONTAINER_ID=$(docker create -it sqlite-builder)
152 |           docker cp $CONTAINER_ID:/usr/src/build/prebuilds/ ./prebuilds
153 | 
154 |       - name: Upload binaries to commit artifacts
155 |         uses: actions/upload-artifact@v4
156 |         if: matrix.node == 18
157 |         with:
158 |           name: prebuilt-binaries
159 |           path: prebuilds/*
160 |           retention-days: 7
161 | 
162 |       - name: Upload binaries to GitHub Release
163 |         run: yarn install --ignore-scripts && yarn upload --upload-all ${{ github.token }}
164 |         if: matrix.node == 18 && startsWith(github.ref, 'refs/tags/')
165 | 


--------------------------------------------------------------------------------
/.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 | prebuilds
33 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2013-2025 Mapbox & Ghost Foundation
 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.45.0, 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 [`prebuild-install`](https://github.com/prebuild/prebuild-install) 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 | * `darwin-arm64`
 41 | * `darwin-x64`
 42 | * `linux-arm64`
 43 | * `linux-x64`
 44 | * `linuxmusl-arm64`
 45 | * `linuxmusl-x64`
 46 | * `win32-ia32`
 47 | * `win32-x64`
 48 | 
 49 | Unfortunately, [prebuild](https://github.com/prebuild/prebuild/issues/174) 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).
 50 | 
 51 | Support for other platforms and architectures may be added in the future if CI supports building on them.
 52 | 
 53 | 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.
 54 | 
 55 | ### Other ways to install
 56 | 
 57 | It is also possible to make your own build of `sqlite3` from its source instead of its npm package ([See below.](#source-install)).
 58 | 
 59 | 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)
 60 | 
 61 | SQLite's [SQLCipher extension](https://github.com/sqlcipher/sqlcipher) is also supported. [(See below.)](#building-for-sqlcipher)
 62 | 
 63 | # API
 64 | 
 65 | See the [API documentation](https://github.com/TryGhost/node-sqlite3/wiki/API) in the wiki.
 66 | 
 67 | # Usage
 68 | 
 69 | **Note:** the module must be [installed](#installing) before use.
 70 | 
 71 | ``` js
 72 | const sqlite3 = require('sqlite3').verbose();
 73 | const db = new sqlite3.Database(':memory:');
 74 | 
 75 | db.serialize(() => {
 76 |     db.run("CREATE TABLE lorem (info TEXT)");
 77 | 
 78 |     const stmt = db.prepare("INSERT INTO lorem VALUES (?)");
 79 |     for (let i = 0; i < 10; i++) {
 80 |         stmt.run("Ipsum " + i);
 81 |     }
 82 |     stmt.finalize();
 83 | 
 84 |     db.each("SELECT rowid AS id, info FROM lorem", (err, row) => {
 85 |         console.log(row.id + ": " + row.info);
 86 |     });
 87 | });
 88 | 
 89 | db.close();
 90 | ```
 91 | 
 92 | ## Source install
 93 | 
 94 | To skip searching for pre-compiled binaries, and force a build from source, use
 95 | 
 96 | ```bash
 97 | npm install --build-from-source
 98 | ```
 99 | 
100 | 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.
101 | 
102 | If you wish to install against an external sqlite then you need to pass the `--sqlite` argument to `npm` wrapper:
103 | 
104 | ```bash
105 | npm install --build-from-source --sqlite=/usr/local
106 | ```
107 | 
108 | 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.
109 | 
110 | Note, if building against homebrew-installed sqlite on OS X you can do:
111 | 
112 | ```bash
113 | npm install --build-from-source --sqlite=/usr/local/opt/sqlite/
114 | ```
115 | 
116 | ## Custom file header (magic)
117 | 
118 | 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.
119 | 
120 | ```bash
121 | npm install --build-from-source --sqlite_magic="MyCustomMagic15"
122 | ```
123 | 
124 | Note that the magic *must* be exactly 15 characters long (16 bytes including null terminator).
125 | 
126 | ## Building for node-webkit
127 | 
128 | Because of ABI differences, `sqlite3` must be built in a custom to be used with [node-webkit](https://github.com/rogerwang/node-webkit).
129 | 
130 | To build `sqlite3` for node-webkit:
131 | 
132 | 1. Install [`nw-gyp`](https://github.com/rogerwang/nw-gyp) globally: `npm install nw-gyp -g` *(unless already installed)*
133 | 
134 | 2. Build the module with the custom flags of `--runtime`, `--target_arch`, and `--target`:
135 | 
136 | ```bash
137 | NODE_WEBKIT_VERSION="0.8.6" # see latest version at https://github.com/rogerwang/node-webkit#downloads
138 | npm install sqlite3 --build-from-source --runtime=node-webkit --target_arch=ia32 --target=$(NODE_WEBKIT_VERSION)
139 | ```
140 | 
141 | You can also run this command from within a `sqlite3` checkout:
142 | 
143 | ```bash
144 | npm install --build-from-source --runtime=node-webkit --target_arch=ia32 --target=$(NODE_WEBKIT_VERSION)
145 | ```
146 | 
147 | Remember the following:
148 | 
149 | * 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).
150 | 
151 | * After the `sqlite3` package is built for node-webkit it cannot run in the vanilla Node.js (and vice versa).
152 |    * For example, `npm test` of the node-webkit's package would fail.
153 | 
154 | 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.
155 | 
156 | ## Building for SQLCipher
157 | 
158 | 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.
159 | 
160 | To run against SQLCipher, you need to compile `sqlite3` from source by passing build options like:
161 | 
162 | ```bash
163 | npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=/usr/
164 | 
165 | node -e 'require("sqlite3")'
166 | ```
167 | 
168 | If your SQLCipher is installed in a custom location (if you compiled and installed it yourself), you'll need to set some environment variables:
169 | 
170 | ### On OS X with Homebrew
171 | 
172 | Set the location where `brew` installed it:
173 | 
174 | ```bash
175 | export LDFLAGS="-L`brew --prefix`/opt/sqlcipher/lib"
176 | export CPPFLAGS="-I`brew --prefix`/opt/sqlcipher/include/sqlcipher"
177 | npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=`brew --prefix`
178 | 
179 | node -e 'require("sqlite3")'
180 | ```
181 | 
182 | ### On most Linuxes (including Raspberry Pi)
183 | 
184 | Set the location where `make` installed it:
185 | 
186 | ```bash
187 | export LDFLAGS="-L/usr/local/lib"
188 | export CPPFLAGS="-I/usr/local/include -I/usr/local/include/sqlcipher"
189 | export CXXFLAGS="$CPPFLAGS"
190 | npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=/usr/local --verbose
191 | 
192 | node -e 'require("sqlite3")'
193 | ```
194 | 
195 | ### Custom builds and Electron
196 | 
197 | 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):
198 | 
199 | ```bash
200 | --runtime=electron --target=18.2.1 --dist-url=https://electronjs.org/headers
201 | ```
202 | 
203 | In the case of MacOS with Homebrew, the command should look like the following:
204 | 
205 | ```bash
206 | npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=`brew --prefix` --runtime=electron --target=18.2.1 --dist-url=https://electronjs.org/headers
207 | ```
208 | 
209 | # Testing
210 | 
211 | ```bash
212 | npm test
213 | ```
214 | 
215 | # Contributors
216 | 
217 | * [Daniel Lockyer](https://github.com/daniellockyer)
218 | * [Konstantin Käfer](https://github.com/kkaefer)
219 | * [Dane Springmeyer](https://github.com/springmeyer)
220 | * [Will White](https://github.com/willwhite)
221 | * [Orlando Vazquez](https://github.com/orlandov)
222 | * [Artem Kustikov](https://github.com/artiz)
223 | * [Eric Fredricksen](https://github.com/grumdrig)
224 | * [John Wright](https://github.com/mrjjwright)
225 | * [Ryan Dahl](https://github.com/ry)
226 | * [Tom MacWright](https://github.com/tmcw)
227 | * [Carter Thaxton](https://github.com/carter-thaxton)
228 | * [Audrius Kažukauskas](https://github.com/audriusk)
229 | * [Johannes Schauer](https://github.com/pyneo)
230 | * [Mithgol](https://github.com/Mithgol)
231 | * [Kewde](https://github.com/kewde)
232 | 
233 | # Acknowledgments
234 | 
235 | Thanks to [Orlando Vazquez](https://github.com/orlandov),
236 | [Eric Fredricksen](https://github.com/grumdrig) and
237 | [Ryan Dahl](https://github.com/ry) for their SQLite bindings for node, and to mraleph on Freenode's #v8 for answering questions.
238 | 
239 | This module was originally created by [Mapbox](https://mapbox.com/) & is now maintained by [Ghost](https://ghost.org).
240 | 
241 | # Changelog
242 | 
243 | 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.
244 | 
245 | # Copyright & license
246 | 
247 | Copyright (c) 2013-2025 Mapbox & Ghost Foundation
248 | 
249 | `node-sqlite3` is [BSD licensed](https://github.com/tryghost/node-sqlite3/raw/master/LICENSE).
250 | 
251 | [![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)
252 | 


--------------------------------------------------------------------------------
/binding.gyp:
--------------------------------------------------------------------------------
 1 | {
 2 |   "includes": [ "deps/common-sqlite.gypi" ],
 3 |   "variables": {
 4 |       "sqlite%":"internal",
 5 |       "sqlite_libname%":"sqlite3",
 6 |       "module_name": "node_sqlite3",
 7 |   },
 8 |   "targets": [
 9 |     {
10 |       "target_name": "<(module_name)",
11 |       "cflags!": [ "-fno-exceptions" ],
12 |       "cflags_cc!": [ "-fno-exceptions" ],
13 |       "xcode_settings": { "GCC_ENABLE_CPP_EXCEPTIONS": "YES",
14 |         "CLANG_CXX_LIBRARY": "libc++",
15 |         "MACOSX_DEPLOYMENT_TARGET": "10.7",
16 |       },
17 |       "msvs_settings": {
18 |         "VCCLCompilerTool": { "ExceptionHandling": 1 },
19 |       },
20 |       "include_dirs": [
21 |         "<!@(node -p \"require('node-addon-api').include\")"],
22 |       "conditions": [
23 |         ["sqlite != 'internal'", {
24 |             "include_dirs": [
25 |               "<!@(node -p \"require('node-addon-api').include\")", "<(sqlite)/include" ],
26 |             "libraries": [
27 |                "-l<(sqlite_libname)"
28 |             ],
29 |             "conditions": [
30 |               [ "OS=='linux'", {"libraries+":["-Wl,-rpath=<@(sqlite)/lib"]} ],
31 |               [ "OS!='win'", {"libraries+":["-L<@(sqlite)/lib"]} ]
32 |             ],
33 |             'msvs_settings': {
34 |               'VCLinkerTool': {
35 |                 'AdditionalLibraryDirectories': [
36 |                   '<(sqlite)/lib'
37 |                 ],
38 |               },
39 |             }
40 |         },
41 |         {
42 |             "dependencies": [
43 |               "<!(node -p \"require('node-addon-api').gyp\")",
44 |               "deps/sqlite3.gyp:sqlite3"
45 |             ]
46 |         }
47 |         ]
48 |       ],
49 |       "sources": [
50 |         "src/backup.cc",
51 |         "src/database.cc",
52 |         "src/node_sqlite3.cc",
53 |         "src/statement.cc"
54 |       ],
55 |       "defines": [ "NAPI_VERSION=<(napi_build_version)", "NAPI_DISABLE_CPP_EXCEPTIONS=1" ]
56 |     }
57 |   ]
58 | }
59 | 


--------------------------------------------------------------------------------
/deps/common-sqlite.gypi:
--------------------------------------------------------------------------------
 1 | {
 2 |   'variables': {
 3 |       'sqlite_version%':'3450000',
 4 |       "toolset%":'',
 5 |   },
 6 |   'target_defaults': {
 7 |     'default_configuration': 'Release',
 8 |     'conditions': [
 9 |       [ 'toolset!=""', {
10 |         'msbuild_toolset':'<(toolset)'
11 |       }]
12 |     ],
13 |     'configurations': {
14 |       'Debug': {
15 |         'defines!': [
16 |           'NDEBUG'
17 |         ],
18 |         'cflags_cc!': [
19 |           '-O3',
20 |           '-Os',
21 |           '-DNDEBUG'
22 |         ],
23 |         'xcode_settings': {
24 |           'OTHER_CPLUSPLUSFLAGS!': [
25 |             '-O3',
26 |             '-Os',
27 |             '-DDEBUG'
28 |           ],
29 |           'GCC_OPTIMIZATION_LEVEL': '0',
30 |           'GCC_GENERATE_DEBUGGING_SYMBOLS': 'YES'
31 |         },
32 |         'msvs_settings': {
33 |           'VCCLCompilerTool': {
34 |             'ExceptionHandling': 1, # /EHsc
35 |           }
36 |         }
37 |       },
38 |       'Release': {
39 |         'defines': [
40 |           'NDEBUG'
41 |         ],
42 |         'xcode_settings': {
43 |           'OTHER_CPLUSPLUSFLAGS!': [
44 |             '-Os',
45 |             '-O2'
46 |           ],
47 |           'GCC_OPTIMIZATION_LEVEL': '3',
48 |           'GCC_GENERATE_DEBUGGING_SYMBOLS': 'NO',
49 |           'DEAD_CODE_STRIPPING': 'YES',
50 |           'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES'
51 |         },
52 |         'msvs_settings': {
53 |           'VCCLCompilerTool': {
54 |             'ExceptionHandling': 1, # /EHsc
55 |           }
56 |         }
57 |       }
58 |     }
59 |   }
60 | }
61 | 


--------------------------------------------------------------------------------
/deps/extract.js:
--------------------------------------------------------------------------------
 1 | const tar = require("tar");
 2 | const path = require("path");
 3 | const tarball = path.resolve(process.argv[2]);
 4 | const dirname = path.resolve(process.argv[3]);
 5 | 
 6 | tar.extract({
 7 |     sync: true,
 8 |     file: tarball,
 9 |     cwd: dirname,
10 | });
11 | 


--------------------------------------------------------------------------------
/deps/sqlite-autoconf-3450000.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/node-sqlite3/528e15ae605bac7aab8de60dd7c46e9fdc1fffd0/deps/sqlite-autoconf-3450000.tar.gz


--------------------------------------------------------------------------------
/deps/sqlite3.gyp:
--------------------------------------------------------------------------------
  1 | {
  2 |   'includes': [ 'common-sqlite.gypi' ],
  3 | 
  4 |   'variables': {
  5 |     'sqlite_magic%': '',
  6 |   },
  7 | 
  8 |   'target_defaults': {
  9 |     'default_configuration': 'Release',
 10 |     'cflags':[
 11 |       '-std=c99'
 12 |     ],
 13 |     'configurations': {
 14 |       'Debug': {
 15 |         'defines': [ 'DEBUG', '_DEBUG' ],
 16 |         'msvs_settings': {
 17 |           'VCCLCompilerTool': {
 18 |             'RuntimeLibrary': 1, # static debug
 19 |           },
 20 |         },
 21 |       },
 22 |       'Release': {
 23 |         'defines': [ 'NDEBUG' ],
 24 |         'msvs_settings': {
 25 |           'VCCLCompilerTool': {
 26 |             'RuntimeLibrary': 0, # static release
 27 |           },
 28 |         },
 29 |       }
 30 |     },
 31 |     'msvs_settings': {
 32 |       'VCCLCompilerTool': {
 33 |       },
 34 |       'VCLibrarianTool': {
 35 |       },
 36 |       'VCLinkerTool': {
 37 |         'GenerateDebugInformation': 'true',
 38 |       },
 39 |     },
 40 |     'conditions': [
 41 |       ['OS == "win"', {
 42 |         'defines': [
 43 |           'WIN32'
 44 |         ],
 45 |       }]
 46 |     ],
 47 |   },
 48 | 
 49 |   'targets': [
 50 |     {
 51 |       'target_name': 'action_before_build',
 52 |       'type': 'none',
 53 |       'hard_dependency': 1,
 54 |       'actions': [
 55 |         {
 56 |           'action_name': 'unpack_sqlite_dep',
 57 |           'inputs': [
 58 |             './sqlite-autoconf-<@(sqlite_version).tar.gz'
 59 |           ],
 60 |           'outputs': [
 61 |             '<(SHARED_INTERMEDIATE_DIR)/sqlite-autoconf-<@(sqlite_version)/sqlite3.c'
 62 |           ],
 63 |           'action': ['node','./extract.js','./sqlite-autoconf-<@(sqlite_version).tar.gz','<(SHARED_INTERMEDIATE_DIR)']
 64 |         }
 65 |       ],
 66 |       'direct_dependent_settings': {
 67 |         'include_dirs': [
 68 |           '<(SHARED_INTERMEDIATE_DIR)/sqlite-autoconf-<@(sqlite_version)/',
 69 |         ]
 70 |       },
 71 |     },
 72 |     {
 73 |       'target_name': 'sqlite3',
 74 |       'type': 'static_library',
 75 |       'include_dirs': [ '<(SHARED_INTERMEDIATE_DIR)/sqlite-autoconf-<@(sqlite_version)/' ],
 76 |       'dependencies': [
 77 |         'action_before_build'
 78 |       ],
 79 |       'sources': [
 80 |         '<(SHARED_INTERMEDIATE_DIR)/sqlite-autoconf-<@(sqlite_version)/sqlite3.c'
 81 |       ],
 82 |       'direct_dependent_settings': {
 83 |         'include_dirs': [ '<(SHARED_INTERMEDIATE_DIR)/sqlite-autoconf-<@(sqlite_version)/' ],
 84 |         'defines': [
 85 |           'SQLITE_THREADSAFE=1',
 86 |           'HAVE_USLEEP=1',
 87 |           'SQLITE_ENABLE_FTS3',
 88 |           'SQLITE_ENABLE_FTS4',
 89 |           'SQLITE_ENABLE_FTS5',
 90 |           'SQLITE_ENABLE_RTREE',
 91 |           'SQLITE_ENABLE_DBSTAT_VTAB=1',
 92 |           'SQLITE_ENABLE_MATH_FUNCTIONS'
 93 |         ],
 94 |       },
 95 |       'cflags_cc': [
 96 |           '-Wno-unused-value'
 97 |       ],
 98 |       'defines': [
 99 |         '_REENTRANT=1',
100 |         'SQLITE_THREADSAFE=1',
101 |         'HAVE_USLEEP=1',
102 |         'SQLITE_ENABLE_FTS3',
103 |         'SQLITE_ENABLE_FTS4',
104 |         'SQLITE_ENABLE_FTS5',
105 |         'SQLITE_ENABLE_RTREE',
106 |         'SQLITE_ENABLE_DBSTAT_VTAB=1',
107 |         'SQLITE_ENABLE_MATH_FUNCTIONS'
108 |       ],
109 |       'export_dependent_settings': [
110 |         'action_before_build',
111 |       ],
112 |       'conditions': [
113 |         ["sqlite_magic != ''", {
114 |             'defines': [
115 |               'SQLITE_FILE_HEADER="<(sqlite_magic)"'
116 |             ]
117 |         }]
118 |       ],
119 |     }
120 |   ]
121 | }
122 | 


--------------------------------------------------------------------------------
/lib/sqlite3-binding.js:
--------------------------------------------------------------------------------
1 | module.exports = require('bindings')('node_sqlite3.node');
2 | 


--------------------------------------------------------------------------------
/lib/sqlite3.d.ts:
--------------------------------------------------------------------------------
  1 | // Type definitions for sqlite3
  2 | // Project: http://github.com/tryghost/node-sqlite3
  3 | 
  4 | /// <reference types="node" />
  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<T>(callback?: (err: Error | null, row?: T) => void): this;
 84 |     get<T>(params: any, callback?: (this: RunResult, err: Error | null, row?: T) => void): this;
 85 |     get(...params: any[]): this;
 86 | 
 87 |     all<T>(callback?: (err: Error | null, rows: T[]) => void): this;
 88 |     all<T>(params: any, callback?: (this: RunResult, err: Error | null, rows: T[]) => void): this;
 89 |     all(...params: any[]): this;
 90 | 
 91 |     each<T>(callback?: (err: Error | null, row: T) => void, complete?: (err: Error | null, count: number) => void): this;
 92 |     each<T>(params: any, callback?: (this: RunResult, err: Error | null, row: T) => 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<T>(sql: string, callback?: (this: Statement, err: Error | null, row: T) => void): this;
107 |     get<T>(sql: string, params: any, callback?: (this: Statement, err: Error | null, row: T) => void): this;
108 |     get(sql: string, ...params: any[]): this;
109 | 
110 |     all<T>(sql: string, callback?: (this: Statement, err: Error | null, rows: T[]) => void): this;
111 |     all<T>(sql: string, params: any, callback?: (this: Statement, err: Error | null, rows: T[]) => void): this;
112 |     all(sql: string, ...params: any[]): this;
113 | 
114 |     each<T>(sql: string, callback?: (this: Statement, err: Error | null, row: T) => void, complete?: (err: Error | null, count: number) => void): this;
115 |     each<T>(sql: string, params: any, callback?: (this: Statement, err: Error | null, row: T) => 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": "sqlite3",
 3 |   "description": "Asynchronous, non-blocking SQLite3 bindings",
 4 |   "version": "5.1.7",
 5 |   "homepage": "https://github.com/TryGhost/node-sqlite3",
 6 |   "author": {
 7 |     "name": "Mapbox",
 8 |     "url": "https://mapbox.com/"
 9 |   },
10 |   "binary": {
11 |     "napi_versions": [
12 |       3,
13 |       6
14 |     ]
15 |   },
16 |   "contributors": [
17 |     "Daniel Lockyer <hi@daniellockyer.com>",
18 |     "Konstantin Käfer <mail@kkaefer.com>",
19 |     "Dane Springmeyer <dane@mapbox.com>",
20 |     "Will White <will@mapbox.com>",
21 |     "Orlando Vazquez <ovazquez@gmail.com>",
22 |     "Artem Kustikov <kustikoff@gmail.com>",
23 |     "Eric Fredricksen <efredricksen@gmail.com>",
24 |     "John Wright <mrjjwright@gmail.com>",
25 |     "Ryan Dahl <ry@tinyclouds.org>",
26 |     "Tom MacWright <tom@mapbox.com>",
27 |     "Carter Thaxton <carter.thaxton@gmail.com>",
28 |     "Audrius Kažukauskas <audrius@neutrino.lt>",
29 |     "Johannes Schauer <josch@pyneo.org>",
30 |     "Nathan Rajlich <nathan@tootallnate.net>",
31 |     "AJ ONeal <coolaj86@gmail.com>",
32 |     "Mithgol",
33 |     "Ben Noordhuis <ben@strongloop.com>"
34 |   ],
35 |   "files": [
36 |     "binding.gyp",
37 |     "deps/",
38 |     "lib/*.js",
39 |     "lib/*.d.ts",
40 |     "src/"
41 |   ],
42 |   "repository": {
43 |     "type": "git",
44 |     "url": "https://github.com/TryGhost/node-sqlite3.git"
45 |   },
46 |   "dependencies": {
47 |     "bindings": "^1.5.0",
48 |     "node-addon-api": "^7.0.0",
49 |     "prebuild-install": "^7.1.1",
50 |     "tar": "^6.1.11"
51 |   },
52 |   "devDependencies": {
53 |     "eslint": "8.56.0",
54 |     "mocha": "10.2.0",
55 |     "prebuild": "12.1.0"
56 |   },
57 |   "peerDependencies": {
58 |     "node-gyp": "8.x"
59 |   },
60 |   "peerDependenciesMeta": {
61 |     "node-gyp": {
62 |       "optional": true
63 |     }
64 |   },
65 |   "optionalDependencies": {
66 |     "node-gyp": "8.x"
67 |   },
68 |   "scripts": {
69 |     "install": "prebuild-install -r napi || node-gyp rebuild",
70 |     "prebuild": "prebuild --runtime napi --all --verbose",
71 |     "rebuild": "node-gyp rebuild",
72 |     "upload": "prebuild --verbose --prerelease",
73 |     "test": "node test/support/createdb.js && mocha -R spec --timeout 480000"
74 |   },
75 |   "license": "BSD-3-Clause",
76 |   "keywords": [
77 |     "sql",
78 |     "sqlite",
79 |     "sqlite3",
80 |     "database"
81 |   ],
82 |   "main": "./lib/sqlite3",
83 |   "types": "./lib/sqlite3.d.ts",
84 |   "renovate": {
85 |     "extends": [
86 |       "@tryghost:base"
87 |     ]
88 |   }
89 | }
90 | 


--------------------------------------------------------------------------------
/src/async.h:
--------------------------------------------------------------------------------
 1 | #ifndef NODE_SQLITE3_SRC_ASYNC_H
 2 | #define NODE_SQLITE3_SRC_ASYNC_H
 3 | 
 4 | #include <napi.h>
 5 | #include <uv.h>
 6 | 
 7 | #include "threading.h"
 8 | 
 9 | // Generic uv_async handler.
10 | template <class Item, class Parent> 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<Item*> 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<uv_async_cb>(listener));
29 |     }
30 | 
31 |     static void listener(uv_async_t* handle) {
32 |         auto* async = static_cast<Async*>(handle->data);
33 |         std::vector<Item*> rows;
34 |         NODE_SQLITE3_MUTEX_LOCK(&async->mutex)
35 |         rows.swap(async->data);
36 |         NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex)
37 |         for(auto row : rows)
38 |             async->callback(async->parent, row);
39 |     }
40 | 
41 |     static void close(uv_handle_t* handle) {
42 |         assert(handle != NULL);
43 |         assert(handle->data != NULL);
44 |         auto* async = static_cast<Async*>(handle->data);
45 |         delete async;
46 |     }
47 | 
48 |     void finish() {
49 |         // Need to call the listener again to ensure all items have been
50 |         // processed. Is this a bug in uv_async? Feels like uv_close
51 |         // should handle that.
52 |         listener(&watcher);
53 |         uv_close((uv_handle_t*)&watcher, close);
54 |     }
55 | 
56 |     void add(Item* item) {
57 |         NODE_SQLITE3_MUTEX_LOCK(&mutex);
58 |         data.emplace_back(item);
59 |         NODE_SQLITE3_MUTEX_UNLOCK(&mutex)
60 |     }
61 | 
62 |     void send() {
63 |         uv_async_send(&watcher);
64 |     }
65 | 
66 |     void send(Item* item) {
67 |         add(item);
68 |         send();
69 |     }
70 | 
71 |     ~Async() {
72 |         NODE_SQLITE3_MUTEX_DESTROY
73 |     }
74 | };
75 | 
76 | #endif
77 | 


--------------------------------------------------------------------------------
/src/backup.cc:
--------------------------------------------------------------------------------
  1 | #include <cstring>
  2 | #include <napi.h>
  3 | #include "macros.h"
  4 | #include "database.h"
  5 | #include "backup.h"
  6 | 
  7 | using namespace node_sqlite3;
  8 | 
  9 | Napi::Object Backup::Init(Napi::Env env, Napi::Object exports) {
 10 |     Napi::HandleScope scope(env);
 11 | 
 12 |     // declare napi_default_method here as it is only available in Node v14.12.0+
 13 |     auto napi_default_method = static_cast<napi_property_attributes>(napi_writable | napi_configurable);
 14 | 
 15 |     auto t = DefineClass(env, "Backup", {
 16 |         InstanceMethod("step", &Backup::Step, napi_default_method),
 17 |         InstanceMethod("finish", &Backup::Finish, napi_default_method),
 18 |         InstanceAccessor("idle", &Backup::IdleGetter, nullptr),
 19 |         InstanceAccessor("completed", &Backup::CompletedGetter, nullptr),
 20 |         InstanceAccessor("failed", &Backup::FailedGetter, nullptr),
 21 |         InstanceAccessor("remaining", &Backup::RemainingGetter, nullptr),
 22 |         InstanceAccessor("pageCount", &Backup::PageCountGetter, nullptr),
 23 |         InstanceAccessor("retryErrors", &Backup::RetryErrorGetter, &Backup::RetryErrorSetter),
 24 |     });
 25 | 
 26 |     exports.Set("Backup", t);
 27 |     return exports;
 28 | }
 29 | 
 30 | void Backup::Process() {
 31 |     if (finished && !queue.empty()) {
 32 |         return CleanQueue();
 33 |     }
 34 | 
 35 |     while (inited && !locked && !queue.empty()) {
 36 |         auto call = std::move(queue.front());
 37 |         queue.pop();
 38 | 
 39 |         call->callback(call->baton);
 40 |     }
 41 | }
 42 | 
 43 | void Backup::Schedule(Work_Callback callback, Baton* baton) {
 44 |     if (finished) {
 45 |         queue.emplace(new Call(callback, baton));
 46 |         CleanQueue();
 47 |     }
 48 |     else if (!inited || locked || !queue.empty()) {
 49 |         queue.emplace(new Call(callback, baton));
 50 |     }
 51 |     else {
 52 |         callback(baton);
 53 |     }
 54 | }
 55 | 
 56 | template <class T> void Backup::Error(T* baton) {
 57 |     auto env = baton->backup->Env();
 58 |     Napi::HandleScope scope(env);
 59 | 
 60 |     Backup* backup = baton->backup;
 61 |     // Fail hard on logic errors.
 62 |     assert(backup->status != 0);
 63 |     EXCEPTION(Napi::String::New(env, backup->message), backup->status, exception);
 64 | 
 65 |     Napi::Function cb = baton->callback.Value();
 66 | 
 67 |     if (!cb.IsEmpty() && cb.IsFunction()) {
 68 |         Napi::Value argv[] = { exception };
 69 |         TRY_CATCH_CALL(backup->Value(), cb, 1, argv);
 70 |     }
 71 |     else {
 72 |         Napi::Value argv[] = { Napi::String::New(env, "error"), exception };
 73 |         EMIT_EVENT(backup->Value(), 2, argv);
 74 |     }
 75 | }
 76 | 
 77 | void Backup::CleanQueue() {
 78 |     auto env = this->Env();
 79 |     Napi::HandleScope scope(env);
 80 | 
 81 |     if (inited && !queue.empty()) {
 82 |         // This backup has already been initialized and is now finished.
 83 |         // Fire error for all remaining items in the queue.
 84 |         EXCEPTION(Napi::String::New(env, "Backup is already finished"), SQLITE_MISUSE, exception);
 85 |         Napi::Value argv[] = { exception };
 86 |         bool called = false;
 87 | 
 88 |         // Clear out the queue so that this object can get GC'ed.
 89 |         while (!queue.empty()) {
 90 |             auto call = std::move(queue.front());
 91 |             queue.pop();
 92 | 
 93 |             std::unique_ptr<Baton> baton(call->baton);
 94 |             Napi::Function cb = baton->callback.Value();
 95 | 
 96 |             if (inited && !cb.IsEmpty() &&
 97 |                 cb.IsFunction()) {
 98 |                 TRY_CATCH_CALL(Value(), cb, 1, argv);
 99 |                 called = true;
100 |             }
101 |         }
102 | 
103 |         // When we couldn't call a callback function, emit an error on the
104 |         // Backup object.
105 |         if (!called) {
106 |             Napi::Value info[] = { Napi::String::New(env, "error"), exception };
107 |             EMIT_EVENT(Value(), 2, info);
108 |         }
109 |     }
110 |     else while (!queue.empty()) {
111 |         // Just delete all items in the queue; we already fired an event when
112 |         // initializing the backup failed.
113 |         auto call = std::move(queue.front());
114 |         queue.pop();
115 | 
116 |         // We don't call the actual callback, so we have to make sure that
117 |         // the baton gets destroyed.
118 |         delete call->baton;
119 |     }
120 | }
121 | 
122 | Backup::Backup(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Backup>(info) {
123 |     auto env = info.Env();
124 |     if (!info.IsConstructCall()) {
125 |         Napi::TypeError::New(env, "Use the new operator to create new Backup objects").ThrowAsJavaScriptException();
126 |         return;
127 |     }
128 | 
129 |     auto length = info.Length();
130 | 
131 |     if (length <= 0 || !Database::HasInstance(info[0])) {
132 |         Napi::TypeError::New(env, "Database object expected").ThrowAsJavaScriptException();
133 |         return;
134 |     }
135 |     else if (length <= 1 || !info[1].IsString()) {
136 |         Napi::TypeError::New(env, "Filename expected").ThrowAsJavaScriptException();
137 |         return;
138 |     }
139 |     else if (length <= 2 || !info[2].IsString()) {
140 |         Napi::TypeError::New(env, "Source database name expected").ThrowAsJavaScriptException();
141 |         return;
142 |     }
143 |     else if (length <= 3 || !info[3].IsString()) {
144 |         Napi::TypeError::New(env, "Destination database name expected").ThrowAsJavaScriptException();
145 |         return;
146 |     }
147 |     else if (length <= 4 || !info[4].IsBoolean()) {
148 |         Napi::TypeError::New(env, "Direction flag expected").ThrowAsJavaScriptException();
149 |         return;
150 |     }
151 |     else if (length > 5 && !info[5].IsUndefined() && !info[5].IsFunction()) {
152 |         Napi::TypeError::New(env, "Callback expected").ThrowAsJavaScriptException();
153 |         return;
154 |     }
155 | 
156 |     this->db = Napi::ObjectWrap<Database>::Unwrap(info[0].As<Napi::Object>());
157 |     this->db->Ref();
158 | 
159 |     auto filename = info[1].As<Napi::String>();
160 |     auto sourceName = info[2].As<Napi::String>();
161 |     auto destName = info[3].As<Napi::String>();
162 |     auto filenameIsDest = info[4].As<Napi::Boolean>();
163 | 
164 |     info.This().As<Napi::Object>().DefineProperty(Napi::PropertyDescriptor::Value("filename", filename));
165 |     info.This().As<Napi::Object>().DefineProperty(Napi::PropertyDescriptor::Value("sourceName", sourceName));
166 |     info.This().As<Napi::Object>().DefineProperty(Napi::PropertyDescriptor::Value("destName", destName));
167 |     info.This().As<Napi::Object>().DefineProperty(Napi::PropertyDescriptor::Value("filenameIsDest", filenameIsDest));
168 | 
169 |     auto* baton = new InitializeBaton(this->db, info[5].As<Napi::Function>(), this);
170 |     baton->filename = filename.Utf8Value();
171 |     baton->sourceName = sourceName.Utf8Value();
172 |     baton->destName = destName.Utf8Value();
173 |     baton->filenameIsDest = filenameIsDest.Value();
174 | 
175 |     this->db->Schedule(Work_BeginInitialize, baton);
176 | }
177 | 
178 | void Backup::Work_BeginInitialize(Database::Baton* baton) {
179 |     assert(baton->db->open);
180 |     baton->db->pending++;
181 |     auto env = baton->db->Env();
182 |     CREATE_WORK("sqlite3.Backup.Initialize", Work_Initialize, Work_AfterInitialize);
183 | }
184 | 
185 | void Backup::Work_Initialize(napi_env e, void* data) {
186 |     BACKUP_INIT(InitializeBaton);
187 | 
188 |     // In case stepping fails, we use a mutex to make sure we get the associated
189 |     // error message.
190 |     auto* mtx = sqlite3_db_mutex(baton->db->_handle);
191 |     sqlite3_mutex_enter(mtx);
192 | 
193 |     backup->status = sqlite3_open(baton->filename.c_str(), &backup->_otherDb);
194 | 
195 |     if (backup->status == SQLITE_OK) {
196 |         backup->_handle = sqlite3_backup_init(
197 |             baton->filenameIsDest ? backup->_otherDb : backup->db->_handle,
198 |             baton->destName.c_str(),
199 |             baton->filenameIsDest ? backup->db->_handle : backup->_otherDb,
200 |             baton->sourceName.c_str());
201 |     }
202 |     backup->_destDb = baton->filenameIsDest ? backup->_otherDb : backup->db->_handle;
203 | 
204 |     if (backup->status != SQLITE_OK) {
205 |         backup->message = std::string(sqlite3_errmsg(backup->_destDb));
206 |         sqlite3_close(backup->_otherDb);
207 |         backup->_otherDb = NULL;
208 |         backup->_destDb = NULL;
209 |     }
210 | 
211 |     sqlite3_mutex_leave(mtx);
212 | }
213 | 
214 | void Backup::Work_AfterInitialize(napi_env e, napi_status status, void* data) {
215 |     std::unique_ptr<InitializeBaton> baton(static_cast<InitializeBaton*>(data));
216 |     auto* backup = baton->backup;
217 | 
218 |     auto env = backup->Env();
219 |     Napi::HandleScope scope(env);
220 | 
221 |     if (backup->status != SQLITE_OK) {
222 |         Error(baton.get());
223 |         backup->FinishAll();
224 |     }
225 |     else {
226 |         backup->inited = true;
227 |         Napi::Function cb = baton->callback.Value();
228 |         if (!cb.IsEmpty() && cb.IsFunction()) {
229 |             Napi::Value argv[] = { env.Null() };
230 |             TRY_CATCH_CALL(backup->Value(), cb, 1, argv);
231 |         }
232 |     }
233 |     BACKUP_END();
234 | }
235 | 
236 | Napi::Value Backup::Step(const Napi::CallbackInfo& info) {
237 |     auto* backup = this;
238 |     auto env = backup->Env();
239 | 
240 |     REQUIRE_ARGUMENT_INTEGER(0, pages);
241 |     OPTIONAL_ARGUMENT_FUNCTION(1, callback);
242 | 
243 |     auto* baton = new StepBaton(backup, callback, pages);
244 |     backup->GetRetryErrors(baton->retryErrorsSet);
245 |     backup->Schedule(Work_BeginStep, baton);
246 |     return info.This();
247 | }
248 | 
249 | void Backup::Work_BeginStep(Baton* baton) {
250 |     BACKUP_BEGIN(Step);
251 | }
252 | 
253 | void Backup::Work_Step(napi_env e, void* data) {
254 |     BACKUP_INIT(StepBaton);
255 |     if (backup->_handle) {
256 |         backup->status = sqlite3_backup_step(backup->_handle, baton->pages);
257 |         backup->remaining = sqlite3_backup_remaining(backup->_handle);
258 |         backup->pageCount = sqlite3_backup_pagecount(backup->_handle);
259 |     }
260 |     if (backup->status != SQLITE_OK) {
261 |         // Text of message is a little awkward to get, since the error is not associated
262 |         // with a db connection.
263 | #if SQLITE_VERSION_NUMBER >= 3007015
264 |         // sqlite3_errstr is a relatively new method
265 |         backup->message = std::string(sqlite3_errstr(backup->status));
266 | #else
267 |         backup->message = "Sqlite error";
268 | #endif
269 |         if (baton->retryErrorsSet.size() > 0) {
270 |             if (baton->retryErrorsSet.find(backup->status) == baton->retryErrorsSet.end()) {
271 |                 backup->FinishSqlite();
272 |             }
273 |         }
274 |     }
275 | }
276 | 
277 | void Backup::Work_AfterStep(napi_env e, napi_status status, void* data) {
278 |     std::unique_ptr<StepBaton> baton(static_cast<StepBaton*>(data));
279 |     auto* backup = baton->backup;
280 | 
281 |     auto env = backup->Env();
282 |     Napi::HandleScope scope(env);
283 | 
284 |     if (backup->status == SQLITE_DONE) {
285 |         backup->completed = true;
286 |     } else if (!backup->_handle) {
287 |         backup->failed = true;
288 |     }
289 | 
290 |     if (backup->status != SQLITE_OK && backup->status != SQLITE_DONE) {
291 |         Error(baton.get());
292 |     }
293 |     else {
294 |         // Fire callbacks.
295 |         Napi::Function cb = baton->callback.Value();
296 |         if (!cb.IsEmpty() && cb.IsFunction()) {
297 |             Napi::Value argv[] = { env.Null(), Napi::Boolean::New(env, backup->status == SQLITE_DONE) };
298 |             TRY_CATCH_CALL(backup->Value(), cb, 2, argv);
299 |         }
300 |     }
301 | 
302 |     BACKUP_END();
303 | }
304 | 
305 | Napi::Value Backup::Finish(const Napi::CallbackInfo& info) {
306 |     auto* backup = this;
307 |     auto env = backup->Env();
308 | 
309 |     OPTIONAL_ARGUMENT_FUNCTION(0, callback);
310 | 
311 |     auto* baton = new Baton(backup, callback);
312 |     backup->Schedule(Work_BeginFinish, baton);
313 |     return info.This();
314 | }
315 | 
316 | void Backup::Work_BeginFinish(Baton* baton) {
317 |     BACKUP_BEGIN(Finish);
318 | }
319 | 
320 | void Backup::Work_Finish(napi_env e, void* data) {
321 |     BACKUP_INIT(Baton);
322 |     backup->FinishSqlite();
323 | }
324 | 
325 | void Backup::Work_AfterFinish(napi_env e, napi_status status, void* data) {
326 |     std::unique_ptr<Baton> baton(static_cast<Baton*>(data));
327 |     auto* backup = baton->backup;
328 | 
329 |     auto env = backup->Env();
330 |     Napi::HandleScope scope(env);
331 | 
332 |     backup->FinishAll();
333 | 
334 |     // Fire callback in case there was one.
335 |     Napi::Function cb = baton->callback.Value();
336 |     if (!cb.IsEmpty() && cb.IsFunction()) {
337 |         TRY_CATCH_CALL(backup->Value(), cb, 0, NULL);
338 |     }
339 | 
340 |     BACKUP_END();
341 | }
342 | 
343 | void Backup::FinishAll() {
344 |     assert(!finished);
345 |     if (!completed && !failed) {
346 |         failed = true;
347 |     }
348 |     finished = true;
349 |     CleanQueue();
350 |     FinishSqlite();
351 |     db->Unref();
352 | }
353 | 
354 | void Backup::FinishSqlite() {
355 |     if (_handle) {
356 |         sqlite3_backup_finish(_handle);
357 |         _handle = NULL;
358 |     }
359 |     if (_otherDb) {
360 |         sqlite3_close(_otherDb);
361 |         _otherDb = NULL;
362 |     }
363 |     _destDb = NULL;
364 | }
365 | 
366 | Napi::Value Backup::IdleGetter(const Napi::CallbackInfo& info) {
367 |     auto* backup = this;
368 |     bool idle = backup->inited && !backup->locked && backup->queue.empty();
369 |     return Napi::Boolean::New(this->Env(), idle);
370 | }
371 | 
372 | Napi::Value Backup::CompletedGetter(const Napi::CallbackInfo& info) {
373 |     auto* backup = this;
374 |     return Napi::Boolean::New(this->Env(), backup->completed);
375 | }
376 | 
377 | Napi::Value Backup::FailedGetter(const Napi::CallbackInfo& info) {
378 |     auto* backup = this;
379 |     return Napi::Boolean::New(this->Env(), backup->failed);
380 | }
381 | 
382 | Napi::Value Backup::RemainingGetter(const Napi::CallbackInfo& info) {
383 |     auto* backup = this;
384 |     return Napi::Number::New(this->Env(), backup->remaining);
385 | }
386 | 
387 | Napi::Value Backup::PageCountGetter(const Napi::CallbackInfo& info) {
388 |     auto* backup = this;
389 |     return Napi::Number::New(this->Env(), backup->pageCount);
390 | }
391 | 
392 | Napi::Value Backup::RetryErrorGetter(const Napi::CallbackInfo& info) {
393 |     auto* backup = this;
394 |     return backup->retryErrors.Value();
395 | }
396 | 
397 | void Backup::RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value) {
398 |     auto* backup = this;
399 |     auto env = backup->Env();
400 |     if (!value.IsArray()) {
401 |         Napi::Error::New(env, "retryErrors must be an array").ThrowAsJavaScriptException();
402 |         return;
403 |     }
404 |     Napi::Array array = value.As<Napi::Array>();
405 |     backup->retryErrors.Reset(array, 1);
406 | }
407 | 
408 | void Backup::GetRetryErrors(std::set<int>& retryErrorsSet) {
409 |     retryErrorsSet.clear();
410 |     Napi::Array array = retryErrors.Value();
411 |     auto length = array.Length();
412 |     for (size_t i = 0; i < length; i++) {
413 |         Napi::Value code = (array).Get(static_cast<uint32_t>(i));
414 |         if (code.IsNumber()) {
415 |             retryErrorsSet.insert(code.As<Napi::Number>().Int32Value());
416 |         }
417 |     }
418 | }
419 | 


--------------------------------------------------------------------------------
/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 <string>
  7 | #include <queue>
  8 | #include <set>
  9 | 
 10 | #include <sqlite3.h>
 11 | #include <napi.h>
 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<Backup> {
 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() override {
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<int> retryErrorsSet;
136 |         StepBaton(Backup* backup_, Napi::Function cb_, int pages_) :
137 |             Baton(backup_, cb_), pages(pages_) {}
138 |         virtual ~StepBaton() override = default;
139 |     };
140 | 
141 |     typedef void (*Work_Callback)(Baton* baton);
142 | 
143 |     struct Call {
144 |         Call(Work_Callback cb_, Baton* baton_) : callback(cb_), baton(baton_) {};
145 |         Work_Callback callback;
146 |         Baton* baton;
147 |     };
148 | 
149 |     Backup(const Napi::CallbackInfo& info);
150 | 
151 |     ~Backup() {
152 |         if (!finished) {
153 |             FinishAll();
154 |         }
155 |         retryErrors.Reset();
156 |     }
157 | 
158 |     WORK_DEFINITION(Step)
159 |     WORK_DEFINITION(Finish)
160 | 
161 |     Napi::Value IdleGetter(const Napi::CallbackInfo& info);
162 |     Napi::Value CompletedGetter(const Napi::CallbackInfo& info);
163 |     Napi::Value FailedGetter(const Napi::CallbackInfo& info);
164 |     Napi::Value PageCountGetter(const Napi::CallbackInfo& info);
165 |     Napi::Value RemainingGetter(const Napi::CallbackInfo& info);
166 |     Napi::Value FatalErrorGetter(const Napi::CallbackInfo& info);
167 |     Napi::Value RetryErrorGetter(const Napi::CallbackInfo& info);
168 | 
169 |     void FatalErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value);
170 |     void RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value);
171 | 
172 | protected:
173 |     static void Work_BeginInitialize(Database::Baton* baton);
174 |     static void Work_Initialize(napi_env env, void* data);
175 |     static void Work_AfterInitialize(napi_env env, napi_status status, void* data);
176 | 
177 |     void Schedule(Work_Callback callback, Baton* baton);
178 |     void Process();
179 |     void CleanQueue();
180 |     template <class T> static void Error(T* baton);
181 | 
182 |     void FinishAll();
183 |     void FinishSqlite();
184 |     void GetRetryErrors(std::set<int>& retryErrorsSet);
185 | 
186 |     Database* db;
187 | 
188 |     sqlite3_backup* _handle = NULL;
189 |     sqlite3* _otherDb = NULL;
190 |     sqlite3* _destDb = NULL;
191 | 
192 |     bool inited = false;
193 |     bool locked = true;
194 |     bool completed = false;
195 |     bool failed = false;
196 |     int remaining = -1;
197 |     int pageCount = -1;
198 |     bool finished = false;
199 | 
200 |     int status;
201 |     std::string message;
202 |     std::queue<std::unique_ptr<Call>> queue;
203 | 
204 |     Napi::Reference<Array> retryErrors;
205 | };
206 | 
207 | }
208 | 
209 | #endif
210 | 


--------------------------------------------------------------------------------
/src/database.h:
--------------------------------------------------------------------------------
  1 | 
  2 | #ifndef NODE_SQLITE3_SRC_DATABASE_H
  3 | #define NODE_SQLITE3_SRC_DATABASE_H
  4 | 
  5 | 
  6 | #include <assert.h>
  7 | #include <string>
  8 | #include <queue>
  9 | 
 10 | #include <sqlite3.h>
 11 | #include <napi.h>
 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<Database> {
 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 |         auto env = val.Env();
 31 |         Napi::HandleScope scope(env);
 32 |         if (!val.IsObject()) return false;
 33 |         auto obj = val.As<Napi::Object>();
 34 | #if NAPI_VERSION < 6
 35 |         return obj.InstanceOf(constructor.Value());
 36 | #else
 37 |         auto constructor =
 38 |             env.GetInstanceData<Napi::FunctionReference>();
 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 |         virtual ~OpenBaton() override = default;
 70 |     };
 71 | 
 72 |     struct ExecBaton : Baton {
 73 |         std::string sql;
 74 |         ExecBaton(Database* db_, Napi::Function cb_, const char* sql_) :
 75 |             Baton(db_, cb_), sql(sql_) {}
 76 |         virtual ~ExecBaton() override = default;
 77 |     };
 78 | 
 79 |     struct LoadExtensionBaton : Baton {
 80 |         std::string filename;
 81 |         LoadExtensionBaton(Database* db_, Napi::Function cb_, const char* filename_) :
 82 |             Baton(db_, cb_), filename(filename_) {}
 83 |         virtual ~LoadExtensionBaton() override = default;
 84 |     };
 85 | 
 86 |     struct LimitBaton : Baton {
 87 |         int id;
 88 |         int value;
 89 |         LimitBaton(Database* db_, Napi::Function cb_, int id_, int value_) :
 90 |             Baton(db_, cb_), id(id_), value(value_) {}
 91 |         virtual ~LimitBaton() override = default;
 92 |     };
 93 | 
 94 |     typedef void (*Work_Callback)(Baton* baton);
 95 | 
 96 |     struct Call {
 97 |         Call(Work_Callback cb_, Baton* baton_, bool exclusive_ = false) :
 98 |             callback(cb_), exclusive(exclusive_), baton(baton_) {};
 99 |         Work_Callback callback;
100 |         bool exclusive;
101 |         Baton* baton;
102 |     };
103 | 
104 |     struct ProfileInfo {
105 |         std::string sql;
106 |         sqlite3_int64 nsecs;
107 |     };
108 | 
109 |     struct UpdateInfo {
110 |         int type;
111 |         std::string database;
112 |         std::string table;
113 |         sqlite3_int64 rowid;
114 |     };
115 | 
116 |     bool IsOpen() { return open; }
117 |     bool IsLocked() { return locked; }
118 | 
119 |     typedef Async<std::string, Database> AsyncTrace;
120 |     typedef Async<ProfileInfo, Database> AsyncProfile;
121 |     typedef Async<UpdateInfo, Database> AsyncUpdate;
122 | 
123 |     friend class Statement;
124 |     friend class Backup;
125 | 
126 |     Database(const Napi::CallbackInfo& info);
127 | 
128 |     ~Database() {
129 |         RemoveCallbacks();
130 |         sqlite3_close(_handle);
131 |         _handle = NULL;
132 |         open = false;
133 |     }
134 | 
135 | protected:
136 |     WORK_DEFINITION(Open);
137 |     WORK_DEFINITION(Exec);
138 |     WORK_DEFINITION(Close);
139 |     WORK_DEFINITION(LoadExtension);
140 | 
141 |     void Schedule(Work_Callback callback, Baton* baton, bool exclusive = false);
142 |     void Process();
143 | 
144 |     Napi::Value Wait(const Napi::CallbackInfo& info);
145 |     static void Work_Wait(Baton* baton);
146 | 
147 |     Napi::Value Serialize(const Napi::CallbackInfo& info);
148 |     Napi::Value Parallelize(const Napi::CallbackInfo& info);
149 |     Napi::Value Configure(const Napi::CallbackInfo& info);
150 |     Napi::Value Interrupt(const Napi::CallbackInfo& info);
151 | 
152 |     static void SetBusyTimeout(Baton* baton);
153 |     static void SetLimit(Baton* baton);
154 | 
155 |     static void RegisterTraceCallback(Baton* baton);
156 |     static void TraceCallback(void* db, const char* sql);
157 |     static void TraceCallback(Database* db, std::string* sql);
158 | 
159 |     static void RegisterProfileCallback(Baton* baton);
160 |     static void ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs);
161 |     static void ProfileCallback(Database* db, ProfileInfo* info);
162 | 
163 |     static void RegisterUpdateCallback(Baton* baton);
164 |     static void UpdateCallback(void* db, int type, const char* database, const char* table, sqlite3_int64 rowid);
165 |     static void UpdateCallback(Database* db, UpdateInfo* info);
166 | 
167 |     void RemoveCallbacks();
168 | 
169 | protected:
170 |     sqlite3* _handle = NULL;
171 | 
172 |     bool open = false;
173 |     bool closing = false;
174 |     bool locked = false;
175 |     unsigned int pending = 0;
176 | 
177 |     bool serialize = false;
178 | 
179 |     std::queue<Call*> queue;
180 | 
181 |     AsyncTrace* debug_trace = NULL;
182 |     AsyncProfile* debug_profile = NULL;
183 |     AsyncUpdate* update_event = NULL;
184 | };
185 | 
186 | }
187 | 
188 | #endif
189 | 


--------------------------------------------------------------------------------
/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 <features.h>
 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 log2,log2@GLIBC_2.2.5");
16 | __asm__(".symver pow,pow@GLIBC_2.2.5");
17 | __asm__(".symver fcntl64,fcntl@GLIBC_2.2.5");
18 | #endif
19 | 
20 | #if defined(__aarch64__) || defined(_M_ARM64)
21 | __asm__(".symver memcpy,memcpy@GLIBC_2.17");
22 | __asm__(".symver exp,exp@GLIBC_2.17");
23 | __asm__(".symver log,log@GLIBC_2.17");
24 | __asm__(".symver log2,log2@GLIBC_2.17");
25 | __asm__(".symver pow,pow@GLIBC_2.17");
26 | __asm__(".symver fcntl64,fcntl@GLIBC_2.17");
27 | #endif
28 | 
29 | #endif
30 | #endif
31 | 


--------------------------------------------------------------------------------
/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 <vector>
  7 | 
  8 | // TODO: better way to work around StringConcat?
  9 | #include <napi.h>
 10 | inline Napi::String StringConcat(Napi::Value str1, Napi::Value str2) {
 11 |   return Napi::String::New(str1.Env(), str1.As<Napi::String>().Utf8Value() +
 12 |                     str2.As<Napi::String>().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 = static_cast<double>(source.Int32Value());
 19 |     if (orig_val == int_val) {
 20 |         return true;
 21 |     } else {
 22 |         return false;
 23 |     }
 24 | }
 25 | 
 26 | #define IS_FUNCTION(cb) \
 27 |     !cb.IsUndefined() && cb.IsFunction()
 28 | 
 29 | #define REQUIRE_ARGUMENTS(n)                                                   \
 30 |     if (info.Length() < (n)) {                                                 \
 31 |         Napi::TypeError::New(env, "Expected " #n "arguments").ThrowAsJavaScriptException(); \
 32 |         return env.Null(); \
 33 |     }
 34 | 
 35 | 
 36 | #define REQUIRE_ARGUMENT_EXTERNAL(i, var)                                      \
 37 |     if (info.Length() <= (i) || !info[i].IsExternal()) {                       \
 38 |         Napi::TypeError::New(env, "Argument " #i " invalid").ThrowAsJavaScriptException(); \
 39 |         return env.Null(); \
 40 |     }                                                                          \
 41 |     Napi::External var = info[i].As<Napi::External>();
 42 | 
 43 | 
 44 | #define REQUIRE_ARGUMENT_FUNCTION(i, var)                                      \
 45 |     if (info.Length() <= (i) || !info[i].IsFunction()) {                        \
 46 |         Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \
 47 |         return env.Null(); \
 48 |     }                                                                          \
 49 |     Napi::Function var = info[i].As<Napi::Function>();
 50 | 
 51 | 
 52 | #define REQUIRE_ARGUMENT_STRING(i, var)                                        \
 53 |     if (info.Length() <= (i) || !info[i].IsString()) {                         \
 54 |         Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); \
 55 |         return env.Null(); \
 56 |     }                                                                          \
 57 |     std::string var = info[i].As<Napi::String>();
 58 | 
 59 | #define REQUIRE_ARGUMENT_INTEGER(i, var)                                        \
 60 |     if (info.Length() <= (i) || !info[i].IsNumber()) {                        \
 61 |         Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \
 62 |         return env.Null();        \
 63 |     }                                                                          \
 64 |     int var(info[i].As<Napi::Number>().Int32Value());
 65 | 
 66 | #define OPTIONAL_ARGUMENT_FUNCTION(i, var)                                     \
 67 |     Napi::Function var;                                                        \
 68 |     if (info.Length() > i && !info[i].IsUndefined()) {                         \
 69 |         if (!info[i].IsFunction()) {                                           \
 70 |             Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \
 71 |             return env.Null(); \
 72 |         }                                                                      \
 73 |         var = info[i].As<Napi::Function>();                                    \
 74 |     }
 75 | 
 76 | 
 77 | #define OPTIONAL_ARGUMENT_INTEGER(i, var, default)                             \
 78 |     int var;                                                                   \
 79 |     if (info.Length() <= (i)) {                                                \
 80 |         var = (default);                                                       \
 81 |     }                                                                          \
 82 |     else if (info[i].IsNumber()) {                                             \
 83 |         if (OtherIsInt(info[i].As<Number>())) {                                \
 84 |             var = info[i].As<Napi::Number>().Int32Value();                     \
 85 |         }                                                                      \
 86 |     }                                                                          \
 87 |     else {                                                                     \
 88 |         Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \
 89 |         return env.Null(); \
 90 |     }
 91 | 
 92 | 
 93 | #define DEFINE_CONSTANT_INTEGER(target, constant, name)                        \
 94 |     Napi::PropertyDescriptor::Value(#name, Napi::Number::New(env, constant),   \
 95 |         static_cast<napi_property_attributes>(napi_enumerable | napi_configurable)),
 96 | 
 97 | #define DEFINE_CONSTANT_STRING(target, constant, name)                         \
 98 |     Napi::PropertyDescriptor::Value(#name, Napi::String::New(env, constant),   \
 99 |         static_cast<napi_property_attributes>(napi_enumerable | napi_configurable)),
100 | 
101 | #define EXCEPTION(msg, errno, name)                                            \
102 |     Napi::Value name = Napi::Error::New(env,                                   \
103 |         StringConcat(                                                          \
104 |             StringConcat(                                                      \
105 |                 Napi::String::New(env, sqlite_code_string(errno)),             \
106 |                 Napi::String::New(env, ": ")                                   \
107 |             ),                                                                 \
108 |             (msg)                                                              \
109 |         ).Utf8Value()                                                          \
110 |     ).Value();                                                                 \
111 |     Napi::Object name ##_obj = name.As<Napi::Object>();                        \
112 |     (name ##_obj).Set( Napi::String::New(env, "errno"), Napi::Number::New(env, errno)); \
113 |     (name ##_obj).Set( Napi::String::New(env, "code"),                         \
114 |         Napi::String::New(env, sqlite_code_string(errno)));
115 | 
116 | 
117 | #define EMIT_EVENT(obj, argc, argv)                                            \
118 |     TRY_CATCH_CALL((obj),                                                      \
119 |         (obj).Get("emit").As<Napi::Function>(),\
120 |         argc, argv                                                             \
121 |     );
122 | 
123 | // The Mac OS compiler complains when argv is NULL unless we
124 | // first assign it to a locally defined variable.
125 | #define TRY_CATCH_CALL(context, callback, argc, argv, ...)                     \
126 |     Napi::Value* passed_argv = argv;\
127 |     std::vector<napi_value> args;\
128 |     if ((argc != 0) && (passed_argv != NULL)) {\
129 |       args.assign(passed_argv, passed_argv + argc);\
130 |     }\
131 |     Napi::Value res = (callback).Call(Napi::Value(context), args);             \
132 |     if (res.IsEmpty()) return __VA_ARGS__;
133 | 
134 | #define WORK_DEFINITION(name)                                                  \
135 |     Napi::Value name(const Napi::CallbackInfo& info);                          \
136 |     static void Work_Begin##name(Baton* baton);                                \
137 |     static void Work_##name(napi_env env, void* data);                         \
138 |     static void Work_After##name(napi_env env, napi_status status, void* data);
139 | 
140 | #ifdef DEBUG
141 |     #define ASSERT_STATUS() assert(status == 0);
142 | #else
143 |     #define ASSERT_STATUS() (void)status;
144 | #endif
145 | 
146 | #define CREATE_WORK(name, workerFn, afterFn)                                    \
147 |     int status = napi_create_async_work(env, NULL, Napi::String::New(env, name),\
148 |                              workerFn, afterFn, baton, &baton->request);        \
149 |                                                                                 \
150 |     ASSERT_STATUS();                                                            \
151 |     napi_queue_async_work(env, baton->request);
152 | 
153 | #define STATEMENT_BEGIN(type)                                                  \
154 |     assert(baton);                                                             \
155 |     assert(baton->stmt);                                                       \
156 |     assert(!baton->stmt->locked);                                              \
157 |     assert(!baton->stmt->finalized);                                           \
158 |     assert(baton->stmt->prepared);                                             \
159 |     baton->stmt->locked = true;                                                \
160 |     baton->stmt->db->pending++;                                                \
161 |     auto env = baton->stmt->Env();                                             \
162 |     CREATE_WORK("sqlite3.Statement."#type, Work_##type, Work_After##type);
163 | 
164 | #define STATEMENT_INIT(type)                                                   \
165 |     type* baton = static_cast<type*>(data);                                    \
166 |     Statement* stmt = baton->stmt;
167 | 
168 | #define STATEMENT_MUTEX(name) \
169 |     if (!stmt->db->_handle) { \
170 |         stmt->status = SQLITE_MISUSE; \
171 |         stmt->message = "Database handle is closed"; \
172 |         return; \
173 |     } \
174 |     sqlite3_mutex* name = sqlite3_db_mutex(stmt->db->_handle);
175 | 
176 | #define STATEMENT_END()                                                        \
177 |     assert(stmt->locked);                                                      \
178 |     assert(stmt->db->pending);                                                 \
179 |     stmt->locked = false;                                                      \
180 |     stmt->db->pending--;                                                       \
181 |     stmt->Process();                                                           \
182 |     stmt->db->Process();
183 | 
184 | #define BACKUP_BEGIN(type)                                                     \
185 |     assert(baton);                                                             \
186 |     assert(baton->backup);                                                     \
187 |     assert(!baton->backup->locked);                                            \
188 |     assert(!baton->backup->finished);                                          \
189 |     assert(baton->backup->inited);                                             \
190 |     baton->backup->locked = true;                                              \
191 |     baton->backup->db->pending++;                                              \
192 |     auto env = baton->backup->Env();                                           \
193 |     CREATE_WORK("sqlite3.Backup."#type, Work_##type, Work_After##type);
194 | 
195 | #define BACKUP_INIT(type)                                                      \
196 |     type* baton = static_cast<type*>(data);                                    \
197 |     Backup* backup = baton->backup;
198 | 
199 | #define BACKUP_END()                                                           \
200 |     assert(backup->locked);                                                    \
201 |     assert(backup->db->pending);                                               \
202 |     backup->locked = false;                                                    \
203 |     backup->db->pending--;                                                     \
204 |     backup->Process();                                                         \
205 |     backup->db->Process();
206 | 
207 | #endif


--------------------------------------------------------------------------------
/src/node_sqlite3.cc:
--------------------------------------------------------------------------------
  1 | #include <stdint.h>
  2 | #include <sstream>
  3 | #include <cstring>
  4 | #include <string>
  5 | #include <sqlite3.h>
  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 |         case SQLITE_OK:         return "SQLITE_OK";
 87 |         case SQLITE_ERROR:      return "SQLITE_ERROR";
 88 |         case SQLITE_INTERNAL:   return "SQLITE_INTERNAL";
 89 |         case SQLITE_PERM:       return "SQLITE_PERM";
 90 |         case SQLITE_ABORT:      return "SQLITE_ABORT";
 91 |         case SQLITE_BUSY:       return "SQLITE_BUSY";
 92 |         case SQLITE_LOCKED:     return "SQLITE_LOCKED";
 93 |         case SQLITE_NOMEM:      return "SQLITE_NOMEM";
 94 |         case SQLITE_READONLY:   return "SQLITE_READONLY";
 95 |         case SQLITE_INTERRUPT:  return "SQLITE_INTERRUPT";
 96 |         case SQLITE_IOERR:      return "SQLITE_IOERR";
 97 |         case SQLITE_CORRUPT:    return "SQLITE_CORRUPT";
 98 |         case SQLITE_NOTFOUND:   return "SQLITE_NOTFOUND";
 99 |         case SQLITE_FULL:       return "SQLITE_FULL";
100 |         case SQLITE_CANTOPEN:   return "SQLITE_CANTOPEN";
101 |         case SQLITE_PROTOCOL:   return "SQLITE_PROTOCOL";
102 |         case SQLITE_EMPTY:      return "SQLITE_EMPTY";
103 |         case SQLITE_SCHEMA:     return "SQLITE_SCHEMA";
104 |         case SQLITE_TOOBIG:     return "SQLITE_TOOBIG";
105 |         case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT";
106 |         case SQLITE_MISMATCH:   return "SQLITE_MISMATCH";
107 |         case SQLITE_MISUSE:     return "SQLITE_MISUSE";
108 |         case SQLITE_NOLFS:      return "SQLITE_NOLFS";
109 |         case SQLITE_AUTH:       return "SQLITE_AUTH";
110 |         case SQLITE_FORMAT:     return "SQLITE_FORMAT";
111 |         case SQLITE_RANGE:      return "SQLITE_RANGE";
112 |         case SQLITE_NOTADB:     return "SQLITE_NOTADB";
113 |         case SQLITE_ROW:        return "SQLITE_ROW";
114 |         case SQLITE_DONE:       return "SQLITE_DONE";
115 |         default:                return "UNKNOWN";
116 |     }
117 | }
118 | 
119 | const char* sqlite_authorizer_string(int type) {
120 |     switch (type) {
121 |         case SQLITE_INSERT:     return "insert";
122 |         case SQLITE_UPDATE:     return "update";
123 |         case SQLITE_DELETE:     return "delete";
124 |         default:                return "";
125 |     }
126 | }
127 | 
128 | NODE_API_MODULE(node_sqlite3, RegisterModule)
129 | 


--------------------------------------------------------------------------------
/src/statement.h:
--------------------------------------------------------------------------------
  1 | #ifndef NODE_SQLITE3_SRC_STATEMENT_H
  2 | #define NODE_SQLITE3_SRC_STATEMENT_H
  3 | 
  4 | #include <cstdlib>
  5 | #include <cstring>
  6 | #include <string>
  7 | #include <queue>
  8 | #include <vector>
  9 | #include <sqlite3.h>
 10 | #include <napi.h>
 11 | #include <uv.h>
 12 | 
 13 | #include "database.h"
 14 | #include "threading.h"
 15 | 
 16 | using namespace Napi;
 17 | 
 18 | namespace node_sqlite3 {
 19 | 
 20 | namespace Values {
 21 |     struct Field {
 22 |         inline Field(unsigned short _index, unsigned short _type = SQLITE_NULL) :
 23 |             type(_type), index(_index) {}
 24 |         inline Field(const char* _name, unsigned short _type = SQLITE_NULL) :
 25 |             type(_type), index(0), name(_name) {}
 26 | 
 27 |         unsigned short type;
 28 |         unsigned short index;
 29 |         std::string name;
 30 | 
 31 |         virtual ~Field() = default;
 32 |     };
 33 | 
 34 |     struct Integer : Field {
 35 |         template <class T> inline Integer(T _name, int64_t val) :
 36 |             Field(_name, SQLITE_INTEGER), value(val) {}
 37 |         int64_t value;
 38 |         virtual ~Integer() override = default;
 39 |     };
 40 | 
 41 |     struct Float : Field {
 42 |         template <class T> inline Float(T _name, double val) :
 43 |             Field(_name, SQLITE_FLOAT), value(val) {}
 44 |         double value;
 45 |         virtual ~Float() override = default;
 46 |     };
 47 | 
 48 |     struct Text : Field {
 49 |         template <class T> inline Text(T _name, size_t len, const char* val) :
 50 |             Field(_name, SQLITE_TEXT), value(val, len) {}
 51 |         std::string value;
 52 |         virtual ~Text() override = default;
 53 |     };
 54 | 
 55 |     struct Blob : Field {
 56 |         template <class T> inline Blob(T _name, size_t len, const void* val) :
 57 |                 Field(_name, SQLITE_BLOB), length(len) {
 58 |             value = new char[len];
 59 |             assert(value != nullptr);
 60 |             memcpy(value, val, len);
 61 |         }
 62 |         inline virtual ~Blob() override {
 63 |             delete[] value;
 64 |         }
 65 |         int length;
 66 |         char* value;
 67 |     };
 68 | 
 69 |     typedef Field Null;
 70 | }
 71 | 
 72 | typedef std::vector<std::unique_ptr<Values::Field> > Row;
 73 | typedef std::vector<std::unique_ptr<Row> > Rows;
 74 | typedef Row Parameters;
 75 | 
 76 | 
 77 | 
 78 | class Statement : public Napi::ObjectWrap<Statement> {
 79 | public:
 80 |     static Napi::Object Init(Napi::Env env, Napi::Object exports);
 81 |     static Napi::Value New(const Napi::CallbackInfo& info);
 82 | 
 83 |     struct Baton {
 84 |         napi_async_work request = NULL;
 85 |         Statement* stmt;
 86 |         Napi::FunctionReference callback;
 87 |         Parameters parameters;
 88 | 
 89 |         Baton(Statement* stmt_, Napi::Function cb_) : stmt(stmt_) {
 90 |             stmt->Ref();
 91 |             callback.Reset(cb_, 1);
 92 |         }
 93 |         virtual ~Baton() {
 94 |             parameters.clear();
 95 |             if (request) napi_delete_async_work(stmt->Env(), request);
 96 |             stmt->Unref();
 97 |             callback.Reset();
 98 |         }
 99 |     };
100 | 
101 |     struct RowBaton : Baton {
102 |         RowBaton(Statement* stmt_, Napi::Function cb_) :
103 |             Baton(stmt_, cb_) {}
104 |         Row row;
105 |         virtual ~RowBaton() override = default;
106 |     };
107 | 
108 |     struct RunBaton : Baton {
109 |         RunBaton(Statement* stmt_, Napi::Function cb_) :
110 |             Baton(stmt_, cb_), inserted_id(0), changes(0) {}
111 |         sqlite3_int64 inserted_id;
112 |         int changes;
113 |         virtual ~RunBaton() override = default;
114 |     };
115 | 
116 |     struct RowsBaton : Baton {
117 |         RowsBaton(Statement* stmt_, Napi::Function cb_) :
118 |             Baton(stmt_, cb_) {}
119 |         Rows rows;
120 |         virtual ~RowsBaton() override = default;
121 |     };
122 | 
123 |     struct Async;
124 | 
125 |     struct EachBaton : Baton {
126 |         Napi::FunctionReference completed;
127 |         Async* async; // Isn't deleted when the baton is deleted.
128 | 
129 |         EachBaton(Statement* stmt_, Napi::Function cb_) :
130 |             Baton(stmt_, cb_) {}
131 |         virtual ~EachBaton() override {
132 |             completed.Reset();
133 |         }
134 |     };
135 | 
136 |     struct PrepareBaton : Database::Baton {
137 |         Statement* stmt;
138 |         std::string sql;
139 |         PrepareBaton(Database* db_, Napi::Function cb_, Statement* stmt_) :
140 |             Baton(db_, cb_), stmt(stmt_) {
141 |             stmt->Ref();
142 |         }
143 |         virtual ~PrepareBaton() override {
144 |             stmt->Unref();
145 |             if (!db->IsOpen() && db->IsLocked()) {
146 |                 // The database handle was closed before the statement could be
147 |                 // prepared.
148 |                 stmt->Finalize_();
149 |             }
150 |         }
151 |     };
152 | 
153 |     typedef void (*Work_Callback)(Baton* baton);
154 | 
155 |     struct Call {
156 |         Call(Work_Callback cb_, Baton* baton_) : callback(cb_), baton(baton_) {};
157 |         Work_Callback callback;
158 |         Baton* baton;
159 |     };
160 | 
161 |     struct Async {
162 |         uv_async_t watcher;
163 |         Statement* stmt;
164 |         Rows data;
165 |         NODE_SQLITE3_MUTEX_t;
166 |         bool completed;
167 |         int retrieved;
168 | 
169 |         // Store the callbacks here because we don't have
170 |         // access to the baton in the async callback.
171 |         Napi::FunctionReference item_cb;
172 |         Napi::FunctionReference completed_cb;
173 | 
174 |         Async(Statement* st, uv_async_cb async_cb) :
175 |                 stmt(st), completed(false), retrieved(0) {
176 |             watcher.data = this;
177 |             NODE_SQLITE3_MUTEX_INIT
178 |             stmt->Ref();
179 |             uv_loop_t *loop;
180 |             napi_get_uv_event_loop(stmt->Env(), &loop);
181 |             uv_async_init(loop, &watcher, async_cb);
182 |         }
183 | 
184 |         ~Async() {
185 |             stmt->Unref();
186 |             item_cb.Reset();
187 |             completed_cb.Reset();
188 |             NODE_SQLITE3_MUTEX_DESTROY
189 |         }
190 |     };
191 | 
192 |     Statement(const Napi::CallbackInfo& info);
193 | 
194 |     ~Statement() {
195 |         if (!finalized) Finalize_();
196 |     }
197 | 
198 |     WORK_DEFINITION(Bind)
199 |     WORK_DEFINITION(Get)
200 |     WORK_DEFINITION(Run)
201 |     WORK_DEFINITION(All)
202 |     WORK_DEFINITION(Each)
203 |     WORK_DEFINITION(Reset)
204 | 
205 |     Napi::Value Finalize_(const Napi::CallbackInfo& info);
206 | 
207 | protected:
208 |     static void Work_BeginPrepare(Database::Baton* baton);
209 |     static void Work_Prepare(napi_env env, void* data);
210 |     static void Work_AfterPrepare(napi_env env, napi_status status, void* data);
211 | 
212 |     static void AsyncEach(uv_async_t* handle);
213 |     static void CloseCallback(uv_handle_t* handle);
214 | 
215 |     static void Finalize_(Baton* baton);
216 |     void Finalize_();
217 | 
218 |     template <class T> inline std::unique_ptr<Values::Field> BindParameter(const Napi::Value source, T pos);
219 |     template <class T> T* Bind(const Napi::CallbackInfo& info, int start = 0, int end = -1);
220 |     bool Bind(const Parameters &parameters);
221 | 
222 |     static void GetRow(Row* row, sqlite3_stmt* stmt);
223 |     static Napi::Value RowToJS(Napi::Env env, Row* row);
224 |     void Schedule(Work_Callback callback, Baton* baton);
225 |     void Process();
226 |     void CleanQueue();
227 |     template <class T> static void Error(T* baton);
228 | 
229 | protected:
230 |     Database* db;
231 | 
232 |     sqlite3_stmt* _handle = NULL;
233 |     int status = SQLITE_OK;
234 |     bool prepared = false;
235 |     bool locked = true;
236 |     bool finalized = false;
237 | 
238 |     std::queue<Call*> queue;
239 |     std::string message;
240 | };
241 | 
242 | }
243 | 
244 | #endif
245 | 


--------------------------------------------------------------------------------
/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 | <!DOCTYPE html>
 2 | <html>
 3 |   <head>
 4 |     <title>Hello World!</title>
 5 |   </head>
 6 |   <body>
 7 |     <h1>Hello World!</h1>
 8 |     Using node-sqlite3:
 9 |     <script>
10 |     var sqlite = require('sqlite3');
11 |     document.write(sqlite.VERSION);
12 |     </script>.
13 |   </body>
14 | </html>


--------------------------------------------------------------------------------
/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 |     it('should ignore faulty toString in array', function(done) {
 99 |         const faulty = [[{toString: null}], 1];
100 |         db.all('SELECT * FROM txt_table WHERE txt = ? LIMIT ?', faulty, function (err) {
101 |             assert.equal(err, null);
102 |             done();
103 |         });
104 |     });
105 | 
106 |     it('should ignore faulty toString set to function', function(done) {
107 |         const faulty = [[{toString: function () {console.log('oh no');}}], 1];
108 |         db.all('SELECT * FROM txt_table WHERE txt = ? LIMIT ?', faulty, function (err) {
109 |             assert.equal(err, undefined);
110 |             done();
111 |         });
112 |     });
113 | 
114 | });
115 | 


--------------------------------------------------------------------------------
/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 statSync   = require('fs').statSync || require('path').statSync;
 6 |     var path = require('path');
 7 | 
 8 |     var sqlite3 = require('../../lib/sqlite3');
 9 | 
10 |     var count = 1000000;
11 |     var db_path = path.join(__dirname,'big.db');
12 | 
13 |     function randomString() {
14 |         var str = '';
15 |         var chars = 'abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY0123456789  ';
16 |         for (var i = Math.random() * 100; i > 0; i--) {
17 |             str += chars[Math.floor(Math.random() * chars.length)];
18 |         }
19 |         return str;
20 |     }
21 | 
22 | // Make sure the file exists and is also valid.
23 |     if (existsSync(db_path) && statSync(db_path).size !== 0) {
24 |         console.log('okay: database already created (' + db_path + ')');
25 |         if (callback) callback();
26 |     } else {
27 |         console.log("Creating test database... This may take several minutes.");
28 |         var db = new sqlite3.Database(db_path);
29 |         db.serialize(function() {
30 |             db.run("CREATE TABLE foo (id INT, txt TEXT)");
31 |             db.run("BEGIN TRANSACTION");
32 |             var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
33 |             for (var i = 0; i < count; i++) {
34 |                 stmt.run(i, randomString());
35 |             }
36 |             stmt.finalize();
37 |             db.run("COMMIT TRANSACTION", [], function () {
38 |                 db.close(callback);
39 |             });
40 |         });
41 |     }
42 | }
43 | 
44 | if (require.main === module) {
45 |     createdb();
46 | }
47 | 
48 | module.exports = createdb;
49 | 


--------------------------------------------------------------------------------
/test/support/elmo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/node-sqlite3/528e15ae605bac7aab8de60dd7c46e9fdc1fffd0/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 |     fs.statSync(name);
33 | };


--------------------------------------------------------------------------------
/test/support/prepare.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/node-sqlite3/528e15ae605bac7aab8de60dd7c46e9fdc1fffd0/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=18
 2 | ARG VARIANT=bullseye
 3 | 
 4 | FROM node:$NODE_VERSION-$VARIANT
 5 | 
 6 | ARG VARIANT
 7 | 
 8 | RUN if case $VARIANT in "alpine"*) true;; *) false;; esac; 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 | ENV CFLAGS="${CFLAGS:-} -include ../src/gcc-preinclude.h"
16 | ENV CXXFLAGS="${CXXFLAGS:-} -include ../src/gcc-preinclude.h"
17 | RUN npm run prebuild
18 | 
19 | RUN if case $VARIANT in "alpine"*) false;; *) true;; esac; then ldd build/**/node_sqlite3.node; nm build/**/node_sqlite3.node | grep \"GLIBC_\" | c++filt || true ; fi
20 | 
21 | RUN npm run test
22 | 
23 | CMD ["sh"]
24 | 


--------------------------------------------------------------------------------
/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.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 | 


--------------------------------------------------------------------------------
/tools/semver-check.js:
--------------------------------------------------------------------------------
 1 | const fs = require('fs');
 2 | const path = require('path');
 3 | const semver = require('semver');
 4 | 
 5 | const supportedVersions = '10.12.0';
 6 | 
 7 | function checkEngines(modulePath) {
 8 |     const packageJsonPath = path.join(modulePath, 'package.json');
 9 | 
10 |     if (!fs.existsSync(packageJsonPath)) return;
11 | 
12 |     const packageJson = JSON.parse(fs.readFileSync(packageJsonPath));
13 |     const engines = packageJson.engines;
14 | 
15 |     if (engines && engines.node) {
16 |         const minVersion = semver.minVersion(engines.node);
17 | 
18 |         if (semver.gt(minVersion, supportedVersions)) {
19 |             console.log(`${packageJson.name}@${packageJson.version} requires ${engines.node}`);
20 |             process.exit(1);
21 |         }
22 |     }
23 | }
24 | 
25 | const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')));
26 | 
27 | const allDependencies = Object.keys(packageJson.dependencies || {}).concat(Object.keys(packageJson.optionalDependencies || {}));
28 | 
29 | for (const dependency of allDependencies) {
30 |     const modulePath = path.join(__dirname, '..', 'node_modules', dependency);
31 |     checkEngines(modulePath);
32 | }
33 | 
34 | 


--------------------------------------------------------------------------------