diff --git a/docs/connection-options.md b/docs/connection-options.md index cd9915732f..d52a64bc81 100644 --- a/docs/connection-options.md +++ b/docs/connection-options.md @@ -5,6 +5,7 @@ * [`mysql` / `mariadb` connection options](#mysql--mariadb-connection-options) * [`postgres` / `cockroachdb` connection options](#postgres--cockroachdb-connection-options) * [`sqlite` connection options](#sqlite-connection-options) +* [`better-sqlite3` connection options](#better-sqlite3-connection-options) * [`cordova` connection options](#cordova-connection-options) * [`react-native` connection options](#react-native-connection-options) * [`nativescript` connection options](#nativescript-connection-options) @@ -22,7 +23,7 @@ Connection options is a connection configuration you pass to `createConnection` ## Common connection options * `type` - Database type. You must specify what database engine you use. - Possible values are "mysql", "postgres", "cockroachdb", "mariadb", "sqlite", "cordova", "nativescript", + Possible values are "mysql", "postgres", "cockroachdb", "mariadb", "sqlite", "better-sqlite3", "cordova", "nativescript", "oracle", "mssql", "mongodb", "sqljs", "react-native". This option is **required**. @@ -185,6 +186,14 @@ See [SSL options](https://github.com/mysqljs/mysql#ssl-options). * `database` - Database path. For example "./mydb.sql" +## `better-sqlite3` connection options + +* `database` - Database path. For example "./mydb.sql" + +* `statementCacheSize` - Cache size of sqlite statement to speed up queries (default 100). + +* `prepareDatabase` - Function to run before a database is used in typeorm. You can access original better-sqlite3 Database object here. + ## `cordova` connection options * `database` - Database name diff --git a/docs/zh_CN/connection-options.md b/docs/zh_CN/connection-options.md index 3966d6c8e8..5523bc6afb 100644 --- a/docs/zh_CN/connection-options.md +++ b/docs/zh_CN/connection-options.md @@ -5,6 +5,7 @@ - [`mysql`/`mariadb`](#mysql/mariadb) - [`postgres`/`cockroachdb`连接选项](#postgres/cockroachdb连接选项) - [`sqlite`](#sqlite) + - [`better-sqlite3`](#better-sqlite3) - [`cordova`](#cordova) - [`react-native`](#react-native) - [`nativescript`](#nativescript) @@ -20,7 +21,7 @@ ## 常用的连接选项 -- `type` - 数据库类型。你必须指定要使用的数据库引擎。该值可以是"mysql","postgres","mariadb","sqlite","cordova","nativescript","oracle","mssql","mongodb","sqljs","react-native"。此选项是**必需**的。 +- `type` - 数据库类型。你必须指定要使用的数据库引擎。该值可以是"mysql","postgres","mariadb","sqlite", "better-sqlite3","cordova","nativescript","oracle","mssql","mongodb","sqljs","react-native"。此选项是**必需**的。 - `name` - 连接名。 在使用 `getConnection(name: string)` 或 `ConnectionManager.get(name: string)`时候需要用到。不同连接的连接名称不能相同,它们都必须是唯一的。如果没有给出连接名称,那么它将被设置为"default"。 @@ -128,6 +129,14 @@ - `database` - 数据库路径。 例如 "./mydb.sql" +## `better-sqlite3` + +* `database` - 数据库路径。 例如 "./mydb.sql" + +* `statementCacheSize` - Sqlite 查询 Statement 缓存大小。默认100 + +* `prepareDatabase` - 在数据库投入使用前运行的函数。你可以在这里访问到better-sqlite3原始数据库对象。 + ## `cordova` - `database` - 数据库名 diff --git a/ormconfig.circleci-cockroach.json b/ormconfig.circleci-cockroach.json index b91014a781..96049b3fbd 100644 --- a/ormconfig.circleci-cockroach.json +++ b/ormconfig.circleci-cockroach.json @@ -25,6 +25,12 @@ "type": "sqlite", "database": "temp/sqlitedb.db" }, + { + "skip": true, + "name": "better-sqlite3", + "type": "better-sqlite3", + "database": "temp/better-sqlite3db.db" + }, { "skip": true, "name": "postgres", diff --git a/ormconfig.circleci-common.json b/ormconfig.circleci-common.json index 286289f0f6..16a85b9e79 100644 --- a/ormconfig.circleci-common.json +++ b/ormconfig.circleci-common.json @@ -25,6 +25,12 @@ "type": "sqlite", "database": "temp/sqlitedb.db" }, + { + "skip": false, + "name": "better-sqlite3", + "type": "better-sqlite3", + "database": "temp/better-sqlite3db.db" + }, { "skip": false, "name": "postgres", diff --git a/ormconfig.circleci-oracle.json b/ormconfig.circleci-oracle.json index 83bc38febe..aef977102f 100644 --- a/ormconfig.circleci-oracle.json +++ b/ormconfig.circleci-oracle.json @@ -25,6 +25,12 @@ "type": "sqlite", "database": "temp/sqlitedb.db" }, + { + "skip": true, + "name": "better-sqlite3", + "type": "better-sqlite3", + "database": "temp/better-sqlite3db.db" + }, { "skip": true, "name": "postgres", diff --git a/ormconfig.json.dist b/ormconfig.json.dist index 4f456b7efe..f855baf460 100644 --- a/ormconfig.json.dist +++ b/ormconfig.json.dist @@ -28,6 +28,13 @@ "database": "temp/sqlitedb.db", "logging": false }, + { + "skip": false, + "name": "better-sqlite3", + "type": "better-sqlite3", + "database": "temp/better-sqlite3db.db", + "logging": false + }, { "skip": false, "name": "postgres", diff --git a/ormconfig.travis.json b/ormconfig.travis.json index f6a76e4c9c..ca9054416b 100644 --- a/ormconfig.travis.json +++ b/ormconfig.travis.json @@ -25,6 +25,13 @@ "type": "sqlite", "database": "temp/sqlitedb.db" }, + { + "skip": false, + "name": "better-sqlite3", + "type": "better-sqlite3", + "database": "temp/better-sqlite3db.db", + "logging": false + }, { "skip": false, "name": "postgres", diff --git a/package-lock.json b/package-lock.json index 1cda64a919..cd9bfb8a68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -907,6 +907,17 @@ "tweetnacl": "^0.14.3" } }, + "better-sqlite3": { + "version": "7.0.1", + "resolved": "https://registry.npm.taobao.org/better-sqlite3/download/better-sqlite3-7.0.1.tgz", + "integrity": "sha1-fQhhMLQtfVydKEeJnFfuWe7Jy7M=", + "dev": true, + "requires": { + "bindings": "^1.5.0", + "prebuild-install": "^5.3.3", + "tar": "4.4.10" + } + }, "big-number": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/big-number/-/big-number-0.3.1.tgz", @@ -931,6 +942,15 @@ "integrity": "sha512-xVNN69YGDghOqCCtA6FI7avYrr02mTJjOgB0/f1VPD3pJC8QEvjTKWc4epDx8AqxxA75NI0QpVM2gPJXUbE4Tg==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npm.taobao.org/bindings/download/bindings-1.5.0.tgz", + "integrity": "sha1-EDU8npRTNLwFEabZCzj7x8nFBN8=", + "dev": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bl": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", @@ -1931,6 +1951,15 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npm.taobao.org/decompress-response/download/decompress-response-4.2.1.tgz?cache=0&sync_timestamp=1589512178920&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdecompress-response%2Fdownload%2Fdecompress-response-4.2.1.tgz", + "integrity": "sha1-QUAjzHowLaJc4uyC0NUjjMr9iYY=", + "dev": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -2394,6 +2423,12 @@ } } }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/expand-template/download/expand-template-2.0.3.tgz", + "integrity": "sha1-bhSz/O4POmNA7LV9LokYaSBSpHw=", + "dev": true + }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -2546,6 +2581,12 @@ "object-assign": "^4.1.0" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/file-uri-to-path/download/file-uri-to-path-1.0.0.tgz", + "integrity": "sha1-VTp7hEb/b2hDWcRF8eN6BdrMM90=", + "dev": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -2699,6 +2740,12 @@ "map-cache": "^0.2.2" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/fs-constants/download/fs-constants-1.0.0.tgz", + "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", + "dev": true + }, "fs-minipass": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", @@ -3599,6 +3646,12 @@ "ini": "^1.3.2" } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npm.taobao.org/github-from-package/download/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "dev": true + }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -5752,6 +5805,12 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/mimic-response/download/mimic-response-2.1.0.tgz", + "integrity": "sha1-0Tdj019hPQnsN+uzC6wEacDuj0M=", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -5829,6 +5888,12 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz", "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==" }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npm.taobao.org/mkdirp-classic/download/mkdirp-classic-0.5.3.tgz", + "integrity": "sha1-+hDJEVzG2IZb4iG6R+6b7XhgERM=", + "dev": true + }, "mocha": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", @@ -6247,6 +6312,12 @@ "to-regex": "^3.0.1" } }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/napi-build-utils/download/napi-build-utils-1.0.2.tgz", + "integrity": "sha1-sf3cCyxG44Cgt6dvmE3UfEGhOAY=", + "dev": true + }, "native-duplexpair": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", @@ -6313,6 +6384,15 @@ } } }, + "node-abi": { + "version": "2.18.0", + "resolved": "https://registry.npm.taobao.org/node-abi/download/node-abi-2.18.0.tgz", + "integrity": "sha1-H1SGz9fTi9T1OS+kSkrU2aDf+/Q=", + "dev": true, + "requires": { + "semver": "^5.4.1" + } + }, "node-environment-flags": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", @@ -6376,6 +6456,12 @@ } } }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/noop-logger/download/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", + "dev": true + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -7106,6 +7192,56 @@ "xtend": "^4.0.0" } }, + "prebuild-install": { + "version": "5.3.4", + "resolved": "https://registry.npm.taobao.org/prebuild-install/download/prebuild-install-5.3.4.tgz", + "integrity": "sha1-aYLRAIQmnTZMGFZVC30JDqMfopM=", + "dev": true, + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp": "^0.5.1", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npm.taobao.org/minimist/download/minimist-1.2.5.tgz", + "integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.5.tgz?cache=0&sync_timestamp=1588819864223&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-0.5.5.tgz", + "integrity": "sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/pump/download/pump-3.0.0.tgz", + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -7717,6 +7853,23 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/simple-concat/download/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", + "dev": true + }, + "simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/simple-get/download/simple-get-3.1.0.tgz", + "integrity": "sha1-tFvgYkNeUNFZVAtXYgLO7EC5xrM=", + "dev": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "simple-git": { "version": "1.107.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.107.0.tgz", @@ -8240,6 +8393,85 @@ } } }, + "tar-fs": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/tar-fs/download/tar-fs-2.1.0.tgz", + "integrity": "sha1-0c3RIatGXuDrnM3i01BJ0/Pa8NU=", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/pump/download/pump-3.0.0.tgz", + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "tar-stream": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/tar-stream/download/tar-stream-2.1.2.tgz", + "integrity": "sha1-bV7xp+V4OpX/cLabl0VaWWjcEyU=", + "dev": true, + "requires": { + "bl": "^4.0.1", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "bl": { + "version": "4.0.2", + "resolved": "https://registry.npm.taobao.org/bl/download/bl-4.0.2.tgz?cache=0&sync_timestamp=1584503592586&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbl%2Fdownload%2Fbl-4.0.2.tgz", + "integrity": "sha1-UrcekIhRXQYG2d2cx6pI3B+Y5zo=", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz", + "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=", + "dev": true + } + } + }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npm.taobao.org/buffer/download/buffer-5.6.0.tgz?cache=0&sync_timestamp=1588706716358&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-5.6.0.tgz", + "integrity": "sha1-oxdJ3H2B2E2wir+Te2uMQDP2J4Y=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "tedious": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/tedious/-/tedious-2.7.1.tgz", @@ -8845,6 +9077,12 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/which-pm-runs/download/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", diff --git a/package.json b/package.json index 0e6922ee8e..a52319a545 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@types/sinon": "^7.0.8", "@types/source-map-support": "^0.4.2", "@types/yargs": "^12.0.9", + "better-sqlite3": "^7.0.1", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "class-transformer": "^0.2.3", diff --git a/src/commands/InitCommand.ts b/src/commands/InitCommand.ts index b8e553ecb3..c3638ffe66 100644 --- a/src/commands/InitCommand.ts +++ b/src/commands/InitCommand.ts @@ -113,6 +113,12 @@ export class InitCommand implements yargs.CommandModule { "database": "database.sqlite", }); break; + case "better-sqlite3": + Object.assign(options, { + type: "better-sqlite3", + "database": "database.sqlite", + }); + break; case "postgres": Object.assign(options, { "type": "postgres", @@ -457,6 +463,7 @@ services: `; case "sqlite": + case "better-sqlite3": return `version: '3' services: `; @@ -546,6 +553,9 @@ Steps to run this project: case "sqlite": packageJson.dependencies["sqlite3"] = "^4.0.3"; break; + case "better-sqlite3": + packageJson.dependencies["better-sqlite3"] = "^7.0.0"; + break; case "oracle": packageJson.dependencies["oracledb"] = "^1.13.1"; break; diff --git a/src/connection/ConnectionOptions.ts b/src/connection/ConnectionOptions.ts index 1c37c7ce84..40fd608087 100644 --- a/src/connection/ConnectionOptions.ts +++ b/src/connection/ConnectionOptions.ts @@ -13,6 +13,7 @@ import {ExpoConnectionOptions} from "../driver/expo/ExpoConnectionOptions"; import {AuroraDataApiConnectionOptions} from "../driver/aurora-data-api/AuroraDataApiConnectionOptions"; import {SapConnectionOptions} from "../driver/sap/SapConnectionOptions"; import {AuroraDataApiPostgresConnectionOptions} from "../driver/aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions"; +import {BetterSqlite3ConnectionOptions} from "../driver/better-sqlite3/BetterSqlite3ConnectionOptions"; /** @@ -35,4 +36,5 @@ export type ConnectionOptions = MongoConnectionOptions| AuroraDataApiConnectionOptions| AuroraDataApiPostgresConnectionOptions| - ExpoConnectionOptions; + ExpoConnectionOptions| + BetterSqlite3ConnectionOptions; diff --git a/src/connection/ConnectionOptionsReader.ts b/src/connection/ConnectionOptionsReader.ts index 03f00f9957..5e9ee04510 100644 --- a/src/connection/ConnectionOptionsReader.ts +++ b/src/connection/ConnectionOptionsReader.ts @@ -171,7 +171,7 @@ export class ConnectionOptionsReader { } // make database path file in sqlite relative to package.json - if (options.type === "sqlite") { + if (options.type === "sqlite" || options.type === "better-sqlite3") { if (typeof options.database === "string" && options.database.substr(0, 1) !== "/" && // unix absolute options.database.substr(1, 2) !== ":\\" && // windows absolute diff --git a/src/driver/DriverFactory.ts b/src/driver/DriverFactory.ts index 80f58df4ef..3d6456b3bc 100644 --- a/src/driver/DriverFactory.ts +++ b/src/driver/DriverFactory.ts @@ -16,6 +16,7 @@ import {Driver} from "./Driver"; import {Connection} from "../connection/Connection"; import {SapDriver} from "./sap/SapDriver"; import {AuroraDataApiPostgresDriver} from "./postgres/PostgresDriver"; +import {BetterSqlite3Driver} from "./better-sqlite3/BetterSqlite3Driver"; /** * Helps to create drivers. @@ -40,6 +41,8 @@ export class DriverFactory { return new MysqlDriver(connection); case "sqlite": return new SqliteDriver(connection); + case "better-sqlite3": + return new BetterSqlite3Driver(connection); case "cordova": return new CordovaDriver(connection); case "nativescript": diff --git a/src/driver/better-sqlite3/BetterSqlite3ConnectionOptions.ts b/src/driver/better-sqlite3/BetterSqlite3ConnectionOptions.ts new file mode 100644 index 0000000000..09422cbfc0 --- /dev/null +++ b/src/driver/better-sqlite3/BetterSqlite3ConnectionOptions.ts @@ -0,0 +1,60 @@ +import {BaseConnectionOptions} from "../../connection/BaseConnectionOptions"; + +/** + * Sqlite-specific connection options. + */ +export interface BetterSqlite3ConnectionOptions extends BaseConnectionOptions { + + /** + * Database type. + */ + readonly type: "better-sqlite3"; + + /** + * Storage type or path to the storage. + */ + readonly database: string; + + /** + * Encryption key for for SQLCipher. + */ + readonly key?: string; + + /** + * Cache size of sqlite statement to speed up queries. + * Default: 100. + */ + readonly statementCacheSize?: number; + + /** + * Function to run before a database is used in typeorm. + * You can set pragmas, register plugins or register + * functions or aggregates in this function. + */ + readonly prepareDatabase?: (db: any) => void | Promise; + + /** + * Open the database connection in readonly mode. + * Default: false. + */ + readonly readonly?: boolean; + + /** + * If the database does not exist, an Error will be thrown instead of creating a new file. + * This option does not affect in-memory or readonly database connections. + * Default: false. + */ + readonly fileMustExist?: boolean; + + /** + * The number of milliseconds to wait when executing queries + * on a locked database, before throwing a SQLITE_BUSY error. + * Default: 5000. + */ + readonly timeout?: number; + + /** + * Provide a function that gets called with every SQL string executed by the database connection. + */ + readonly verbose?: Function; +} \ No newline at end of file diff --git a/src/driver/better-sqlite3/BetterSqlite3Driver.ts b/src/driver/better-sqlite3/BetterSqlite3Driver.ts new file mode 100644 index 0000000000..814b093d7a --- /dev/null +++ b/src/driver/better-sqlite3/BetterSqlite3Driver.ts @@ -0,0 +1,141 @@ +import { DriverPackageNotInstalledError } from "../../error/DriverPackageNotInstalledError"; +import { DriverOptionNotSetError } from "../../error/DriverOptionNotSetError"; +import { PlatformTools } from "../../platform/PlatformTools"; +import { Connection } from "../../connection/Connection"; +import { ColumnType } from "../types/ColumnTypes"; +import { QueryRunner } from "../../query-runner/QueryRunner"; +import { AbstractSqliteDriver } from "../sqlite-abstract/AbstractSqliteDriver"; +import { BetterSqlite3ConnectionOptions } from "./BetterSqlite3ConnectionOptions"; +import { BetterSqlite3QueryRunner } from "./BetterSqlite3QueryRunner"; + +/** + * Organizes communication with sqlite DBMS. + */ +export class BetterSqlite3Driver extends AbstractSqliteDriver { + + // ------------------------------------------------------------------------- + // Public Implemented Properties + // ------------------------------------------------------------------------- + + /** + * Connection options. + */ + options: BetterSqlite3ConnectionOptions; + + /** + * SQLite underlying library. + */ + sqlite: any; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(connection: Connection) { + super(connection); + + this.connection = connection; + this.options = connection.options as BetterSqlite3ConnectionOptions; + this.database = this.options.database; + + // validate options to make sure everything is set + if (!this.options.database) + throw new DriverOptionNotSetError("database"); + + // load sqlite package + this.loadDependencies(); + } + + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- + + /** + * Closes connection with database. + */ + async disconnect(): Promise { + this.queryRunner = undefined; + this.databaseConnection.close(); + } + + /** + * Creates a query runner used to execute database queries. + */ + createQueryRunner(mode: "master" | "slave" = "master"): QueryRunner { + if (!this.queryRunner) + this.queryRunner = new BetterSqlite3QueryRunner(this); + + return this.queryRunner; + } + + normalizeType(column: { type?: ColumnType, length?: number | string, precision?: number | null, scale?: number }): string { + if ((column.type as any) === Buffer) { + return "blob"; + } + + return super.normalizeType(column); + } + + // ------------------------------------------------------------------------- + // Protected Methods + // ------------------------------------------------------------------------- + + /** + * Creates connection with the database. + */ + protected async createDatabaseConnection() { + // not to create database directory if is in memory + if (this.options.database !== ":memory:") + await this.createDatabaseDirectory(this.options.database); + + const { + database, + readonly = false, + fileMustExist = false, + timeout = 5000, + verbose = null, + prepareDatabase + } = this.options; + const databaseConnection = this.sqlite(database, { readonly, fileMustExist, timeout, verbose }); + + // we need to enable foreign keys in sqlite to make sure all foreign key related features + // working properly. this also makes onDelete to work with sqlite. + databaseConnection.exec(`PRAGMA foreign_keys = ON`); + + // turn on WAL mode to enhance performance + databaseConnection.exec(`PRAGMA journal_mode = WAL`); + + // in the options, if encryption key for SQLCipher is setted. + if (this.options.key) { + databaseConnection.exec(`PRAGMA key = ${JSON.stringify(this.options.key)}`); + } + + if (typeof prepareDatabase === "function") { + prepareDatabase(databaseConnection); + } + + return databaseConnection; + } + + /** + * If driver dependency is not given explicitly, then try to load it via "require". + */ + protected loadDependencies(): void { + try { + this.sqlite = PlatformTools.load("better-sqlite3"); + + } catch (e) { + throw new DriverPackageNotInstalledError("SQLite", "better-sqlite3"); + } + } + + /** + * Auto creates database directory if it does not exist. + */ + protected createDatabaseDirectory(fullPath: string): Promise { + const mkdirp = PlatformTools.load("mkdirp"); + const path = PlatformTools.load("path"); + return mkdirp(path.dirname(fullPath)); + } + +} diff --git a/src/driver/better-sqlite3/BetterSqlite3QueryRunner.ts b/src/driver/better-sqlite3/BetterSqlite3QueryRunner.ts new file mode 100644 index 0000000000..b508fc6b71 --- /dev/null +++ b/src/driver/better-sqlite3/BetterSqlite3QueryRunner.ts @@ -0,0 +1,106 @@ +import { QueryRunnerAlreadyReleasedError } from "../../error/QueryRunnerAlreadyReleasedError"; +import { QueryFailedError } from "../../error/QueryFailedError"; +import { AbstractSqliteQueryRunner } from "../sqlite-abstract/AbstractSqliteQueryRunner"; +import { Broadcaster } from "../../subscriber/Broadcaster"; +import { BetterSqlite3Driver } from "./BetterSqlite3Driver"; + +/** + * Runs queries on a single sqlite database connection. + * + * Does not support compose primary keys with autoincrement field. + * todo: need to throw exception for this case. + */ +export class BetterSqlite3QueryRunner extends AbstractSqliteQueryRunner { + + /** + * Database driver used by connection. + */ + driver: BetterSqlite3Driver; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(driver: BetterSqlite3Driver) { + super(); + this.driver = driver; + this.connection = driver.connection; + this.broadcaster = new Broadcaster(this); + if (typeof this.driver.options.statementCacheSize === "number") { + this.cacheSize = this.driver.options.statementCacheSize; + } else { + this.cacheSize = 100; + } + } + + private cacheSize: number; + private stmtCache = new Map(); + + private async getStmt(query: string) { + if (this.cacheSize > 0) { + let stmt = this.stmtCache.get(query); + if (!stmt) { + const databaseConnection = await this.connect(); + stmt = databaseConnection.prepare(query); + this.stmtCache.set(query, stmt); + while (this.stmtCache.size > this.cacheSize) { + // since es6 map keeps the insertion order, + // it comes to be FIFO cache + const key = this.stmtCache.keys().next().value; + this.stmtCache.delete(key); + } + } + return stmt; + } else { + const databaseConnection = await this.connect(); + return databaseConnection.prepare(query); + } + } + + /** + * Executes a given SQL query. + */ + async query(query: string, parameters?: any[]): Promise { + if (this.isReleased) + throw new QueryRunnerAlreadyReleasedError(); + + const connection = this.driver.connection; + + parameters = parameters || []; + for (let i = 0; i < parameters.length; i++) { + // in "where" clauses the parameters are not escaped by the driver + if (typeof parameters[i] === "boolean") + parameters[i] = +parameters[i]; + } + + this.driver.connection.logger.logQuery(query, parameters, this); + const queryStartTime = +new Date(); + + const stmt = await this.getStmt(query); + + try { + + let result: any; + if (stmt.reader) { + result = stmt.all.apply(stmt, parameters); + } else { + result = stmt.run.apply(stmt, parameters); + if (query.substr(0, 6) === "INSERT") { + result = result.lastInsertRowid; + } + } + + // log slow queries if maxQueryExecution time is set + const maxQueryExecutionTime = connection.options.maxQueryExecutionTime; + const queryEndTime = +new Date(); + const queryExecutionTime = queryEndTime - queryStartTime; + if (maxQueryExecutionTime && queryExecutionTime > maxQueryExecutionTime) + connection.logger.logQuerySlow(queryExecutionTime, query, parameters, this); + + return result; + } catch (err) { + connection.logger.logQueryError(err, query, parameters, this); + throw new QueryFailedError(query, parameters, err); + } + } +} \ No newline at end of file diff --git a/src/driver/types/DatabaseType.ts b/src/driver/types/DatabaseType.ts index 4426c9829d..5ceaece0cc 100644 --- a/src/driver/types/DatabaseType.ts +++ b/src/driver/types/DatabaseType.ts @@ -17,4 +17,5 @@ export type DatabaseType = "mongodb"| "aurora-data-api"| "aurora-data-api-pg"| - "expo"; + "expo"| + "better-sqlite3"; diff --git a/src/error/MissingDriverError.ts b/src/error/MissingDriverError.ts index a5634f4679..5d2518fb55 100644 --- a/src/error/MissingDriverError.ts +++ b/src/error/MissingDriverError.ts @@ -7,7 +7,7 @@ export class MissingDriverError extends Error { constructor(driverType: string) { super(); Object.setPrototypeOf(this, MissingDriverError.prototype); - this.message = `Wrong driver: "${driverType}" given. Supported drivers are: "cordova", "expo", "mariadb", "mongodb", "mssql", "mysql", "oracle", "postgres", "sqlite", "sqljs", "react-native", "aurora-data-api", "aurora-data-api-pg".`; + this.message = `Wrong driver: "${driverType}" given. Supported drivers are: "cordova", "expo", "mariadb", "mongodb", "mssql", "mysql", "oracle", "postgres", "sqlite", "better-sqlite3", "sqljs", "react-native", "aurora-data-api", "aurora-data-api-pg".`; } } diff --git a/src/query-builder/DeleteQueryBuilder.ts b/src/query-builder/DeleteQueryBuilder.ts index 4be20ba377..e6efd9d697 100644 --- a/src/query-builder/DeleteQueryBuilder.ts +++ b/src/query-builder/DeleteQueryBuilder.ts @@ -16,6 +16,7 @@ import {MysqlDriver} from "../driver/mysql/MysqlDriver"; import {BroadcasterResult} from "../subscriber/BroadcasterResult"; import {EntitySchema} from "../index"; import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; +import {BetterSqlite3Driver} from "../driver/better-sqlite3/BetterSqlite3Driver"; /** * Allows to build complex sql queries in a fashion way and execute those queries. @@ -83,6 +84,10 @@ export class DeleteQueryBuilder extends QueryBuilder implements } else if (driver instanceof OracleDriver) { deleteResult.affected = result; + } else if (driver instanceof BetterSqlite3Driver) { // only works for better-sqlite3 + deleteResult.raw = result; + deleteResult.affected = result.changes; + } else { deleteResult.raw = result; } diff --git a/src/query-builder/UpdateQueryBuilder.ts b/src/query-builder/UpdateQueryBuilder.ts index 22ac5c5e12..2e0497f1f5 100644 --- a/src/query-builder/UpdateQueryBuilder.ts +++ b/src/query-builder/UpdateQueryBuilder.ts @@ -24,6 +24,7 @@ import {UpdateValuesMissingError} from "../error/UpdateValuesMissingError"; import {EntityColumnNotFound} from "../error/EntityColumnNotFound"; import {QueryDeepPartialEntity} from "./QueryPartialEntity"; import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; +import {BetterSqlite3Driver} from "../driver/better-sqlite3/BetterSqlite3Driver"; /** * Allows to build complex sql queries in a fashion way and execute those queries. @@ -108,6 +109,10 @@ export class UpdateQueryBuilder extends QueryBuilder implements updateResult.raw = result; updateResult.affected = result.affectedRows; } + else if (this.connection.driver instanceof BetterSqlite3Driver) { // only works for better-sqlite3 + updateResult.raw = result; + updateResult.affected = result.changes; + } else { updateResult.raw = result; } diff --git a/test/functional/connection-options-reader/configs/sqlite-memory.ts b/test/functional/connection-options-reader/configs/sqlite-memory.ts index 243dd45cef..81907c198e 100644 --- a/test/functional/connection-options-reader/configs/sqlite-memory.ts +++ b/test/functional/connection-options-reader/configs/sqlite-memory.ts @@ -6,4 +6,8 @@ module.exports = [{ type: "sqlite", name: "memory", database: ":memory:", +}, { + type: "better-sqlite3", + name: "memory2", + database: ":memory:", }]; \ No newline at end of file diff --git a/test/functional/database-schema/column-collation/sqlite/column-collation-sqlite.ts b/test/functional/database-schema/column-collation/sqlite/column-collation-sqlite.ts index cfd415258e..869aa9785e 100644 --- a/test/functional/database-schema/column-collation/sqlite/column-collation-sqlite.ts +++ b/test/functional/database-schema/column-collation/sqlite/column-collation-sqlite.ts @@ -10,7 +10,7 @@ describe.skip("database schema > column collation > sqlite", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/functional/database-schema/column-length/sqlite/column-length-sqlite..ts b/test/functional/database-schema/column-length/sqlite/column-length-sqlite..ts index deffdbd3aa..64a9d7f8fe 100644 --- a/test/functional/database-schema/column-length/sqlite/column-length-sqlite..ts +++ b/test/functional/database-schema/column-length/sqlite/column-length-sqlite..ts @@ -10,7 +10,7 @@ describe("database schema > column length > sqlite", () => { before(async () => { connections = await createTestingConnections({ entities: [Post], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/functional/database-schema/column-types/sqlite/column-types-sqlite.ts b/test/functional/database-schema/column-types/sqlite/column-types-sqlite.ts index b76a3e5bcc..64a2213f22 100644 --- a/test/functional/database-schema/column-types/sqlite/column-types-sqlite.ts +++ b/test/functional/database-schema/column-types/sqlite/column-types-sqlite.ts @@ -11,7 +11,7 @@ describe("database schema > column types > sqlite", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/functional/database-schema/simple-enums/enums.ts b/test/functional/database-schema/simple-enums/enums.ts index 61f8489342..dc35571042 100644 --- a/test/functional/database-schema/simple-enums/enums.ts +++ b/test/functional/database-schema/simple-enums/enums.ts @@ -9,7 +9,7 @@ describe("database schema > simple-enums", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "mariadb", "postgres", "sqlite", "mssql"] + enabledDrivers: ["mysql", "mariadb", "postgres", "sqlite", "better-sqlite3", "mssql"] }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/functional/indices/conditional-index/conditional-index.ts b/test/functional/indices/conditional-index/conditional-index.ts index 67f6648c8c..6b8ed7d5c9 100644 --- a/test/functional/indices/conditional-index/conditional-index.ts +++ b/test/functional/indices/conditional-index/conditional-index.ts @@ -9,7 +9,7 @@ describe("indices > conditional index", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mssql", "postgres", "sqlite"], // only these drivers supports conditional indices + enabledDrivers: ["mssql", "postgres", "sqlite", "better-sqlite3"], // only these drivers supports conditional indices schemaCreate: true, dropSchema: true, }); diff --git a/test/functional/query-builder/delete/query-builder-delete.ts b/test/functional/query-builder/delete/query-builder-delete.ts index 05d091dc46..4c7be03883 100644 --- a/test/functional/query-builder/delete/query-builder-delete.ts +++ b/test/functional/query-builder/delete/query-builder-delete.ts @@ -96,6 +96,7 @@ describe("query builder > delete", () => { it("should return correct delete result", () => Promise.all(connections.map(async connection => { // don't run test for sqlite and sqljs as they don't return affected rows + // better-sqlite3 works correctly if (connection.name === "sqlite" || connection.name === "sqljs" || connection.name === "sap") return; diff --git a/test/functional/query-builder/insert-on-conflict/query-builder-insert-on-conflict.ts b/test/functional/query-builder/insert-on-conflict/query-builder-insert-on-conflict.ts index 03ee8e50bd..67221d0f30 100644 --- a/test/functional/query-builder/insert-on-conflict/query-builder-insert-on-conflict.ts +++ b/test/functional/query-builder/insert-on-conflict/query-builder-insert-on-conflict.ts @@ -8,7 +8,7 @@ describe("query builder > insertion > on conflict", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["postgres", "sqlite"] // since on conflict statement is only supported in postgres and sqlite >= 3.24.0 + enabledDrivers: ["postgres", "sqlite", "better-sqlite3"] // since on conflict statement is only supported in postgres and sqlite >= 3.24.0 })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/query-runner/create-table.ts b/test/functional/query-runner/create-table.ts index f940faf7ea..3aa973fcea 100644 --- a/test/functional/query-runner/create-table.ts +++ b/test/functional/query-runner/create-table.ts @@ -12,7 +12,6 @@ import {AbstractSqliteDriver} from "../../../src/driver/sqlite-abstract/Abstract import {OracleDriver} from "../../../src/driver/oracle/OracleDriver"; import {Photo} from "./entity/Photo"; import {Book2, Book} from "./entity/Book"; -import {SqliteDriver} from "../../../src/driver/sqlite/SqliteDriver"; describe("query runner > create table", () => { @@ -332,7 +331,7 @@ describe("query runner > create table", () => { it("should correctly create table with different `withoutRowid` definitions", () => Promise.all(connections.map(async connection => { - if (connection.driver instanceof SqliteDriver) { + if (connection.driver instanceof AbstractSqliteDriver) { const queryRunner = connection.createQueryRunner(); // the table 'book' must contain a 'rowid' column @@ -358,7 +357,7 @@ describe("query runner > create table", () => { try { await connection.manager.query("SELECT rowid FROM book2"); } catch (e) { - expect(e.message).equal("SQLITE_ERROR: no such column: rowid"); + expect(e.message).contains("no such column: rowid"); } await queryRunner.dropTable("book2"); diff --git a/test/functional/query-runner/create-unique-constraint.ts b/test/functional/query-runner/create-unique-constraint.ts index ba39b2aa98..b3022e0a36 100644 --- a/test/functional/query-runner/create-unique-constraint.ts +++ b/test/functional/query-runner/create-unique-constraint.ts @@ -10,7 +10,7 @@ describe("query runner > create unique constraint", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mssql", "postgres", "sqlite", "oracle", "cockroachdb"], // mysql and sap does not supports unique constraints + enabledDrivers: ["mssql", "postgres", "sqlite", "better-sqlite3", "oracle", "cockroachdb"], // mysql and sap does not supports unique constraints schemaCreate: true, dropSchema: true, }); diff --git a/test/functional/query-runner/drop-column.ts b/test/functional/query-runner/drop-column.ts index 5e03628eee..2c422c2e9d 100644 --- a/test/functional/query-runner/drop-column.ts +++ b/test/functional/query-runner/drop-column.ts @@ -28,6 +28,11 @@ describe("query runner > drop column", () => { nameColumn!.should.be.exist; versionColumn!.should.be.exist; + // better-sqlite3 seems not able to create a check constraint on a non-existing column + if (connection.name === "better-sqlite3") { + await queryRunner.dropCheckConstraints(table!, table!.checks); + } + // In Sqlite 'dropColumns' method is more optimal than 'dropColumn', because it recreate table just once, // without all removed columns. In other drivers it's no difference between these methods, because 'dropColumns' // calls 'dropColumn' method for each removed column. diff --git a/test/functional/query-runner/drop-unique-constraint.ts b/test/functional/query-runner/drop-unique-constraint.ts index fd00a7952d..d2b5d5bcb0 100644 --- a/test/functional/query-runner/drop-unique-constraint.ts +++ b/test/functional/query-runner/drop-unique-constraint.ts @@ -8,7 +8,7 @@ describe("query runner > drop unique constraint", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mssql", "postgres", "sqlite", "oracle", "cockroachdb"], // mysql and sap does not supports unique constraints + enabledDrivers: ["mssql", "postgres", "sqlite", "better-sqlite3", "oracle", "cockroachdb"], // mysql and sap does not supports unique constraints schemaCreate: true, dropSchema: true, }); diff --git a/test/functional/relations/lazy-relations/basic-lazy-relation/basic-lazy-relations.ts b/test/functional/relations/lazy-relations/basic-lazy-relation/basic-lazy-relations.ts index 3c0d8fe7d1..7c83e84e39 100644 --- a/test/functional/relations/lazy-relations/basic-lazy-relation/basic-lazy-relations.ts +++ b/test/functional/relations/lazy-relations/basic-lazy-relation/basic-lazy-relations.ts @@ -326,7 +326,7 @@ describe("basic-lazy-relations", () => { loadedPost.title.should.be.equal("post with great category"); }))); - it("should successfully load relations within a transaction", () => Promise.all(connections.filter((connection) => (new Set(["mysql", "sqlite", "postgres"])).has(connection.options.type)).map(async connection => { + it("should successfully load relations within a transaction", () => Promise.all(connections.filter((connection) => (new Set(["mysql", "sqlite", "better-sqlite3", "postgres"])).has(connection.options.type)).map(async connection => { await connection.manager.transaction(async (manager) => { const category = new Category(); category.name = "category of great post"; @@ -344,7 +344,7 @@ describe("basic-lazy-relations", () => { }); }))); - it("should successfully load relations outside a transaction with entity generated within a transaction", () => Promise.all(connections.filter((connection) => (new Set(["mysql", "sqlite", "postgres"])).has(connection.options.type)).map(async connection => { + it("should successfully load relations outside a transaction with entity generated within a transaction", () => Promise.all(connections.filter((connection) => (new Set(["mysql", "sqlite", "better-sqlite3", "postgres"])).has(connection.options.type)).map(async connection => { const loadedCategory = await connection.manager.transaction(async (manager) => { const category = new Category(); category.name = "category of great post"; diff --git a/test/functional/repository/basic-methods/repository-basic-methods.ts b/test/functional/repository/basic-methods/repository-basic-methods.ts index 677b5a4c60..cc50f66549 100644 --- a/test/functional/repository/basic-methods/repository-basic-methods.ts +++ b/test/functional/repository/basic-methods/repository-basic-methods.ts @@ -324,8 +324,7 @@ describe("repository > basic methods", () => { }); describe("save", function () { - it("should update existing entity using transformers", async () => { - const connection = connections.find((c: Connection) => c.name === "sqlite"); + it("should update existing entity using transformers", () => Promise.all(connections.filter(c => c.name === "sqlite" || c.name === "better-sqlite3").map(async connection => { if (!connection || (connection.options as any).skip === true) { return; } @@ -354,7 +353,7 @@ describe("repository > basic methods", () => { saved.title.should.be.equal("New title"); saved.dateAdded.should.be.instanceof(Date); saved.dateAdded.getTime().should.be.equal(date.getTime()); - }); + }))); }); describe("preload also should also implement merge functionality", function() { diff --git a/test/functional/repository/find-options/repository-find-options.ts b/test/functional/repository/find-options/repository-find-options.ts index 42bffc16e1..e899d6158c 100644 --- a/test/functional/repository/find-options/repository-find-options.ts +++ b/test/functional/repository/find-options/repository-find-options.ts @@ -12,7 +12,7 @@ describe("repository > find options", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] + enabledDrivers: ["sqlite", "better-sqlite3"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/table-inheritance/single-table/database-option-inherited/database-option-inherited.ts b/test/functional/table-inheritance/single-table/database-option-inherited/database-option-inherited.ts index 18700f5e11..66ba39cef9 100644 --- a/test/functional/table-inheritance/single-table/database-option-inherited/database-option-inherited.ts +++ b/test/functional/table-inheritance/single-table/database-option-inherited/database-option-inherited.ts @@ -8,7 +8,7 @@ describe("table-inheritance > single-table > database-option-inherited", () => { before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], // creating more databases isn't always possible(e.g oracle official docker images) - enabledDrivers: ["postgres", "cockroachdb", "mariadb", "mssql", "mysql", "sqlite", "sqljs"] + enabledDrivers: ["postgres", "cockroachdb", "mariadb", "mssql", "mysql", "sqlite", "better-sqlite3", "sqljs"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/transaction/database-specific-isolation/sqlite-isolation.ts b/test/functional/transaction/database-specific-isolation/sqlite-isolation.ts index 907cb0da2d..9915e44415 100644 --- a/test/functional/transaction/database-specific-isolation/sqlite-isolation.ts +++ b/test/functional/transaction/database-specific-isolation/sqlite-isolation.ts @@ -10,7 +10,7 @@ describe("transaction > transaction with sqlite connection partial isolation sup let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] // todo: for some reasons mariadb tests are not passing here + enabledDrivers: ["sqlite", "better-sqlite3"] // todo: for some reasons mariadb tests are not passing here })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/transaction/return-data-from-transaction/return-data-from-transaction.ts b/test/functional/transaction/return-data-from-transaction/return-data-from-transaction.ts index db10856a45..55db390eb5 100644 --- a/test/functional/transaction/return-data-from-transaction/return-data-from-transaction.ts +++ b/test/functional/transaction/return-data-from-transaction/return-data-from-transaction.ts @@ -10,7 +10,7 @@ describe("transaction > return data from transaction", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "sqlite", "postgres"] // todo: for some reasons mariadb tests are not passing here + enabledDrivers: ["mysql", "sqlite", "better-sqlite3", "postgres"] // todo: for some reasons mariadb tests are not passing here })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/transaction/transaction-in-entity-manager/transaction-in-entity-manager.ts b/test/functional/transaction/transaction-in-entity-manager/transaction-in-entity-manager.ts index 9b20840dc9..a47c9d7fbd 100644 --- a/test/functional/transaction/transaction-in-entity-manager/transaction-in-entity-manager.ts +++ b/test/functional/transaction/transaction-in-entity-manager/transaction-in-entity-manager.ts @@ -10,7 +10,7 @@ describe("transaction > transaction with entity manager", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "sqlite", "postgres"] // todo: for some reasons mariadb tests are not passing here + enabledDrivers: ["mysql", "sqlite", "better-sqlite3", "postgres"] // todo: for some reasons mariadb tests are not passing here })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/uuid/sqlite/uuid-sqlite.ts b/test/functional/uuid/sqlite/uuid-sqlite.ts index f167faee6d..ee03f28e3b 100644 --- a/test/functional/uuid/sqlite/uuid-sqlite.ts +++ b/test/functional/uuid/sqlite/uuid-sqlite.ts @@ -11,7 +11,7 @@ describe("uuid-sqlite", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/functional/view-entity/sqlite/view-entity-sqlite.ts b/test/functional/view-entity/sqlite/view-entity-sqlite.ts index cd1b0aef51..108c9bbea1 100644 --- a/test/functional/view-entity/sqlite/view-entity-sqlite.ts +++ b/test/functional/view-entity/sqlite/view-entity-sqlite.ts @@ -12,7 +12,7 @@ describe("view entity > sqlite", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] + enabledDrivers: ["sqlite", "better-sqlite3"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/134/issue-134.ts b/test/github-issues/134/issue-134.ts index 8a364197fc..1b4b8e1e2c 100644 --- a/test/github-issues/134/issue-134.ts +++ b/test/github-issues/134/issue-134.ts @@ -9,7 +9,7 @@ describe("github issues > #134 Error TIME is converted to 'HH-mm' instead of 'HH let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "mariadb", "sqlite", "mssql", "postgres"] // Oracle does not support TIME data type. + enabledDrivers: ["mysql", "mariadb", "sqlite", "better-sqlite3", "mssql", "postgres"] // Oracle does not support TIME data type. })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/1465/save-relation.ts b/test/github-issues/1465/save-relation.ts index bd5ef09067..e0ee4f0ce2 100644 --- a/test/github-issues/1465/save-relation.ts +++ b/test/github-issues/1465/save-relation.ts @@ -10,7 +10,7 @@ describe("save child and parent entity", () => { let connections: Connection[] = []; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "mariadb", "sqlite", "sqljs"] + enabledDrivers: ["mysql", "mariadb", "sqlite", "better-sqlite3", "sqljs"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/1476/issue-1476.ts b/test/github-issues/1476/issue-1476.ts index b886071a40..7b73d5b468 100644 --- a/test/github-issues/1476/issue-1476.ts +++ b/test/github-issues/1476/issue-1476.ts @@ -12,7 +12,7 @@ describe("github issues > #1476 subqueries", () => { let connections: Connection[] = []; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "mariadb", "sqlite", "sqljs"] + enabledDrivers: ["mysql", "mariadb", "sqlite", "better-sqlite3", "sqljs"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/1898/issue-1898.ts b/test/github-issues/1898/issue-1898.ts index a591d7c1ae..3eac414962 100644 --- a/test/github-issues/1898/issue-1898.ts +++ b/test/github-issues/1898/issue-1898.ts @@ -9,7 +9,7 @@ describe("github issues > #1898 Simple JSON breaking in @next", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], schemaCreate: true, dropSchema: true }); diff --git a/test/github-issues/190/issue-190.ts b/test/github-issues/190/issue-190.ts index 665b3ab5c8..ab7f397027 100644 --- a/test/github-issues/190/issue-190.ts +++ b/test/github-issues/190/issue-190.ts @@ -8,7 +8,7 @@ describe("github issues > #190 too many SQL variables when using setMaxResults i let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] // this issue only related to sqlite + enabledDrivers: ["sqlite", "better-sqlite3"] // this issue only related to sqlite })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/1981/issue-1981.ts b/test/github-issues/1981/issue-1981.ts index 1134f42d75..3e653ec0cf 100644 --- a/test/github-issues/1981/issue-1981.ts +++ b/test/github-issues/1981/issue-1981.ts @@ -7,7 +7,7 @@ describe("github issues > #1981 Boolean values not casted properly when used in let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/2005/issue-2005.ts b/test/github-issues/2005/issue-2005.ts index 52aaf9eb3e..bb714b3158 100644 --- a/test/github-issues/2005/issue-2005.ts +++ b/test/github-issues/2005/issue-2005.ts @@ -7,7 +7,7 @@ describe("github issues > #2005", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] + enabledDrivers: ["sqlite", "better-sqlite3"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/2199/issue-2199.ts b/test/github-issues/2199/issue-2199.ts index 3898c9f36f..8a51691ac9 100644 --- a/test/github-issues/2199/issue-2199.ts +++ b/test/github-issues/2199/issue-2199.ts @@ -9,7 +9,7 @@ describe("github issues > #2199 - Inserting value for @PrimaryGeneratedColumn() let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "mariadb", "sqlite"], + enabledDrivers: ["mysql", "mariadb", "sqlite", "better-sqlite3"], schemaCreate: true, dropSchema: true })); diff --git a/test/github-issues/2518/issue-2518.ts b/test/github-issues/2518/issue-2518.ts index 538f1ee466..8b451aba25 100644 --- a/test/github-issues/2518/issue-2518.ts +++ b/test/github-issues/2518/issue-2518.ts @@ -12,7 +12,7 @@ describe("github issues > #2518 TreeRepository.findDescendantsTree does not load (connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], // data type text isn't compatible with oracle - enabledDrivers: ["postgres", "cockroachdb", "mariadb", "mssql", "mysql", "sqlite", "sqljs"] + enabledDrivers: ["postgres", "cockroachdb", "mariadb", "mssql", "mysql", "sqlite", "better-sqlite3", "sqljs"] })) ); diff --git a/test/github-issues/2733/issue-2733.ts b/test/github-issues/2733/issue-2733.ts index 02ff1074bf..bb4c1eb6cb 100644 --- a/test/github-issues/2733/issue-2733.ts +++ b/test/github-issues/2733/issue-2733.ts @@ -12,7 +12,7 @@ describe("github issues > #2733 should correctly handle function calls with uper entities: [__dirname + "/entity/MSSQLDummy{.js,.ts}"], schemaCreate: true, dropSchema: true, - enabledDrivers: ["mssql", "sqljs", "sqlite"], + enabledDrivers: ["mssql", "sqljs", "sqlite", "better-sqlite3"], }); await reloadTestingDatabases(connections); await Promise.all(connections.map(async connection => { diff --git a/test/github-issues/3158/issue-3158.ts b/test/github-issues/3158/issue-3158.ts index d074678c76..7f26cc6da9 100644 --- a/test/github-issues/3158/issue-3158.ts +++ b/test/github-issues/3158/issue-3158.ts @@ -8,7 +8,7 @@ it("github issues > #3158 Cannot run sync a second time", async () => { entities: [__dirname + "/entity/*{.js,.ts}"], schemaCreate: true, dropSchema: true, - enabledDrivers: ["mysql", "mariadb", "oracle", "mssql", "sqljs", "sqlite"], + enabledDrivers: ["mysql", "mariadb", "oracle", "mssql", "sqljs", "sqlite", "better-sqlite3"], // todo(AlexMesser): check why tests are failing under postgres driver }); await reloadTestingDatabases(connections); diff --git a/test/github-issues/3949/issue-3949.ts b/test/github-issues/3949/issue-3949.ts index 1e89826a45..a2bf94e917 100644 --- a/test/github-issues/3949/issue-3949.ts +++ b/test/github-issues/3949/issue-3949.ts @@ -11,7 +11,7 @@ describe("github issues > #3949 sqlite date hydration is susceptible to corrupti entities: [__dirname + "/entity/*{.js,.ts}"], schemaCreate: true, dropSchema: true, - enabledDrivers: ["sqlite", "sqljs"], + enabledDrivers: ["sqlite", "better-sqlite3", "sqljs"], }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/github-issues/4096/issue-4096.ts b/test/github-issues/4096/issue-4096.ts index 3685d0d17a..40f200f209 100644 --- a/test/github-issues/4096/issue-4096.ts +++ b/test/github-issues/4096/issue-4096.ts @@ -9,7 +9,7 @@ describe("github issues > #4096 SQLite support for orUpdate", () => { before(async () => connections = await createTestingConnections({ entities: [User], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], schemaCreate: true, dropSchema: true, })); diff --git a/test/github-issues/4147/issue-4147.ts b/test/github-issues/4147/issue-4147.ts index d57a645e72..43b52aa152 100644 --- a/test/github-issues/4147/issue-4147.ts +++ b/test/github-issues/4147/issue-4147.ts @@ -14,7 +14,7 @@ describe("github issues > #4147 `SQLITE_ERROR: near \"-\": syntax error` when us (connections = await createTestingConnections({ entities: [new EntitySchema(PostSchema)], dropSchema: true, - enabledDrivers: ["sqlite"] + enabledDrivers: ["sqlite", "better-sqlite3"] })) ); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/github-issues/513/issue-513.ts b/test/github-issues/513/issue-513.ts index be0cf0b754..5d039ca808 100644 --- a/test/github-issues/513/issue-513.ts +++ b/test/github-issues/513/issue-513.ts @@ -11,7 +11,7 @@ describe("github issues > #513 Incorrect time/datetime types for SQLite", () => let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] + enabledDrivers: ["sqlite", "better-sqlite3"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/798/issue-798.ts b/test/github-issues/798/issue-798.ts index 445cbb3126..34a05c6775 100644 --- a/test/github-issues/798/issue-798.ts +++ b/test/github-issues/798/issue-798.ts @@ -28,4 +28,11 @@ describe("github issues > #798 sqlite: 'database' path in ormconfig.json is not assert.strictEqual(connection.isConnected, true); }); + it("should find the sqlite database if the cwd is changed for better-sqlite3", async function () { + const options = await getConnectionOptions("better-sqlite3"); + connection = await createConnection(options); + + assert.strictEqual(connection.isConnected, true); + }); + }); \ No newline at end of file diff --git a/test/github-issues/799/issue-799.ts b/test/github-issues/799/issue-799.ts index 28152caff3..d87a1c2ec4 100644 --- a/test/github-issues/799/issue-799.ts +++ b/test/github-issues/799/issue-799.ts @@ -34,4 +34,14 @@ describe("github issues > #799 sqlite: 'database' path should be created", () => assert.strictEqual(connection.isConnected, true); }); + it("should create the whole path to database file for better-sqlite3", async function () { + connection = await createConnection({ + "name": "better-sqlite3", + "type": "better-sqlite3", + "database": path + }); + + assert.strictEqual(connection.isConnected, true); + }); + });