@atscript/db-sqlite 0.1.33 → 0.1.35
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/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/index.cjs +363 -196
- package/dist/index.d.ts +17 -2
- package/dist/index.mjs +363 -197
- package/package.json +6 -6
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseDbAdapter } from "@atscript/utils-db";
|
|
1
|
+
import { AtscriptDbView, BaseDbAdapter, DbSpace } from "@atscript/utils-db";
|
|
2
2
|
import { walkFilter } from "@uniqu/core";
|
|
3
3
|
|
|
4
4
|
//#region rolldown:runtime
|
|
@@ -7,6 +7,207 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
7
7
|
throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function.");
|
|
8
8
|
});
|
|
9
9
|
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region packages/db-sqlite/src/better-sqlite3-driver.ts
|
|
12
|
+
function _define_property$1(obj, key, value) {
|
|
13
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
14
|
+
value,
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true
|
|
18
|
+
});
|
|
19
|
+
else obj[key] = value;
|
|
20
|
+
return obj;
|
|
21
|
+
}
|
|
22
|
+
var BetterSqlite3Driver = class {
|
|
23
|
+
run(sql, params) {
|
|
24
|
+
const stmt = this.db.prepare(sql);
|
|
25
|
+
const result = params ? stmt.run(...params) : stmt.run();
|
|
26
|
+
return {
|
|
27
|
+
changes: result.changes,
|
|
28
|
+
lastInsertRowid: result.lastInsertRowid
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
all(sql, params) {
|
|
32
|
+
const stmt = this.db.prepare(sql);
|
|
33
|
+
return params ? stmt.all(...params) : stmt.all();
|
|
34
|
+
}
|
|
35
|
+
get(sql, params) {
|
|
36
|
+
const stmt = this.db.prepare(sql);
|
|
37
|
+
return (params ? stmt.get(...params) : stmt.get()) ?? null;
|
|
38
|
+
}
|
|
39
|
+
exec(sql) {
|
|
40
|
+
this.db.exec(sql);
|
|
41
|
+
}
|
|
42
|
+
close() {
|
|
43
|
+
this.db.close();
|
|
44
|
+
}
|
|
45
|
+
constructor(pathOrDb, options) {
|
|
46
|
+
_define_property$1(this, "db", void 0);
|
|
47
|
+
if (typeof pathOrDb === "string") {
|
|
48
|
+
const Database = __require("better-sqlite3");
|
|
49
|
+
this.db = new Database(pathOrDb, options);
|
|
50
|
+
} else this.db = pathOrDb;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region packages/db-sqlite/src/sql-builder.ts
|
|
56
|
+
function buildInsert(table, data) {
|
|
57
|
+
const keys = Object.keys(data);
|
|
58
|
+
const cols = keys.map((k) => `"${esc(k)}"`).join(", ");
|
|
59
|
+
const placeholders = keys.map(() => "?").join(", ");
|
|
60
|
+
return {
|
|
61
|
+
sql: `INSERT INTO "${esc(table)}" (${cols}) VALUES (${placeholders})`,
|
|
62
|
+
params: keys.map((k) => toSqliteValue(data[k]))
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function buildSelect(table, where, controls) {
|
|
66
|
+
const cols = buildProjection(controls?.$select);
|
|
67
|
+
let sql = `SELECT ${cols} FROM "${esc(table)}" WHERE ${where.sql}`;
|
|
68
|
+
const params = [...where.params];
|
|
69
|
+
if (controls?.$sort) {
|
|
70
|
+
const orderParts = [];
|
|
71
|
+
for (const [col, dir] of Object.entries(controls.$sort)) orderParts.push(`"${esc(col)}" ${dir === -1 ? "DESC" : "ASC"}`);
|
|
72
|
+
if (orderParts.length > 0) sql += ` ORDER BY ${orderParts.join(", ")}`;
|
|
73
|
+
}
|
|
74
|
+
if (controls?.$limit !== undefined) {
|
|
75
|
+
sql += ` LIMIT ?`;
|
|
76
|
+
params.push(controls.$limit);
|
|
77
|
+
}
|
|
78
|
+
if (controls?.$skip !== undefined) {
|
|
79
|
+
if (controls.$limit === undefined) sql += ` LIMIT -1`;
|
|
80
|
+
sql += ` OFFSET ?`;
|
|
81
|
+
params.push(controls.$skip);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
sql,
|
|
85
|
+
params
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function buildUpdate(table, data, where) {
|
|
89
|
+
const setClauses = [];
|
|
90
|
+
const params = [];
|
|
91
|
+
for (const [key, value] of Object.entries(data)) {
|
|
92
|
+
setClauses.push(`"${esc(key)}" = ?`);
|
|
93
|
+
params.push(toSqliteValue(value));
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
sql: `UPDATE "${esc(table)}" SET ${setClauses.join(", ")} WHERE ${where.sql}`,
|
|
97
|
+
params: [...params, ...where.params]
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function buildDelete(table, where) {
|
|
101
|
+
return {
|
|
102
|
+
sql: `DELETE FROM "${esc(table)}" WHERE ${where.sql}`,
|
|
103
|
+
params: [...where.params]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function buildCreateTable(table, fields, foreignKeys) {
|
|
107
|
+
const colDefs = [];
|
|
108
|
+
const primaryKeys = fields.filter((f) => f.isPrimaryKey);
|
|
109
|
+
for (const field of fields) {
|
|
110
|
+
if (field.ignored) continue;
|
|
111
|
+
const sqlType = field.isPrimaryKey && (field.designType === "number" || field.designType === "integer") ? "INTEGER" : sqliteTypeFromDesignType(field.designType);
|
|
112
|
+
let def = `"${esc(field.physicalName)}" ${sqlType}`;
|
|
113
|
+
if (field.isPrimaryKey && primaryKeys.length === 1) def += " PRIMARY KEY";
|
|
114
|
+
if (!field.optional && !field.isPrimaryKey) def += " NOT NULL";
|
|
115
|
+
colDefs.push(def);
|
|
116
|
+
}
|
|
117
|
+
if (primaryKeys.length > 1) {
|
|
118
|
+
const pkCols = primaryKeys.map((pk) => `"${esc(pk.physicalName)}"`).join(", ");
|
|
119
|
+
colDefs.push(`PRIMARY KEY (${pkCols})`);
|
|
120
|
+
}
|
|
121
|
+
if (foreignKeys) for (const fk of foreignKeys.values()) {
|
|
122
|
+
const localCols = fk.fields.map((f) => `"${esc(f)}"`).join(", ");
|
|
123
|
+
const targetCols = fk.targetFields.map((f) => `"${esc(f)}"`).join(", ");
|
|
124
|
+
let constraint = `FOREIGN KEY (${localCols}) REFERENCES "${esc(fk.targetTable)}" (${targetCols})`;
|
|
125
|
+
if (fk.onDelete) constraint += ` ON DELETE ${refActionToSql(fk.onDelete)}`;
|
|
126
|
+
if (fk.onUpdate) constraint += ` ON UPDATE ${refActionToSql(fk.onUpdate)}`;
|
|
127
|
+
colDefs.push(constraint);
|
|
128
|
+
}
|
|
129
|
+
return `CREATE TABLE IF NOT EXISTS "${esc(table)}" (${colDefs.join(", ")})`;
|
|
130
|
+
}
|
|
131
|
+
function refActionToSql(action) {
|
|
132
|
+
switch (action) {
|
|
133
|
+
case "cascade": return "CASCADE";
|
|
134
|
+
case "restrict": return "RESTRICT";
|
|
135
|
+
case "setNull": return "SET NULL";
|
|
136
|
+
case "setDefault": return "SET DEFAULT";
|
|
137
|
+
default: return "NO ACTION";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function sqliteTypeFromDesignType(designType) {
|
|
141
|
+
switch (designType) {
|
|
142
|
+
case "number":
|
|
143
|
+
case "integer": return "REAL";
|
|
144
|
+
case "boolean": return "INTEGER";
|
|
145
|
+
case "string": return "TEXT";
|
|
146
|
+
default: return "TEXT";
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function buildProjection(select) {
|
|
150
|
+
const fields = select?.asArray;
|
|
151
|
+
if (!fields) return "*";
|
|
152
|
+
let sql = "";
|
|
153
|
+
for (let i = 0; i < fields.length; i++) {
|
|
154
|
+
if (i > 0) sql += ", ";
|
|
155
|
+
sql += `"${esc(fields[i])}"`;
|
|
156
|
+
}
|
|
157
|
+
return sql || "*";
|
|
158
|
+
}
|
|
159
|
+
function esc(name) {
|
|
160
|
+
return name.replace(/"/g, "\"\"");
|
|
161
|
+
}
|
|
162
|
+
function toSqliteValue(value) {
|
|
163
|
+
if (value === undefined) return null;
|
|
164
|
+
if (value === null) return null;
|
|
165
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
166
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
function buildCreateView(viewName, plan, columns, resolveFieldRef) {
|
|
170
|
+
const selectCols = columns.map((c) => `"${esc(c.sourceTable)}"."${esc(c.sourceColumn)}" AS "${esc(c.viewColumn)}"`).join(", ");
|
|
171
|
+
let sql = `CREATE VIEW IF NOT EXISTS "${esc(viewName)}" AS SELECT ${selectCols} FROM "${esc(plan.entryTable)}"`;
|
|
172
|
+
for (const join of plan.joins) {
|
|
173
|
+
const onClause = queryNodeToSql(join.condition, resolveFieldRef);
|
|
174
|
+
sql += ` JOIN "${esc(join.targetTable)}" ON ${onClause}`;
|
|
175
|
+
}
|
|
176
|
+
if (plan.filter) {
|
|
177
|
+
const whereClause = queryNodeToSql(plan.filter, resolveFieldRef);
|
|
178
|
+
sql += ` WHERE ${whereClause}`;
|
|
179
|
+
}
|
|
180
|
+
return sql;
|
|
181
|
+
}
|
|
182
|
+
const queryOpToSql = {
|
|
183
|
+
$eq: "=",
|
|
184
|
+
$ne: "!=",
|
|
185
|
+
$gt: ">",
|
|
186
|
+
$gte: ">=",
|
|
187
|
+
$lt: "<",
|
|
188
|
+
$lte: "<="
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Renders an AtscriptQueryNode tree to raw SQL (no parameters — for DDL use only).
|
|
192
|
+
*/ function queryNodeToSql(node, resolveFieldRef) {
|
|
193
|
+
if ("$and" in node) {
|
|
194
|
+
const children = node.$and;
|
|
195
|
+
return children.map((n) => queryNodeToSql(n, resolveFieldRef)).join(" AND ");
|
|
196
|
+
}
|
|
197
|
+
if ("$or" in node) {
|
|
198
|
+
const children = node.$or;
|
|
199
|
+
return `(${children.map((n) => queryNodeToSql(n, resolveFieldRef)).join(" OR ")})`;
|
|
200
|
+
}
|
|
201
|
+
if ("$not" in node) return `NOT (${queryNodeToSql(node.$not, resolveFieldRef)})`;
|
|
202
|
+
const comp = node;
|
|
203
|
+
const leftSql = resolveFieldRef(comp.left);
|
|
204
|
+
const sqlOp = queryOpToSql[comp.op] || "=";
|
|
205
|
+
if (comp.right && typeof comp.right === "object" && "field" in comp.right) return `${leftSql} ${sqlOp} ${resolveFieldRef(comp.right)}`;
|
|
206
|
+
if (comp.right === null || comp.right === undefined) return comp.op === "$ne" ? `${leftSql} IS NOT NULL` : `${leftSql} IS NULL`;
|
|
207
|
+
if (typeof comp.right === "string") return `${leftSql} ${sqlOp} '${comp.right.replace(/'/g, "''")}'`;
|
|
208
|
+
return `${leftSql} ${sqlOp} ${comp.right}`;
|
|
209
|
+
}
|
|
210
|
+
|
|
10
211
|
//#endregion
|
|
11
212
|
//#region packages/db-sqlite/src/filter-builder.ts
|
|
12
213
|
const EMPTY_AND = {
|
|
@@ -22,7 +223,7 @@ const EMPTY_OR = {
|
|
|
22
223
|
* into a parameterized SQL WHERE clause.
|
|
23
224
|
*/ const sqlVisitor = {
|
|
24
225
|
comparison(field, op, value) {
|
|
25
|
-
const col = `"${
|
|
226
|
+
const col = `"${esc(field)}"`;
|
|
26
227
|
const v = toSqliteParam(value);
|
|
27
228
|
switch (op) {
|
|
28
229
|
case "$eq": {
|
|
@@ -119,7 +320,7 @@ const EMPTY_OR = {
|
|
|
119
320
|
};
|
|
120
321
|
function buildWhere(filter) {
|
|
121
322
|
if (!filter || Object.keys(filter).length === 0) return EMPTY_AND;
|
|
122
|
-
return walkFilter(filter, sqlVisitor);
|
|
323
|
+
return walkFilter(filter, sqlVisitor) ?? EMPTY_AND;
|
|
123
324
|
}
|
|
124
325
|
/**
|
|
125
326
|
* Basic regex-to-LIKE conversion.
|
|
@@ -141,12 +342,6 @@ function buildWhere(filter) {
|
|
|
141
342
|
return `%${core}%`;
|
|
142
343
|
}
|
|
143
344
|
/**
|
|
144
|
-
* Escapes a SQL identifier to prevent injection.
|
|
145
|
-
* Doubles any embedded double-quotes.
|
|
146
|
-
*/ function escapeIdent(name) {
|
|
147
|
-
return name.replace(/"/g, "\"\"");
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
345
|
* Converts a JS value to a SQLite-bindable parameter.
|
|
151
346
|
* SQLite cannot bind booleans — they must be 0/1.
|
|
152
347
|
*/ function toSqliteParam(value) {
|
|
@@ -154,111 +349,9 @@ function buildWhere(filter) {
|
|
|
154
349
|
return value;
|
|
155
350
|
}
|
|
156
351
|
|
|
157
|
-
//#endregion
|
|
158
|
-
//#region packages/db-sqlite/src/sql-builder.ts
|
|
159
|
-
function buildInsert(table, data) {
|
|
160
|
-
const keys = Object.keys(data);
|
|
161
|
-
const cols = keys.map((k) => `"${esc$1(k)}"`).join(", ");
|
|
162
|
-
const placeholders = keys.map(() => "?").join(", ");
|
|
163
|
-
return {
|
|
164
|
-
sql: `INSERT INTO "${esc$1(table)}" (${cols}) VALUES (${placeholders})`,
|
|
165
|
-
params: keys.map((k) => toSqliteValue$1(data[k]))
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
function buildSelect(table, where, controls) {
|
|
169
|
-
const cols = buildProjection(controls?.$select);
|
|
170
|
-
let sql = `SELECT ${cols} FROM "${esc$1(table)}" WHERE ${where.sql}`;
|
|
171
|
-
const params = [...where.params];
|
|
172
|
-
if (controls?.$sort) {
|
|
173
|
-
const orderParts = [];
|
|
174
|
-
for (const [col, dir] of Object.entries(controls.$sort)) orderParts.push(`"${esc$1(col)}" ${dir === -1 ? "DESC" : "ASC"}`);
|
|
175
|
-
if (orderParts.length > 0) sql += ` ORDER BY ${orderParts.join(", ")}`;
|
|
176
|
-
}
|
|
177
|
-
if (controls?.$limit !== undefined) {
|
|
178
|
-
sql += ` LIMIT ?`;
|
|
179
|
-
params.push(controls.$limit);
|
|
180
|
-
}
|
|
181
|
-
if (controls?.$skip !== undefined) {
|
|
182
|
-
if (controls.$limit === undefined) sql += ` LIMIT -1`;
|
|
183
|
-
sql += ` OFFSET ?`;
|
|
184
|
-
params.push(controls.$skip);
|
|
185
|
-
}
|
|
186
|
-
return {
|
|
187
|
-
sql,
|
|
188
|
-
params
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
function buildUpdate(table, data, where) {
|
|
192
|
-
const setClauses = [];
|
|
193
|
-
const params = [];
|
|
194
|
-
for (const [key, value] of Object.entries(data)) {
|
|
195
|
-
setClauses.push(`"${esc$1(key)}" = ?`);
|
|
196
|
-
params.push(toSqliteValue$1(value));
|
|
197
|
-
}
|
|
198
|
-
return {
|
|
199
|
-
sql: `UPDATE "${esc$1(table)}" SET ${setClauses.join(", ")} WHERE ${where.sql}`,
|
|
200
|
-
params: [...params, ...where.params]
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
function buildDelete(table, where) {
|
|
204
|
-
return {
|
|
205
|
-
sql: `DELETE FROM "${esc$1(table)}" WHERE ${where.sql}`,
|
|
206
|
-
params: [...where.params]
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
function buildCreateTable(table, fields) {
|
|
210
|
-
const colDefs = [];
|
|
211
|
-
const primaryKeys = fields.filter((f) => f.isPrimaryKey);
|
|
212
|
-
for (const field of fields) {
|
|
213
|
-
if (field.ignored) continue;
|
|
214
|
-
const sqlType = field.isPrimaryKey && (field.designType === "number" || field.designType === "integer") ? "INTEGER" : sqliteTypeFromDesignType(field.designType);
|
|
215
|
-
let def = `"${esc$1(field.physicalName)}" ${sqlType}`;
|
|
216
|
-
if (field.isPrimaryKey && primaryKeys.length === 1) def += " PRIMARY KEY";
|
|
217
|
-
if (!field.optional && !field.isPrimaryKey) def += " NOT NULL";
|
|
218
|
-
colDefs.push(def);
|
|
219
|
-
}
|
|
220
|
-
if (primaryKeys.length > 1) {
|
|
221
|
-
const pkCols = primaryKeys.map((pk) => `"${esc$1(pk.physicalName)}"`).join(", ");
|
|
222
|
-
colDefs.push(`PRIMARY KEY (${pkCols})`);
|
|
223
|
-
}
|
|
224
|
-
return `CREATE TABLE IF NOT EXISTS "${esc$1(table)}" (${colDefs.join(", ")})`;
|
|
225
|
-
}
|
|
226
|
-
function sqliteTypeFromDesignType(designType) {
|
|
227
|
-
switch (designType) {
|
|
228
|
-
case "number":
|
|
229
|
-
case "integer": return "REAL";
|
|
230
|
-
case "boolean": return "INTEGER";
|
|
231
|
-
case "string": return "TEXT";
|
|
232
|
-
default: return "TEXT";
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
function buildProjection(select) {
|
|
236
|
-
const fields = select?.asArray;
|
|
237
|
-
if (!fields) return "*";
|
|
238
|
-
let sql = "";
|
|
239
|
-
for (let i = 0; i < fields.length; i++) {
|
|
240
|
-
if (i > 0) sql += ", ";
|
|
241
|
-
sql += `"${esc$1(fields[i])}"`;
|
|
242
|
-
}
|
|
243
|
-
return sql || "*";
|
|
244
|
-
}
|
|
245
|
-
function esc$1(name) {
|
|
246
|
-
return name.replace(/"/g, "\"\"");
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Converts a JS value to a SQLite-compatible value.
|
|
250
|
-
* Objects and arrays are stored as JSON strings.
|
|
251
|
-
*/ function toSqliteValue$1(value) {
|
|
252
|
-
if (value === undefined) return null;
|
|
253
|
-
if (value === null) return null;
|
|
254
|
-
if (typeof value === "object") return JSON.stringify(value);
|
|
255
|
-
if (typeof value === "boolean") return value ? 1 : 0;
|
|
256
|
-
return value;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
352
|
//#endregion
|
|
260
353
|
//#region packages/db-sqlite/src/sqlite-adapter.ts
|
|
261
|
-
function _define_property
|
|
354
|
+
function _define_property(obj, key, value) {
|
|
262
355
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
263
356
|
value,
|
|
264
357
|
enumerable: true,
|
|
@@ -269,6 +362,19 @@ else obj[key] = value;
|
|
|
269
362
|
return obj;
|
|
270
363
|
}
|
|
271
364
|
var SqliteAdapter = class extends BaseDbAdapter {
|
|
365
|
+
async _beginTransaction() {
|
|
366
|
+
this._log("BEGIN");
|
|
367
|
+
this.driver.exec("BEGIN");
|
|
368
|
+
return undefined;
|
|
369
|
+
}
|
|
370
|
+
async _commitTransaction() {
|
|
371
|
+
this._log("COMMIT");
|
|
372
|
+
this.driver.exec("COMMIT");
|
|
373
|
+
}
|
|
374
|
+
async _rollbackTransaction() {
|
|
375
|
+
this._log("ROLLBACK");
|
|
376
|
+
this.driver.exec("ROLLBACK");
|
|
377
|
+
}
|
|
272
378
|
/** SQLite does not use schemas — override to always exclude schema. */ resolveTableName() {
|
|
273
379
|
return super.resolveTableName(false);
|
|
274
380
|
}
|
|
@@ -277,27 +383,24 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
277
383
|
}
|
|
278
384
|
async insertOne(data) {
|
|
279
385
|
const { sql, params } = buildInsert(this.resolveTableName(), data);
|
|
386
|
+
this._log(sql, params);
|
|
280
387
|
const result = this.driver.run(sql, params);
|
|
281
388
|
return { insertedId: result.lastInsertRowid };
|
|
282
389
|
}
|
|
283
390
|
async insertMany(data) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
try {
|
|
391
|
+
return this.withTransaction(async () => {
|
|
392
|
+
const ids = [];
|
|
287
393
|
for (const row of data) {
|
|
288
394
|
const { sql, params } = buildInsert(this.resolveTableName(), row);
|
|
395
|
+
this._log(sql, params);
|
|
289
396
|
const result = this.driver.run(sql, params);
|
|
290
397
|
ids.push(result.lastInsertRowid);
|
|
291
398
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
return {
|
|
298
|
-
insertedCount: ids.length,
|
|
299
|
-
insertedIds: ids
|
|
300
|
-
};
|
|
399
|
+
return {
|
|
400
|
+
insertedCount: ids.length,
|
|
401
|
+
insertedIds: ids
|
|
402
|
+
};
|
|
403
|
+
});
|
|
301
404
|
}
|
|
302
405
|
async findOne(query) {
|
|
303
406
|
const where = buildWhere(query.filter);
|
|
@@ -306,17 +409,20 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
306
409
|
$limit: 1
|
|
307
410
|
};
|
|
308
411
|
const { sql, params } = buildSelect(this.resolveTableName(), where, controls);
|
|
412
|
+
this._log(sql, params);
|
|
309
413
|
return this.driver.get(sql, params);
|
|
310
414
|
}
|
|
311
415
|
async findMany(query) {
|
|
312
416
|
const where = buildWhere(query.filter);
|
|
313
417
|
const { sql, params } = buildSelect(this.resolveTableName(), where, query.controls);
|
|
418
|
+
this._log(sql, params);
|
|
314
419
|
return this.driver.all(sql, params);
|
|
315
420
|
}
|
|
316
421
|
async count(query) {
|
|
317
422
|
const where = buildWhere(query.filter);
|
|
318
423
|
const tableName = this.resolveTableName();
|
|
319
424
|
const sql = `SELECT COUNT(*) as cnt FROM "${esc(tableName)}" WHERE ${where.sql}`;
|
|
425
|
+
this._log(sql, where.params);
|
|
320
426
|
const row = this.driver.get(sql, where.params);
|
|
321
427
|
return row?.cnt ?? 0;
|
|
322
428
|
}
|
|
@@ -330,7 +436,9 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
330
436
|
setParams.push(toSqliteValue(value));
|
|
331
437
|
}
|
|
332
438
|
const sql = `UPDATE "${esc(tableName)}" SET ${setClauses.join(", ")} WHERE rowid = (SELECT rowid FROM "${esc(tableName)}" WHERE ${where.sql} LIMIT 1)`;
|
|
333
|
-
const
|
|
439
|
+
const allParams = [...setParams, ...where.params];
|
|
440
|
+
this._log(sql, allParams);
|
|
441
|
+
const result = this.driver.run(sql, allParams);
|
|
334
442
|
return {
|
|
335
443
|
matchedCount: result.changes,
|
|
336
444
|
modifiedCount: result.changes
|
|
@@ -339,6 +447,7 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
339
447
|
async updateMany(filter, data) {
|
|
340
448
|
const where = buildWhere(filter);
|
|
341
449
|
const { sql, params } = buildUpdate(this.resolveTableName(), data, where);
|
|
450
|
+
this._log(sql, params);
|
|
342
451
|
const result = this.driver.run(sql, params);
|
|
343
452
|
return {
|
|
344
453
|
matchedCount: result.changes,
|
|
@@ -348,27 +457,22 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
348
457
|
async replaceOne(filter, data) {
|
|
349
458
|
const where = buildWhere(filter);
|
|
350
459
|
const tableName = this.resolveTableName();
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
modifiedCount: delResult.changes
|
|
363
|
-
};
|
|
364
|
-
} catch (error) {
|
|
365
|
-
this.driver.exec("ROLLBACK");
|
|
366
|
-
throw error;
|
|
367
|
-
}
|
|
460
|
+
const limitedWhere = {
|
|
461
|
+
sql: `rowid = (SELECT rowid FROM "${esc(tableName)}" WHERE ${where.sql} LIMIT 1)`,
|
|
462
|
+
params: where.params
|
|
463
|
+
};
|
|
464
|
+
const { sql, params } = buildUpdate(tableName, data, limitedWhere);
|
|
465
|
+
this._log(sql, params);
|
|
466
|
+
const result = this.driver.run(sql, params);
|
|
467
|
+
return {
|
|
468
|
+
matchedCount: result.changes,
|
|
469
|
+
modifiedCount: result.changes
|
|
470
|
+
};
|
|
368
471
|
}
|
|
369
472
|
async replaceMany(filter, data) {
|
|
370
473
|
const where = buildWhere(filter);
|
|
371
474
|
const { sql, params } = buildUpdate(this.resolveTableName(), data, where);
|
|
475
|
+
this._log(sql, params);
|
|
372
476
|
const result = this.driver.run(sql, params);
|
|
373
477
|
return {
|
|
374
478
|
matchedCount: result.changes,
|
|
@@ -379,19 +483,115 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
379
483
|
const where = buildWhere(filter);
|
|
380
484
|
const tableName = this.resolveTableName();
|
|
381
485
|
const sql = `DELETE FROM "${esc(tableName)}" WHERE rowid = (SELECT rowid FROM "${esc(tableName)}" WHERE ${where.sql} LIMIT 1)`;
|
|
486
|
+
this._log(sql, where.params);
|
|
382
487
|
const result = this.driver.run(sql, where.params);
|
|
383
488
|
return { deletedCount: result.changes };
|
|
384
489
|
}
|
|
385
490
|
async deleteMany(filter) {
|
|
386
491
|
const where = buildWhere(filter);
|
|
387
492
|
const { sql, params } = buildDelete(this.resolveTableName(), where);
|
|
493
|
+
this._log(sql, params);
|
|
388
494
|
const result = this.driver.run(sql, params);
|
|
389
495
|
return { deletedCount: result.changes };
|
|
390
496
|
}
|
|
391
497
|
async ensureTable() {
|
|
392
|
-
|
|
498
|
+
if (this._table instanceof AtscriptDbView) return this.ensureView();
|
|
499
|
+
const sql = buildCreateTable(this.resolveTableName(), this._table.fieldDescriptors, this._table.foreignKeys);
|
|
500
|
+
this._log(sql);
|
|
393
501
|
this.driver.exec(sql);
|
|
394
502
|
}
|
|
503
|
+
async ensureView() {
|
|
504
|
+
const view = this._table;
|
|
505
|
+
const sql = buildCreateView(this.resolveTableName(), view.viewPlan, view.getViewColumnMappings(), (ref) => view.resolveFieldRef(ref));
|
|
506
|
+
this._log(sql);
|
|
507
|
+
this.driver.exec(sql);
|
|
508
|
+
}
|
|
509
|
+
async getExistingColumns() {
|
|
510
|
+
return this.getExistingColumnsForTable(this.resolveTableName());
|
|
511
|
+
}
|
|
512
|
+
async syncColumns(diff) {
|
|
513
|
+
const tableName = this.resolveTableName();
|
|
514
|
+
const added = [];
|
|
515
|
+
const renamed = [];
|
|
516
|
+
for (const { field, oldName } of diff.renamed ?? []) {
|
|
517
|
+
const ddl = `ALTER TABLE "${esc(tableName)}" RENAME COLUMN "${esc(oldName)}" TO "${esc(field.physicalName)}"`;
|
|
518
|
+
this._log(ddl);
|
|
519
|
+
this.driver.exec(ddl);
|
|
520
|
+
renamed.push(field.physicalName);
|
|
521
|
+
}
|
|
522
|
+
for (const field of diff.added) {
|
|
523
|
+
const sqlType = field.isPrimaryKey && (field.designType === "number" || field.designType === "integer") ? "INTEGER" : sqliteTypeFromDesignType(field.designType);
|
|
524
|
+
let ddl = `ALTER TABLE "${esc(tableName)}" ADD COLUMN "${esc(field.physicalName)}" ${sqlType}`;
|
|
525
|
+
if (!field.optional && !field.isPrimaryKey) ddl += ` NOT NULL DEFAULT ${defaultValueForType(field.designType)}`;
|
|
526
|
+
this._log(ddl);
|
|
527
|
+
this.driver.exec(ddl);
|
|
528
|
+
added.push(field.physicalName);
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
added,
|
|
532
|
+
renamed
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
async recreateTable() {
|
|
536
|
+
const tableName = this.resolveTableName();
|
|
537
|
+
const tempName = `${tableName}__tmp_${Date.now()}`;
|
|
538
|
+
const createSql = buildCreateTable(tempName, this._table.fieldDescriptors, this._table.foreignKeys);
|
|
539
|
+
this._log(createSql);
|
|
540
|
+
this.driver.exec(createSql);
|
|
541
|
+
const oldCols = (await this.getExistingColumns()).map((c) => c.name);
|
|
542
|
+
const newCols = this._table.fieldDescriptors.filter((f) => !f.ignored).map((f) => f.physicalName);
|
|
543
|
+
const oldColSet = new Set(oldCols);
|
|
544
|
+
const commonCols = newCols.filter((c) => oldColSet.has(c));
|
|
545
|
+
if (commonCols.length > 0) {
|
|
546
|
+
const cols = commonCols.map((c) => `"${esc(c)}"`).join(", ");
|
|
547
|
+
const copySql = `INSERT INTO "${esc(tempName)}" (${cols}) SELECT ${cols} FROM "${esc(tableName)}"`;
|
|
548
|
+
this._log(copySql);
|
|
549
|
+
this.driver.exec(copySql);
|
|
550
|
+
}
|
|
551
|
+
this.driver.exec(`DROP TABLE "${esc(tableName)}"`);
|
|
552
|
+
this.driver.exec(`ALTER TABLE "${esc(tempName)}" RENAME TO "${esc(tableName)}"`);
|
|
553
|
+
}
|
|
554
|
+
async dropTable() {
|
|
555
|
+
const tableName = this.resolveTableName();
|
|
556
|
+
const ddl = `DROP TABLE IF EXISTS "${esc(tableName)}"`;
|
|
557
|
+
this._log(ddl);
|
|
558
|
+
this.driver.exec(ddl);
|
|
559
|
+
}
|
|
560
|
+
async dropColumns(columns) {
|
|
561
|
+
await this.withTransaction(async () => {
|
|
562
|
+
const tableName = this.resolveTableName();
|
|
563
|
+
for (const col of columns) {
|
|
564
|
+
const ddl = `ALTER TABLE "${esc(tableName)}" DROP COLUMN "${esc(col)}"`;
|
|
565
|
+
this._log(ddl);
|
|
566
|
+
this.driver.exec(ddl);
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
async dropTableByName(tableName) {
|
|
571
|
+
const ddl = `DROP TABLE IF EXISTS "${esc(tableName)}"`;
|
|
572
|
+
this._log(ddl);
|
|
573
|
+
this.driver.exec(ddl);
|
|
574
|
+
}
|
|
575
|
+
async dropViewByName(viewName) {
|
|
576
|
+
const ddl = `DROP VIEW IF EXISTS "${esc(viewName)}"`;
|
|
577
|
+
this._log(ddl);
|
|
578
|
+
this.driver.exec(ddl);
|
|
579
|
+
}
|
|
580
|
+
async renameTable(oldName) {
|
|
581
|
+
const newName = this.resolveTableName();
|
|
582
|
+
const ddl = `ALTER TABLE "${esc(oldName)}" RENAME TO "${esc(newName)}"`;
|
|
583
|
+
this._log(ddl);
|
|
584
|
+
this.driver.exec(ddl);
|
|
585
|
+
}
|
|
586
|
+
async getExistingColumnsForTable(tableName) {
|
|
587
|
+
const rows = this.driver.all(`PRAGMA table_info("${esc(tableName)}")`);
|
|
588
|
+
return rows.map((r) => ({
|
|
589
|
+
name: r.name,
|
|
590
|
+
type: r.type,
|
|
591
|
+
notnull: r.notnull === 1,
|
|
592
|
+
pk: r.pk > 0
|
|
593
|
+
}));
|
|
594
|
+
}
|
|
395
595
|
async syncIndexes() {
|
|
396
596
|
const tableName = this.resolveTableName();
|
|
397
597
|
await this.syncIndexesWithDiff({
|
|
@@ -399,72 +599,38 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
399
599
|
createIndex: async (index) => {
|
|
400
600
|
const unique = index.type === "unique" ? "UNIQUE " : "";
|
|
401
601
|
const cols = index.fields.map((f) => `"${esc(f.name)}" ${f.sort === "desc" ? "DESC" : "ASC"}`).join(", ");
|
|
402
|
-
|
|
602
|
+
const sql = `CREATE ${unique}INDEX IF NOT EXISTS "${esc(index.key)}" ON "${esc(tableName)}" (${cols})`;
|
|
603
|
+
this._log(sql);
|
|
604
|
+
this.driver.exec(sql);
|
|
403
605
|
},
|
|
404
606
|
dropIndex: async (name) => {
|
|
405
|
-
|
|
607
|
+
const sql = `DROP INDEX IF EXISTS "${esc(name)}"`;
|
|
608
|
+
this._log(sql);
|
|
609
|
+
this.driver.exec(sql);
|
|
406
610
|
},
|
|
407
611
|
shouldSkipType: (type) => type === "fulltext"
|
|
408
612
|
});
|
|
409
613
|
}
|
|
410
614
|
constructor(driver) {
|
|
411
|
-
super(), _define_property
|
|
615
|
+
super(), _define_property(this, "driver", void 0), this.driver = driver;
|
|
616
|
+
this.driver.exec("PRAGMA foreign_keys = ON");
|
|
412
617
|
}
|
|
413
618
|
};
|
|
414
|
-
function
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if (typeof value === "boolean") return value ? 1 : 0;
|
|
422
|
-
return value;
|
|
619
|
+
/** Returns a safe SQLite DEFAULT literal for a given design type. */ function defaultValueForType(designType) {
|
|
620
|
+
switch (designType) {
|
|
621
|
+
case "number":
|
|
622
|
+
case "integer": return "0";
|
|
623
|
+
case "boolean": return "0";
|
|
624
|
+
default: return "''";
|
|
625
|
+
}
|
|
423
626
|
}
|
|
424
627
|
|
|
425
628
|
//#endregion
|
|
426
|
-
//#region packages/db-sqlite/src/
|
|
427
|
-
function
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
enumerable: true,
|
|
431
|
-
configurable: true,
|
|
432
|
-
writable: true
|
|
433
|
-
});
|
|
434
|
-
else obj[key] = value;
|
|
435
|
-
return obj;
|
|
629
|
+
//#region packages/db-sqlite/src/index.ts
|
|
630
|
+
function createAdapter(connection, options) {
|
|
631
|
+
const driver = new BetterSqlite3Driver(connection, options);
|
|
632
|
+
return new DbSpace(() => new SqliteAdapter(driver));
|
|
436
633
|
}
|
|
437
|
-
var BetterSqlite3Driver = class {
|
|
438
|
-
run(sql, params) {
|
|
439
|
-
const stmt = this.db.prepare(sql);
|
|
440
|
-
const result = params ? stmt.run(...params) : stmt.run();
|
|
441
|
-
return {
|
|
442
|
-
changes: result.changes,
|
|
443
|
-
lastInsertRowid: result.lastInsertRowid
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
all(sql, params) {
|
|
447
|
-
const stmt = this.db.prepare(sql);
|
|
448
|
-
return params ? stmt.all(...params) : stmt.all();
|
|
449
|
-
}
|
|
450
|
-
get(sql, params) {
|
|
451
|
-
const stmt = this.db.prepare(sql);
|
|
452
|
-
return (params ? stmt.get(...params) : stmt.get()) ?? null;
|
|
453
|
-
}
|
|
454
|
-
exec(sql) {
|
|
455
|
-
this.db.exec(sql);
|
|
456
|
-
}
|
|
457
|
-
close() {
|
|
458
|
-
this.db.close();
|
|
459
|
-
}
|
|
460
|
-
constructor(pathOrDb, options) {
|
|
461
|
-
_define_property(this, "db", void 0);
|
|
462
|
-
if (typeof pathOrDb === "string") {
|
|
463
|
-
const Database = __require("better-sqlite3");
|
|
464
|
-
this.db = new Database(pathOrDb, options);
|
|
465
|
-
} else this.db = pathOrDb;
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
634
|
|
|
469
635
|
//#endregion
|
|
470
|
-
export { BetterSqlite3Driver, SqliteAdapter, buildWhere };
|
|
636
|
+
export { BetterSqlite3Driver, SqliteAdapter, buildWhere, createAdapter };
|