@yandjin-mikro-orm/better-sqlite 6.1.4-rc-sti-changes-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BetterSqliteConnection.d.ts +16 -0
- package/BetterSqliteConnection.js +172 -0
- package/BetterSqliteDriver.d.ts +6 -0
- package/BetterSqliteDriver.js +15 -0
- package/BetterSqliteExceptionConverter.d.ts +9 -0
- package/BetterSqliteExceptionConverter.js +56 -0
- package/BetterSqliteMikroORM.d.ts +19 -0
- package/BetterSqliteMikroORM.js +29 -0
- package/BetterSqlitePlatform.d.ts +55 -0
- package/BetterSqlitePlatform.js +96 -0
- package/BetterSqliteSchemaHelper.d.ts +23 -0
- package/BetterSqliteSchemaHelper.js +168 -0
- package/LICENSE +21 -0
- package/README.md +383 -0
- package/index.d.ts +7 -0
- package/index.js +26 -0
- package/index.mjs +220 -0
- package/package.json +72 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AbstractSqlConnection, type Knex } from "@yandjin-mikro-orm/knex";
|
|
2
|
+
export declare class BetterSqliteConnection extends AbstractSqlConnection {
|
|
3
|
+
createKnex(): void;
|
|
4
|
+
connect(): Promise<void>;
|
|
5
|
+
getDefaultClientUrl(): string;
|
|
6
|
+
getClientUrl(): string;
|
|
7
|
+
loadFile(path: string): Promise<void>;
|
|
8
|
+
protected getKnexOptions(type: string): Knex.Config;
|
|
9
|
+
protected transformRawResult<T>(res: any, method: "all" | "get" | "run"): T;
|
|
10
|
+
/**
|
|
11
|
+
* monkey patch knex' BetterSqlite Dialect so it returns inserted id when doing raw insert query
|
|
12
|
+
*/
|
|
13
|
+
private getPatchedDialect;
|
|
14
|
+
private isRunQuery;
|
|
15
|
+
private getCallMethod;
|
|
16
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BetterSqliteConnection = void 0;
|
|
4
|
+
const fs_extra_1 = require("fs-extra");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const knex_1 = require("@yandjin-mikro-orm/knex");
|
|
7
|
+
const core_1 = require("@yandjin-mikro-orm/core");
|
|
8
|
+
class BetterSqliteConnection extends knex_1.AbstractSqlConnection {
|
|
9
|
+
createKnex() {
|
|
10
|
+
this.getPatchedDialect();
|
|
11
|
+
this.client = this.createKnexClient("better-sqlite3");
|
|
12
|
+
this.connected = true;
|
|
13
|
+
}
|
|
14
|
+
async connect() {
|
|
15
|
+
this.createKnex();
|
|
16
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.dirname)(this.config.get("dbName")));
|
|
17
|
+
await this.client.raw("pragma foreign_keys = on");
|
|
18
|
+
}
|
|
19
|
+
getDefaultClientUrl() {
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
getClientUrl() {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
async loadFile(path) {
|
|
26
|
+
const conn = await this.client.client.acquireConnection();
|
|
27
|
+
await conn.exec((await (0, fs_extra_1.readFile)(path)).toString());
|
|
28
|
+
await this.client.client.releaseConnection(conn);
|
|
29
|
+
}
|
|
30
|
+
getKnexOptions(type) {
|
|
31
|
+
return core_1.Utils.mergeConfig({
|
|
32
|
+
client: type,
|
|
33
|
+
connection: {
|
|
34
|
+
filename: this.config.get("dbName"),
|
|
35
|
+
},
|
|
36
|
+
pool: this.config.get("pool"),
|
|
37
|
+
useNullAsDefault: true,
|
|
38
|
+
}, this.config.get("driverOptions"));
|
|
39
|
+
}
|
|
40
|
+
transformRawResult(res, method) {
|
|
41
|
+
if (method === "get") {
|
|
42
|
+
return res[0];
|
|
43
|
+
}
|
|
44
|
+
if (method === "all") {
|
|
45
|
+
return res;
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(res)) {
|
|
48
|
+
return {
|
|
49
|
+
insertId: res[res.length - 1]?.id ?? 0,
|
|
50
|
+
affectedRows: res.length,
|
|
51
|
+
row: res[0],
|
|
52
|
+
rows: res,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
insertId: res.lastInsertRowid,
|
|
57
|
+
affectedRows: res.changes,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* monkey patch knex' BetterSqlite Dialect so it returns inserted id when doing raw insert query
|
|
62
|
+
*/
|
|
63
|
+
getPatchedDialect() {
|
|
64
|
+
const { Sqlite3Dialect, Sqlite3DialectTableCompiler } = knex_1.MonkeyPatchable;
|
|
65
|
+
if (Sqlite3Dialect.prototype.__patched) {
|
|
66
|
+
return Sqlite3Dialect;
|
|
67
|
+
}
|
|
68
|
+
const processResponse = Sqlite3Dialect.prototype.processResponse;
|
|
69
|
+
Sqlite3Dialect.prototype.__patched = true;
|
|
70
|
+
Sqlite3Dialect.prototype.processResponse = (obj, runner) => {
|
|
71
|
+
if (obj.method === "raw" && this.isRunQuery(obj.sql)) {
|
|
72
|
+
return obj.response ?? obj.context;
|
|
73
|
+
}
|
|
74
|
+
return processResponse(obj, runner);
|
|
75
|
+
};
|
|
76
|
+
Sqlite3Dialect.prototype._query = (connection, obj) => {
|
|
77
|
+
const callMethod = this.getCallMethod(obj);
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
/* istanbul ignore if */
|
|
80
|
+
if (!connection?.[callMethod]) {
|
|
81
|
+
return reject(new Error(`Error calling ${callMethod} on connection.`));
|
|
82
|
+
}
|
|
83
|
+
connection[callMethod](obj.sql, obj.bindings, function (err, response) {
|
|
84
|
+
if (err) {
|
|
85
|
+
return reject(err);
|
|
86
|
+
}
|
|
87
|
+
obj.response = response;
|
|
88
|
+
obj.context = this;
|
|
89
|
+
return resolve(obj);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
/* istanbul ignore next */
|
|
94
|
+
Sqlite3DialectTableCompiler.prototype.foreign = function (foreignInfo) {
|
|
95
|
+
foreignInfo.column = this.formatter.columnize(foreignInfo.column);
|
|
96
|
+
foreignInfo.column = Array.isArray(foreignInfo.column)
|
|
97
|
+
? foreignInfo.column
|
|
98
|
+
: [foreignInfo.column];
|
|
99
|
+
foreignInfo.column = foreignInfo.column.map((column) => this.client.customWrapIdentifier(column, (a) => a));
|
|
100
|
+
foreignInfo.inTable = this.client.customWrapIdentifier(foreignInfo.inTable, (a) => a);
|
|
101
|
+
foreignInfo.references = Array.isArray(foreignInfo.references)
|
|
102
|
+
? foreignInfo.references
|
|
103
|
+
: [foreignInfo.references];
|
|
104
|
+
foreignInfo.references = foreignInfo.references.map((column) => this.client.customWrapIdentifier(column, (a) => a));
|
|
105
|
+
// quoted versions
|
|
106
|
+
const column = this.formatter.columnize(foreignInfo.column);
|
|
107
|
+
const inTable = this.formatter.columnize(foreignInfo.inTable);
|
|
108
|
+
const references = this.formatter.columnize(foreignInfo.references);
|
|
109
|
+
const keyName = this.formatter.columnize(foreignInfo.keyName);
|
|
110
|
+
const addColumnQuery = this.sequence.find((query) => query.sql.includes(`add column ${column[0]}`));
|
|
111
|
+
// no need for temp tables if we just add a column
|
|
112
|
+
if (addColumnQuery) {
|
|
113
|
+
const onUpdate = foreignInfo.onUpdate
|
|
114
|
+
? ` on update ${foreignInfo.onUpdate}`
|
|
115
|
+
: "";
|
|
116
|
+
const deleteRule = foreignInfo.deleteRule
|
|
117
|
+
? ` on delete ${foreignInfo.deleteRule}`
|
|
118
|
+
: "";
|
|
119
|
+
addColumnQuery.sql += ` constraint ${keyName} references ${inTable} (${references})${onUpdate}${deleteRule}`;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
123
|
+
const compiler = this;
|
|
124
|
+
if (this.method !== "create" && this.method !== "createIfNot") {
|
|
125
|
+
this.pushQuery({
|
|
126
|
+
sql: `PRAGMA table_info(${this.tableName()})`,
|
|
127
|
+
statementsProducer(pragma, connection) {
|
|
128
|
+
return compiler.client
|
|
129
|
+
.ddl(compiler, pragma, connection)
|
|
130
|
+
.foreign(foreignInfo);
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
return Sqlite3Dialect;
|
|
136
|
+
}
|
|
137
|
+
isRunQuery(query) {
|
|
138
|
+
query = query.trim().toLowerCase();
|
|
139
|
+
if ((query.startsWith("insert into") || query.startsWith("update ")) &&
|
|
140
|
+
query.includes(" returning ")) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
return (query.startsWith("insert into") ||
|
|
144
|
+
query.startsWith("update") ||
|
|
145
|
+
query.startsWith("delete") ||
|
|
146
|
+
query.startsWith("truncate"));
|
|
147
|
+
}
|
|
148
|
+
getCallMethod(obj) {
|
|
149
|
+
if (obj.method === "raw") {
|
|
150
|
+
const query = obj.sql.trim().toLowerCase();
|
|
151
|
+
if ((query.startsWith("insert into") || query.startsWith("update ")) &&
|
|
152
|
+
query.includes(" returning ")) {
|
|
153
|
+
return "all";
|
|
154
|
+
}
|
|
155
|
+
if (this.isRunQuery(query)) {
|
|
156
|
+
return "run";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/* istanbul ignore next */
|
|
160
|
+
switch (obj.method) {
|
|
161
|
+
case "insert":
|
|
162
|
+
case "update":
|
|
163
|
+
return obj.returning ? "all" : "run";
|
|
164
|
+
case "counter":
|
|
165
|
+
case "del":
|
|
166
|
+
return "run";
|
|
167
|
+
default:
|
|
168
|
+
return "all";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
exports.BetterSqliteConnection = BetterSqliteConnection;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Configuration } from "@yandjin-mikro-orm/core";
|
|
2
|
+
import { AbstractSqlDriver } from "@yandjin-mikro-orm/knex";
|
|
3
|
+
import { BetterSqliteConnection } from "./BetterSqliteConnection";
|
|
4
|
+
export declare class BetterSqliteDriver extends AbstractSqlDriver<BetterSqliteConnection> {
|
|
5
|
+
constructor(config: Configuration);
|
|
6
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BetterSqliteDriver = void 0;
|
|
4
|
+
const knex_1 = require("@yandjin-mikro-orm/knex");
|
|
5
|
+
const BetterSqliteConnection_1 = require("./BetterSqliteConnection");
|
|
6
|
+
const BetterSqlitePlatform_1 = require("./BetterSqlitePlatform");
|
|
7
|
+
class BetterSqliteDriver extends knex_1.AbstractSqlDriver {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
super(config, new BetterSqlitePlatform_1.BetterSqlitePlatform(), BetterSqliteConnection_1.BetterSqliteConnection, [
|
|
10
|
+
"knex",
|
|
11
|
+
"better-sqlite3",
|
|
12
|
+
]);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.BetterSqliteDriver = BetterSqliteDriver;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ExceptionConverter, type Dictionary, type DriverException } from "@yandjin-mikro-orm/core";
|
|
2
|
+
export declare class BetterSqliteExceptionConverter extends ExceptionConverter {
|
|
3
|
+
/**
|
|
4
|
+
* @inheritDoc
|
|
5
|
+
* @link http://www.sqlite.org/c3ref/c_abort.html
|
|
6
|
+
* @link https://github.com/doctrine/dbal/blob/master/src/Driver/AbstractSQLiteDriver.php
|
|
7
|
+
*/
|
|
8
|
+
convertException(exception: Error & Dictionary): DriverException;
|
|
9
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BetterSqliteExceptionConverter = void 0;
|
|
4
|
+
const core_1 = require("@yandjin-mikro-orm/core");
|
|
5
|
+
class BetterSqliteExceptionConverter extends core_1.ExceptionConverter {
|
|
6
|
+
/* istanbul ignore next */
|
|
7
|
+
/**
|
|
8
|
+
* @inheritDoc
|
|
9
|
+
* @link http://www.sqlite.org/c3ref/c_abort.html
|
|
10
|
+
* @link https://github.com/doctrine/dbal/blob/master/src/Driver/AbstractSQLiteDriver.php
|
|
11
|
+
*/
|
|
12
|
+
convertException(exception) {
|
|
13
|
+
if (exception.message.includes("database is locked")) {
|
|
14
|
+
return new core_1.LockWaitTimeoutException(exception);
|
|
15
|
+
}
|
|
16
|
+
if (exception.message.includes("must be unique") ||
|
|
17
|
+
exception.message.includes("is not unique") ||
|
|
18
|
+
exception.message.includes("are not unique") ||
|
|
19
|
+
exception.message.includes("UNIQUE constraint failed")) {
|
|
20
|
+
return new core_1.UniqueConstraintViolationException(exception);
|
|
21
|
+
}
|
|
22
|
+
if (exception.message.includes("may not be NULL") ||
|
|
23
|
+
exception.message.includes("NOT NULL constraint failed")) {
|
|
24
|
+
return new core_1.NotNullConstraintViolationException(exception);
|
|
25
|
+
}
|
|
26
|
+
if (exception.message.includes("CHECK constraint failed")) {
|
|
27
|
+
return new core_1.CheckConstraintViolationException(exception);
|
|
28
|
+
}
|
|
29
|
+
if (exception.message.includes("no such table:")) {
|
|
30
|
+
return new core_1.TableNotFoundException(exception);
|
|
31
|
+
}
|
|
32
|
+
if (exception.message.includes("already exists")) {
|
|
33
|
+
return new core_1.TableExistsException(exception);
|
|
34
|
+
}
|
|
35
|
+
if (exception.message.includes("no such column:")) {
|
|
36
|
+
return new core_1.InvalidFieldNameException(exception);
|
|
37
|
+
}
|
|
38
|
+
if (exception.message.includes("ambiguous column name")) {
|
|
39
|
+
return new core_1.NonUniqueFieldNameException(exception);
|
|
40
|
+
}
|
|
41
|
+
if (exception.message.includes("syntax error")) {
|
|
42
|
+
return new core_1.SyntaxErrorException(exception);
|
|
43
|
+
}
|
|
44
|
+
if (exception.message.includes("attempt to write a readonly database")) {
|
|
45
|
+
return new core_1.ReadOnlyException(exception);
|
|
46
|
+
}
|
|
47
|
+
if (exception.message.includes("unable to open database file")) {
|
|
48
|
+
return new core_1.ConnectionException(exception);
|
|
49
|
+
}
|
|
50
|
+
if (exception.message.includes("FOREIGN KEY constraint failed")) {
|
|
51
|
+
return new core_1.ForeignKeyConstraintViolationException(exception);
|
|
52
|
+
}
|
|
53
|
+
return super.convertException(exception);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.BetterSqliteExceptionConverter = BetterSqliteExceptionConverter;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { MikroORM, type Options, type IDatabaseDriver, type EntityManager, type EntityManagerType } from "@yandjin-mikro-orm/core";
|
|
2
|
+
import { BetterSqliteDriver } from "./BetterSqliteDriver";
|
|
3
|
+
import type { SqlEntityManager } from "@yandjin-mikro-orm/knex";
|
|
4
|
+
/**
|
|
5
|
+
* @inheritDoc
|
|
6
|
+
*/
|
|
7
|
+
export declare class BetterSqliteMikroORM<EM extends EntityManager = SqlEntityManager> extends MikroORM<BetterSqliteDriver, EM> {
|
|
8
|
+
private static DRIVER;
|
|
9
|
+
/**
|
|
10
|
+
* @inheritDoc
|
|
11
|
+
*/
|
|
12
|
+
static init<D extends IDatabaseDriver = BetterSqliteDriver, EM extends EntityManager = D[typeof EntityManagerType] & EntityManager>(options?: Options<D, EM>): Promise<MikroORM<D, EM>>;
|
|
13
|
+
/**
|
|
14
|
+
* @inheritDoc
|
|
15
|
+
*/
|
|
16
|
+
static initSync<D extends IDatabaseDriver = BetterSqliteDriver, EM extends EntityManager = D[typeof EntityManagerType] & EntityManager>(options: Options<D, EM>): MikroORM<D, EM>;
|
|
17
|
+
}
|
|
18
|
+
export type BetterSqliteOptions = Options<BetterSqliteDriver>;
|
|
19
|
+
export declare function defineBetterSqliteConfig(options: BetterSqliteOptions): Options<BetterSqliteDriver, SqlEntityManager<BetterSqliteDriver> & EntityManager<IDatabaseDriver<import("@yandjin-mikro-orm/core").Connection>>>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineBetterSqliteConfig = exports.BetterSqliteMikroORM = void 0;
|
|
4
|
+
const core_1 = require("@yandjin-mikro-orm/core");
|
|
5
|
+
const BetterSqliteDriver_1 = require("./BetterSqliteDriver");
|
|
6
|
+
/**
|
|
7
|
+
* @inheritDoc
|
|
8
|
+
*/
|
|
9
|
+
class BetterSqliteMikroORM extends core_1.MikroORM {
|
|
10
|
+
static DRIVER = BetterSqliteDriver_1.BetterSqliteDriver;
|
|
11
|
+
/**
|
|
12
|
+
* @inheritDoc
|
|
13
|
+
*/
|
|
14
|
+
static async init(options) {
|
|
15
|
+
return super.init(options);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* @inheritDoc
|
|
19
|
+
*/
|
|
20
|
+
static initSync(options) {
|
|
21
|
+
return super.initSync(options);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.BetterSqliteMikroORM = BetterSqliteMikroORM;
|
|
25
|
+
/* istanbul ignore next */
|
|
26
|
+
function defineBetterSqliteConfig(options) {
|
|
27
|
+
return (0, core_1.defineConfig)({ driver: BetterSqliteDriver_1.BetterSqliteDriver, ...options });
|
|
28
|
+
}
|
|
29
|
+
exports.defineBetterSqliteConfig = defineBetterSqliteConfig;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type EntityProperty } from "@yandjin-mikro-orm/core";
|
|
2
|
+
import { AbstractSqlPlatform } from "@yandjin-mikro-orm/knex";
|
|
3
|
+
import { BetterSqliteSchemaHelper } from "./BetterSqliteSchemaHelper";
|
|
4
|
+
import { BetterSqliteExceptionConverter } from "./BetterSqliteExceptionConverter";
|
|
5
|
+
export declare class BetterSqlitePlatform extends AbstractSqlPlatform {
|
|
6
|
+
protected readonly schemaHelper: BetterSqliteSchemaHelper;
|
|
7
|
+
protected readonly exceptionConverter: BetterSqliteExceptionConverter;
|
|
8
|
+
usesDefaultKeyword(): boolean;
|
|
9
|
+
usesReturningStatement(): boolean;
|
|
10
|
+
getCurrentTimestampSQL(length: number): string;
|
|
11
|
+
getDateTimeTypeDeclarationSQL(column: {
|
|
12
|
+
length: number;
|
|
13
|
+
}): string;
|
|
14
|
+
getEnumTypeDeclarationSQL(column: {
|
|
15
|
+
items?: unknown[];
|
|
16
|
+
fieldNames: string[];
|
|
17
|
+
length?: number;
|
|
18
|
+
unsigned?: boolean;
|
|
19
|
+
autoincrement?: boolean;
|
|
20
|
+
}): string;
|
|
21
|
+
getTinyIntTypeDeclarationSQL(column: {
|
|
22
|
+
length?: number;
|
|
23
|
+
unsigned?: boolean;
|
|
24
|
+
autoincrement?: boolean;
|
|
25
|
+
}): string;
|
|
26
|
+
getSmallIntTypeDeclarationSQL(column: {
|
|
27
|
+
length?: number;
|
|
28
|
+
unsigned?: boolean;
|
|
29
|
+
autoincrement?: boolean;
|
|
30
|
+
}): string;
|
|
31
|
+
getIntegerTypeDeclarationSQL(column: {
|
|
32
|
+
length?: number;
|
|
33
|
+
unsigned?: boolean;
|
|
34
|
+
autoincrement?: boolean;
|
|
35
|
+
}): string;
|
|
36
|
+
getFloatDeclarationSQL(): string;
|
|
37
|
+
getBooleanTypeDeclarationSQL(): string;
|
|
38
|
+
getVarcharTypeDeclarationSQL(column: {
|
|
39
|
+
length?: number;
|
|
40
|
+
}): string;
|
|
41
|
+
convertsJsonAutomatically(): boolean;
|
|
42
|
+
allowsComparingTuples(): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* This is used to narrow the value of Date properties as they will be stored as timestamps in sqlite.
|
|
45
|
+
* We use this method to convert Dates to timestamps when computing the changeset, so we have the right
|
|
46
|
+
* data type in the payload as well as in original entity data. Without that, we would end up with diffs
|
|
47
|
+
* including all Date properties, as we would be comparing Date object with timestamp.
|
|
48
|
+
*/
|
|
49
|
+
processDateProperty(value: unknown): string | number | Date;
|
|
50
|
+
quoteVersionValue(value: Date | number, prop: EntityProperty): Date | string | number;
|
|
51
|
+
quoteValue(value: any): string;
|
|
52
|
+
getIndexName(tableName: string, columns: string[], type: "index" | "unique" | "foreign" | "primary" | "sequence"): string;
|
|
53
|
+
getDefaultPrimaryName(tableName: string, columns: string[]): string;
|
|
54
|
+
supportsDownMigrations(): boolean;
|
|
55
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BetterSqlitePlatform = void 0;
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
const sqlstring_sqlite_1 = require("sqlstring-sqlite");
|
|
6
|
+
const core_1 = require("@yandjin-mikro-orm/core");
|
|
7
|
+
const knex_1 = require("@yandjin-mikro-orm/knex");
|
|
8
|
+
const BetterSqliteSchemaHelper_1 = require("./BetterSqliteSchemaHelper");
|
|
9
|
+
const BetterSqliteExceptionConverter_1 = require("./BetterSqliteExceptionConverter");
|
|
10
|
+
class BetterSqlitePlatform extends knex_1.AbstractSqlPlatform {
|
|
11
|
+
schemaHelper = new BetterSqliteSchemaHelper_1.BetterSqliteSchemaHelper(this);
|
|
12
|
+
exceptionConverter = new BetterSqliteExceptionConverter_1.BetterSqliteExceptionConverter();
|
|
13
|
+
usesDefaultKeyword() {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
usesReturningStatement() {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
getCurrentTimestampSQL(length) {
|
|
20
|
+
return super.getCurrentTimestampSQL(0);
|
|
21
|
+
}
|
|
22
|
+
getDateTimeTypeDeclarationSQL(column) {
|
|
23
|
+
return "datetime";
|
|
24
|
+
}
|
|
25
|
+
getEnumTypeDeclarationSQL(column) {
|
|
26
|
+
if (column.items?.every((item) => core_1.Utils.isString(item))) {
|
|
27
|
+
return "text";
|
|
28
|
+
}
|
|
29
|
+
return this.getTinyIntTypeDeclarationSQL(column);
|
|
30
|
+
}
|
|
31
|
+
getTinyIntTypeDeclarationSQL(column) {
|
|
32
|
+
return this.getIntegerTypeDeclarationSQL(column);
|
|
33
|
+
}
|
|
34
|
+
getSmallIntTypeDeclarationSQL(column) {
|
|
35
|
+
return this.getIntegerTypeDeclarationSQL(column);
|
|
36
|
+
}
|
|
37
|
+
getIntegerTypeDeclarationSQL(column) {
|
|
38
|
+
return "integer";
|
|
39
|
+
}
|
|
40
|
+
getFloatDeclarationSQL() {
|
|
41
|
+
return "real";
|
|
42
|
+
}
|
|
43
|
+
getBooleanTypeDeclarationSQL() {
|
|
44
|
+
return "integer";
|
|
45
|
+
}
|
|
46
|
+
getVarcharTypeDeclarationSQL(column) {
|
|
47
|
+
return "text";
|
|
48
|
+
}
|
|
49
|
+
convertsJsonAutomatically() {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
allowsComparingTuples() {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* This is used to narrow the value of Date properties as they will be stored as timestamps in sqlite.
|
|
57
|
+
* We use this method to convert Dates to timestamps when computing the changeset, so we have the right
|
|
58
|
+
* data type in the payload as well as in original entity data. Without that, we would end up with diffs
|
|
59
|
+
* including all Date properties, as we would be comparing Date object with timestamp.
|
|
60
|
+
*/
|
|
61
|
+
processDateProperty(value) {
|
|
62
|
+
if (value instanceof Date) {
|
|
63
|
+
return +value;
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
quoteVersionValue(value, prop) {
|
|
68
|
+
if (prop.runtimeType === "Date") {
|
|
69
|
+
return (0, sqlstring_sqlite_1.escape)(value, true, this.timezone).replace(/^'|\.\d{3}'$/g, "");
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
quoteValue(value) {
|
|
74
|
+
/* istanbul ignore if */
|
|
75
|
+
if (core_1.Utils.isPlainObject(value) || value?.[core_1.JsonProperty]) {
|
|
76
|
+
return (0, sqlstring_sqlite_1.escape)(JSON.stringify(value), true, this.timezone);
|
|
77
|
+
}
|
|
78
|
+
if (value instanceof Date) {
|
|
79
|
+
return "" + +value;
|
|
80
|
+
}
|
|
81
|
+
return (0, sqlstring_sqlite_1.escape)(value, true, this.timezone);
|
|
82
|
+
}
|
|
83
|
+
getIndexName(tableName, columns, type) {
|
|
84
|
+
if (type === "primary") {
|
|
85
|
+
return this.getDefaultPrimaryName(tableName, columns);
|
|
86
|
+
}
|
|
87
|
+
return super.getIndexName(tableName, columns, type);
|
|
88
|
+
}
|
|
89
|
+
getDefaultPrimaryName(tableName, columns) {
|
|
90
|
+
return "primary";
|
|
91
|
+
}
|
|
92
|
+
supportsDownMigrations() {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.BetterSqlitePlatform = BetterSqlitePlatform;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Connection, Dictionary } from "@yandjin-mikro-orm/core";
|
|
2
|
+
import { SchemaHelper, type AbstractSqlConnection, type IndexDef, type CheckDef } from "@yandjin-mikro-orm/knex";
|
|
3
|
+
export declare class BetterSqliteSchemaHelper extends SchemaHelper {
|
|
4
|
+
disableForeignKeysSQL(): string;
|
|
5
|
+
enableForeignKeysSQL(): string;
|
|
6
|
+
supportsSchemaConstraints(): boolean;
|
|
7
|
+
getListTablesSQL(): string;
|
|
8
|
+
private parseTableDefinition;
|
|
9
|
+
getColumns(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<any[]>;
|
|
10
|
+
getEnumDefinitions(connection: AbstractSqlConnection, checks: CheckDef[], tableName: string, schemaName: string): Promise<Dictionary<string[]>>;
|
|
11
|
+
getPrimaryKeys(connection: AbstractSqlConnection, indexes: IndexDef[], tableName: string, schemaName?: string): Promise<string[]>;
|
|
12
|
+
getIndexes(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<IndexDef[]>;
|
|
13
|
+
getChecks(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<CheckDef[]>;
|
|
14
|
+
getForeignKeysSQL(tableName: string): string;
|
|
15
|
+
mapForeignKeys(fks: any[], tableName: string): Dictionary;
|
|
16
|
+
getManagementDbName(): string;
|
|
17
|
+
getCreateDatabaseSQL(name: string): string;
|
|
18
|
+
databaseExists(connection: Connection, name: string): Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Implicit indexes will be ignored when diffing
|
|
21
|
+
*/
|
|
22
|
+
isImplicitIndex(name: string): boolean;
|
|
23
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BetterSqliteSchemaHelper = void 0;
|
|
4
|
+
const knex_1 = require("@yandjin-mikro-orm/knex");
|
|
5
|
+
class BetterSqliteSchemaHelper extends knex_1.SchemaHelper {
|
|
6
|
+
disableForeignKeysSQL() {
|
|
7
|
+
return "pragma foreign_keys = off;";
|
|
8
|
+
}
|
|
9
|
+
enableForeignKeysSQL() {
|
|
10
|
+
return "pragma foreign_keys = on;";
|
|
11
|
+
}
|
|
12
|
+
supportsSchemaConstraints() {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
getListTablesSQL() {
|
|
16
|
+
return (`select name as table_name from sqlite_master where type = 'table' and name != 'sqlite_sequence' and name != 'geometry_columns' and name != 'spatial_ref_sys' ` +
|
|
17
|
+
`union all select name as table_name from sqlite_temp_master where type = 'table' order by name`);
|
|
18
|
+
}
|
|
19
|
+
parseTableDefinition(sql, cols) {
|
|
20
|
+
const columns = {};
|
|
21
|
+
// extract all columns definitions
|
|
22
|
+
let columnsDef = sql
|
|
23
|
+
.replaceAll("\n", "")
|
|
24
|
+
.match(new RegExp(`create table [\`"']?.*?[\`"']? \\((.*)\\)`, "i"))?.[1];
|
|
25
|
+
/* istanbul ignore else */
|
|
26
|
+
if (columnsDef) {
|
|
27
|
+
for (let i = cols.length - 1; i >= 0; i--) {
|
|
28
|
+
const col = cols[i];
|
|
29
|
+
const re = ` *, *[\`"']?${col.name}[\`"']? (.*)`;
|
|
30
|
+
const columnDef = columnsDef.match(new RegExp(re, "i"));
|
|
31
|
+
/* istanbul ignore else */
|
|
32
|
+
if (columnDef) {
|
|
33
|
+
columns[col.name] = { name: col.name, definition: columnDef[1] };
|
|
34
|
+
columnsDef = columnsDef.substring(0, columnDef.index);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return columns;
|
|
39
|
+
}
|
|
40
|
+
async getColumns(connection, tableName, schemaName) {
|
|
41
|
+
const columns = await connection.execute(`pragma table_xinfo('${tableName}')`);
|
|
42
|
+
const sql = `select sql from sqlite_master where type = ? and name = ?`;
|
|
43
|
+
const tableDefinition = await connection.execute(sql, ["table", tableName], "get");
|
|
44
|
+
const composite = columns.reduce((count, col) => count + (col.pk ? 1 : 0), 0) > 1;
|
|
45
|
+
// there can be only one, so naive check like this should be enough
|
|
46
|
+
const hasAutoincrement = tableDefinition.sql
|
|
47
|
+
.toLowerCase()
|
|
48
|
+
.includes("autoincrement");
|
|
49
|
+
const columnDefinitions = this.parseTableDefinition(tableDefinition.sql, columns);
|
|
50
|
+
return columns.map((col) => {
|
|
51
|
+
const mappedType = connection.getPlatform().getMappedType(col.type);
|
|
52
|
+
let generated;
|
|
53
|
+
if (col.hidden > 1) {
|
|
54
|
+
const storage = col.hidden === 2 ? "virtual" : "stored";
|
|
55
|
+
const re = `(generated always)? as \\((.*)\\)( ${storage})?$`;
|
|
56
|
+
const match = columnDefinitions[col.name].definition.match(re);
|
|
57
|
+
if (match) {
|
|
58
|
+
generated = `${match[2]} ${storage}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
name: col.name,
|
|
63
|
+
type: col.type,
|
|
64
|
+
default: col.dflt_value,
|
|
65
|
+
nullable: !col.notnull,
|
|
66
|
+
primary: !!col.pk,
|
|
67
|
+
mappedType,
|
|
68
|
+
unsigned: false,
|
|
69
|
+
autoincrement: !composite &&
|
|
70
|
+
col.pk &&
|
|
71
|
+
this.platform.isNumericColumn(mappedType) &&
|
|
72
|
+
hasAutoincrement,
|
|
73
|
+
generated,
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async getEnumDefinitions(connection, checks, tableName, schemaName) {
|
|
78
|
+
const sql = `select sql from sqlite_master where type = ? and name = ?`;
|
|
79
|
+
const tableDefinition = await connection.execute(sql, ["table", tableName], "get");
|
|
80
|
+
const checkConstraints = [
|
|
81
|
+
...(tableDefinition.sql.match(/[`["'][^`\]"']+[`\]"'] text check \(.*?\)/gi) ?? []),
|
|
82
|
+
];
|
|
83
|
+
return checkConstraints.reduce((o, item) => {
|
|
84
|
+
// check constraints are defined as (note that last closing paren is missing):
|
|
85
|
+
// `type` text check (`type` in ('local', 'global')
|
|
86
|
+
const match = item.match(/[`["']([^`\]"']+)[`\]"'] text check \(.* \((.*)\)/i);
|
|
87
|
+
/* istanbul ignore else */
|
|
88
|
+
if (match) {
|
|
89
|
+
o[match[1]] = match[2]
|
|
90
|
+
.split(",")
|
|
91
|
+
.map((item) => item.trim().match(/^\(?'(.*)'/)[1]);
|
|
92
|
+
}
|
|
93
|
+
return o;
|
|
94
|
+
}, {});
|
|
95
|
+
}
|
|
96
|
+
async getPrimaryKeys(connection, indexes, tableName, schemaName) {
|
|
97
|
+
const sql = `pragma table_info(\`${tableName}\`)`;
|
|
98
|
+
const cols = await connection.execute(sql);
|
|
99
|
+
return cols.filter((col) => !!col.pk).map((col) => col.name);
|
|
100
|
+
}
|
|
101
|
+
async getIndexes(connection, tableName, schemaName) {
|
|
102
|
+
const sql = `pragma table_info(\`${tableName}\`)`;
|
|
103
|
+
const cols = await connection.execute(sql);
|
|
104
|
+
const indexes = await connection.execute(`pragma index_list(\`${tableName}\`)`);
|
|
105
|
+
const ret = [];
|
|
106
|
+
for (const col of cols.filter((c) => c.pk)) {
|
|
107
|
+
ret.push({
|
|
108
|
+
columnNames: [col.name],
|
|
109
|
+
keyName: "primary",
|
|
110
|
+
constraint: true,
|
|
111
|
+
unique: true,
|
|
112
|
+
primary: true,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
for (const index of indexes.filter((index) => !this.isImplicitIndex(index.name))) {
|
|
116
|
+
const res = await connection.execute(`pragma index_info(\`${index.name}\`)`);
|
|
117
|
+
ret.push(...res.map((row) => ({
|
|
118
|
+
columnNames: [row.name],
|
|
119
|
+
keyName: index.name,
|
|
120
|
+
unique: !!index.unique,
|
|
121
|
+
constraint: !!index.unique,
|
|
122
|
+
primary: false,
|
|
123
|
+
})));
|
|
124
|
+
}
|
|
125
|
+
return this.mapIndexes(ret);
|
|
126
|
+
}
|
|
127
|
+
async getChecks(connection, tableName, schemaName) {
|
|
128
|
+
// Not supported at the moment.
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
getForeignKeysSQL(tableName) {
|
|
132
|
+
return `pragma foreign_key_list(\`${tableName}\`)`;
|
|
133
|
+
}
|
|
134
|
+
mapForeignKeys(fks, tableName) {
|
|
135
|
+
return fks.reduce((ret, fk) => {
|
|
136
|
+
ret[fk.from] = {
|
|
137
|
+
constraintName: this.platform.getIndexName(tableName, [fk.from], "foreign"),
|
|
138
|
+
columnName: fk.from,
|
|
139
|
+
columnNames: [fk.from],
|
|
140
|
+
localTableName: tableName,
|
|
141
|
+
referencedTableName: fk.table,
|
|
142
|
+
referencedColumnName: fk.to,
|
|
143
|
+
referencedColumnNames: [fk.to],
|
|
144
|
+
updateRule: fk.on_update.toLowerCase(),
|
|
145
|
+
deleteRule: fk.on_delete.toLowerCase(),
|
|
146
|
+
};
|
|
147
|
+
return ret;
|
|
148
|
+
}, {});
|
|
149
|
+
}
|
|
150
|
+
getManagementDbName() {
|
|
151
|
+
return "";
|
|
152
|
+
}
|
|
153
|
+
getCreateDatabaseSQL(name) {
|
|
154
|
+
return "";
|
|
155
|
+
}
|
|
156
|
+
async databaseExists(connection, name) {
|
|
157
|
+
const tables = await connection.execute(this.getListTablesSQL());
|
|
158
|
+
return tables.length > 0;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Implicit indexes will be ignored when diffing
|
|
162
|
+
*/
|
|
163
|
+
isImplicitIndex(name) {
|
|
164
|
+
// Ignore indexes with reserved names, e.g. autoindexes
|
|
165
|
+
return name.startsWith("sqlite_");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
exports.BetterSqliteSchemaHelper = BetterSqliteSchemaHelper;
|