@atscript/db-sqlite 0.1.34 → 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 +362 -213
- package/dist/index.d.ts +17 -2
- package/dist/index.mjs +362 -214
- 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,128 +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, foreignKeys) {
|
|
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
|
-
if (foreignKeys) for (const fk of foreignKeys.values()) {
|
|
225
|
-
const localCols = fk.fields.map((f) => `"${esc$1(f)}"`).join(", ");
|
|
226
|
-
const targetCols = fk.targetFields.map((f) => `"${esc$1(f)}"`).join(", ");
|
|
227
|
-
let constraint = `FOREIGN KEY (${localCols}) REFERENCES "${esc$1(fk.targetTable)}" (${targetCols})`;
|
|
228
|
-
if (fk.onDelete) constraint += ` ON DELETE ${refActionToSql(fk.onDelete)}`;
|
|
229
|
-
if (fk.onUpdate) constraint += ` ON UPDATE ${refActionToSql(fk.onUpdate)}`;
|
|
230
|
-
colDefs.push(constraint);
|
|
231
|
-
}
|
|
232
|
-
return `CREATE TABLE IF NOT EXISTS "${esc$1(table)}" (${colDefs.join(", ")})`;
|
|
233
|
-
}
|
|
234
|
-
function refActionToSql(action) {
|
|
235
|
-
switch (action) {
|
|
236
|
-
case "cascade": return "CASCADE";
|
|
237
|
-
case "restrict": return "RESTRICT";
|
|
238
|
-
case "setNull": return "SET NULL";
|
|
239
|
-
case "setDefault": return "SET DEFAULT";
|
|
240
|
-
default: return "NO ACTION";
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
function sqliteTypeFromDesignType(designType) {
|
|
244
|
-
switch (designType) {
|
|
245
|
-
case "number":
|
|
246
|
-
case "integer": return "REAL";
|
|
247
|
-
case "boolean": return "INTEGER";
|
|
248
|
-
case "string": return "TEXT";
|
|
249
|
-
default: return "TEXT";
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
function buildProjection(select) {
|
|
253
|
-
const fields = select?.asArray;
|
|
254
|
-
if (!fields) return "*";
|
|
255
|
-
let sql = "";
|
|
256
|
-
for (let i = 0; i < fields.length; i++) {
|
|
257
|
-
if (i > 0) sql += ", ";
|
|
258
|
-
sql += `"${esc$1(fields[i])}"`;
|
|
259
|
-
}
|
|
260
|
-
return sql || "*";
|
|
261
|
-
}
|
|
262
|
-
function esc$1(name) {
|
|
263
|
-
return name.replace(/"/g, "\"\"");
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Converts a JS value to a SQLite-compatible value.
|
|
267
|
-
* Objects and arrays are stored as JSON strings.
|
|
268
|
-
*/ function toSqliteValue$1(value) {
|
|
269
|
-
if (value === undefined) return null;
|
|
270
|
-
if (value === null) return null;
|
|
271
|
-
if (typeof value === "object") return JSON.stringify(value);
|
|
272
|
-
if (typeof value === "boolean") return value ? 1 : 0;
|
|
273
|
-
return value;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
352
|
//#endregion
|
|
277
353
|
//#region packages/db-sqlite/src/sqlite-adapter.ts
|
|
278
|
-
function _define_property
|
|
354
|
+
function _define_property(obj, key, value) {
|
|
279
355
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
280
356
|
value,
|
|
281
357
|
enumerable: true,
|
|
@@ -286,6 +362,19 @@ else obj[key] = value;
|
|
|
286
362
|
return obj;
|
|
287
363
|
}
|
|
288
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
|
+
}
|
|
289
378
|
/** SQLite does not use schemas — override to always exclude schema. */ resolveTableName() {
|
|
290
379
|
return super.resolveTableName(false);
|
|
291
380
|
}
|
|
@@ -294,27 +383,24 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
294
383
|
}
|
|
295
384
|
async insertOne(data) {
|
|
296
385
|
const { sql, params } = buildInsert(this.resolveTableName(), data);
|
|
386
|
+
this._log(sql, params);
|
|
297
387
|
const result = this.driver.run(sql, params);
|
|
298
388
|
return { insertedId: result.lastInsertRowid };
|
|
299
389
|
}
|
|
300
390
|
async insertMany(data) {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
try {
|
|
391
|
+
return this.withTransaction(async () => {
|
|
392
|
+
const ids = [];
|
|
304
393
|
for (const row of data) {
|
|
305
394
|
const { sql, params } = buildInsert(this.resolveTableName(), row);
|
|
395
|
+
this._log(sql, params);
|
|
306
396
|
const result = this.driver.run(sql, params);
|
|
307
397
|
ids.push(result.lastInsertRowid);
|
|
308
398
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
return {
|
|
315
|
-
insertedCount: ids.length,
|
|
316
|
-
insertedIds: ids
|
|
317
|
-
};
|
|
399
|
+
return {
|
|
400
|
+
insertedCount: ids.length,
|
|
401
|
+
insertedIds: ids
|
|
402
|
+
};
|
|
403
|
+
});
|
|
318
404
|
}
|
|
319
405
|
async findOne(query) {
|
|
320
406
|
const where = buildWhere(query.filter);
|
|
@@ -323,17 +409,20 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
323
409
|
$limit: 1
|
|
324
410
|
};
|
|
325
411
|
const { sql, params } = buildSelect(this.resolveTableName(), where, controls);
|
|
412
|
+
this._log(sql, params);
|
|
326
413
|
return this.driver.get(sql, params);
|
|
327
414
|
}
|
|
328
415
|
async findMany(query) {
|
|
329
416
|
const where = buildWhere(query.filter);
|
|
330
417
|
const { sql, params } = buildSelect(this.resolveTableName(), where, query.controls);
|
|
418
|
+
this._log(sql, params);
|
|
331
419
|
return this.driver.all(sql, params);
|
|
332
420
|
}
|
|
333
421
|
async count(query) {
|
|
334
422
|
const where = buildWhere(query.filter);
|
|
335
423
|
const tableName = this.resolveTableName();
|
|
336
424
|
const sql = `SELECT COUNT(*) as cnt FROM "${esc(tableName)}" WHERE ${where.sql}`;
|
|
425
|
+
this._log(sql, where.params);
|
|
337
426
|
const row = this.driver.get(sql, where.params);
|
|
338
427
|
return row?.cnt ?? 0;
|
|
339
428
|
}
|
|
@@ -347,7 +436,9 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
347
436
|
setParams.push(toSqliteValue(value));
|
|
348
437
|
}
|
|
349
438
|
const sql = `UPDATE "${esc(tableName)}" SET ${setClauses.join(", ")} WHERE rowid = (SELECT rowid FROM "${esc(tableName)}" WHERE ${where.sql} LIMIT 1)`;
|
|
350
|
-
const
|
|
439
|
+
const allParams = [...setParams, ...where.params];
|
|
440
|
+
this._log(sql, allParams);
|
|
441
|
+
const result = this.driver.run(sql, allParams);
|
|
351
442
|
return {
|
|
352
443
|
matchedCount: result.changes,
|
|
353
444
|
modifiedCount: result.changes
|
|
@@ -356,6 +447,7 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
356
447
|
async updateMany(filter, data) {
|
|
357
448
|
const where = buildWhere(filter);
|
|
358
449
|
const { sql, params } = buildUpdate(this.resolveTableName(), data, where);
|
|
450
|
+
this._log(sql, params);
|
|
359
451
|
const result = this.driver.run(sql, params);
|
|
360
452
|
return {
|
|
361
453
|
matchedCount: result.changes,
|
|
@@ -365,27 +457,22 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
365
457
|
async replaceOne(filter, data) {
|
|
366
458
|
const where = buildWhere(filter);
|
|
367
459
|
const tableName = this.resolveTableName();
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
modifiedCount: delResult.changes
|
|
380
|
-
};
|
|
381
|
-
} catch (error) {
|
|
382
|
-
this.driver.exec("ROLLBACK");
|
|
383
|
-
throw error;
|
|
384
|
-
}
|
|
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
|
+
};
|
|
385
471
|
}
|
|
386
472
|
async replaceMany(filter, data) {
|
|
387
473
|
const where = buildWhere(filter);
|
|
388
474
|
const { sql, params } = buildUpdate(this.resolveTableName(), data, where);
|
|
475
|
+
this._log(sql, params);
|
|
389
476
|
const result = this.driver.run(sql, params);
|
|
390
477
|
return {
|
|
391
478
|
matchedCount: result.changes,
|
|
@@ -396,19 +483,115 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
396
483
|
const where = buildWhere(filter);
|
|
397
484
|
const tableName = this.resolveTableName();
|
|
398
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);
|
|
399
487
|
const result = this.driver.run(sql, where.params);
|
|
400
488
|
return { deletedCount: result.changes };
|
|
401
489
|
}
|
|
402
490
|
async deleteMany(filter) {
|
|
403
491
|
const where = buildWhere(filter);
|
|
404
492
|
const { sql, params } = buildDelete(this.resolveTableName(), where);
|
|
493
|
+
this._log(sql, params);
|
|
405
494
|
const result = this.driver.run(sql, params);
|
|
406
495
|
return { deletedCount: result.changes };
|
|
407
496
|
}
|
|
408
497
|
async ensureTable() {
|
|
498
|
+
if (this._table instanceof AtscriptDbView) return this.ensureView();
|
|
409
499
|
const sql = buildCreateTable(this.resolveTableName(), this._table.fieldDescriptors, this._table.foreignKeys);
|
|
500
|
+
this._log(sql);
|
|
410
501
|
this.driver.exec(sql);
|
|
411
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
|
+
}
|
|
412
595
|
async syncIndexes() {
|
|
413
596
|
const tableName = this.resolveTableName();
|
|
414
597
|
await this.syncIndexesWithDiff({
|
|
@@ -416,73 +599,38 @@ var SqliteAdapter = class extends BaseDbAdapter {
|
|
|
416
599
|
createIndex: async (index) => {
|
|
417
600
|
const unique = index.type === "unique" ? "UNIQUE " : "";
|
|
418
601
|
const cols = index.fields.map((f) => `"${esc(f.name)}" ${f.sort === "desc" ? "DESC" : "ASC"}`).join(", ");
|
|
419
|
-
|
|
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);
|
|
420
605
|
},
|
|
421
606
|
dropIndex: async (name) => {
|
|
422
|
-
|
|
607
|
+
const sql = `DROP INDEX IF EXISTS "${esc(name)}"`;
|
|
608
|
+
this._log(sql);
|
|
609
|
+
this.driver.exec(sql);
|
|
423
610
|
},
|
|
424
611
|
shouldSkipType: (type) => type === "fulltext"
|
|
425
612
|
});
|
|
426
613
|
}
|
|
427
614
|
constructor(driver) {
|
|
428
|
-
super(), _define_property
|
|
429
|
-
driver.exec("PRAGMA foreign_keys = ON");
|
|
615
|
+
super(), _define_property(this, "driver", void 0), this.driver = driver;
|
|
616
|
+
this.driver.exec("PRAGMA foreign_keys = ON");
|
|
430
617
|
}
|
|
431
618
|
};
|
|
432
|
-
function
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
if (typeof value === "boolean") return value ? 1 : 0;
|
|
440
|
-
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
|
+
}
|
|
441
626
|
}
|
|
442
627
|
|
|
443
628
|
//#endregion
|
|
444
|
-
//#region packages/db-sqlite/src/
|
|
445
|
-
function
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
enumerable: true,
|
|
449
|
-
configurable: true,
|
|
450
|
-
writable: true
|
|
451
|
-
});
|
|
452
|
-
else obj[key] = value;
|
|
453
|
-
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));
|
|
454
633
|
}
|
|
455
|
-
var BetterSqlite3Driver = class {
|
|
456
|
-
run(sql, params) {
|
|
457
|
-
const stmt = this.db.prepare(sql);
|
|
458
|
-
const result = params ? stmt.run(...params) : stmt.run();
|
|
459
|
-
return {
|
|
460
|
-
changes: result.changes,
|
|
461
|
-
lastInsertRowid: result.lastInsertRowid
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
all(sql, params) {
|
|
465
|
-
const stmt = this.db.prepare(sql);
|
|
466
|
-
return params ? stmt.all(...params) : stmt.all();
|
|
467
|
-
}
|
|
468
|
-
get(sql, params) {
|
|
469
|
-
const stmt = this.db.prepare(sql);
|
|
470
|
-
return (params ? stmt.get(...params) : stmt.get()) ?? null;
|
|
471
|
-
}
|
|
472
|
-
exec(sql) {
|
|
473
|
-
this.db.exec(sql);
|
|
474
|
-
}
|
|
475
|
-
close() {
|
|
476
|
-
this.db.close();
|
|
477
|
-
}
|
|
478
|
-
constructor(pathOrDb, options) {
|
|
479
|
-
_define_property(this, "db", void 0);
|
|
480
|
-
if (typeof pathOrDb === "string") {
|
|
481
|
-
const Database = __require("better-sqlite3");
|
|
482
|
-
this.db = new Database(pathOrDb, options);
|
|
483
|
-
} else this.db = pathOrDb;
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
634
|
|
|
487
635
|
//#endregion
|
|
488
|
-
export { BetterSqlite3Driver, SqliteAdapter, buildWhere };
|
|
636
|
+
export { BetterSqlite3Driver, SqliteAdapter, buildWhere, createAdapter };
|