@revojs/storage 0.1.58
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/dist/commands/generate.d.ts +8 -0
- package/dist/commands/generate.js +48 -0
- package/dist/index.d.ts +360 -0
- package/dist/index.js +842 -0
- package/dist/module/index.d.ts +3 -0
- package/dist/module/index.js +19 -0
- package/dist/plugins/index.d.ts +4 -0
- package/dist/plugins/index.js +15 -0
- package/dist/protocols/mysql.d.ts +175 -0
- package/dist/protocols/mysql.js +327 -0
- package/dist/protocols/postgresql.d.ts +204 -0
- package/dist/protocols/postgresql.js +373 -0
- package/dist/protocols/sqlite.d.ts +96 -0
- package/dist/protocols/sqlite.js +217 -0
- package/dist/providers/bun-sqlite.d.ts +18 -0
- package/dist/providers/bun-sqlite.js +37 -0
- package/dist/providers/d1.d.ts +18 -0
- package/dist/providers/d1.js +37 -0
- package/dist/types/index.d.ts +14 -0
- package/package.json +48 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,842 @@
|
|
|
1
|
+
import { defineContext, getContext, setContext } from "revojs";
|
|
2
|
+
//#region src/index.ts
|
|
3
|
+
var Parameter = class {
|
|
4
|
+
value;
|
|
5
|
+
constructor(value) {
|
|
6
|
+
this.value = value;
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
var Sql = class Sql {
|
|
10
|
+
$chunks;
|
|
11
|
+
constructor(...chunks) {
|
|
12
|
+
this.$chunks = chunks;
|
|
13
|
+
}
|
|
14
|
+
push(...chunks) {
|
|
15
|
+
chunks.forEach((chunk) => {
|
|
16
|
+
if (chunk instanceof Sql && this.$chunks.at(-1) instanceof Sql) this.$chunks.push(" ");
|
|
17
|
+
this.$chunks.push(chunk);
|
|
18
|
+
});
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
toQuery() {
|
|
22
|
+
const queries = this.$chunks.map((chunk) => {
|
|
23
|
+
if (typeof chunk === "string") return {
|
|
24
|
+
sql: chunk,
|
|
25
|
+
parameters: []
|
|
26
|
+
};
|
|
27
|
+
if (chunk instanceof Parameter) return {
|
|
28
|
+
sql: "?",
|
|
29
|
+
parameters: [chunk.value]
|
|
30
|
+
};
|
|
31
|
+
if (chunk instanceof Sql || chunk instanceof Table || chunk instanceof Column) return chunk.toSql().toQuery();
|
|
32
|
+
return {
|
|
33
|
+
sql: "?",
|
|
34
|
+
parameters: [chunk]
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
sql: queries.map((query) => query.sql).join(""),
|
|
39
|
+
parameters: queries.flatMap((query) => query.parameters)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
toSql() {
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var Statement = class extends Sql {
|
|
47
|
+
$session;
|
|
48
|
+
$fields;
|
|
49
|
+
constructor(session, value, ...chunks) {
|
|
50
|
+
super(...chunks);
|
|
51
|
+
this.$session = session;
|
|
52
|
+
this.$fields = Object.entries(value instanceof Table ? value.$columns : value);
|
|
53
|
+
}
|
|
54
|
+
async then(onfulfilled, onrejected) {
|
|
55
|
+
let value;
|
|
56
|
+
try {
|
|
57
|
+
value = (await this.$session.all(this.toQuery())).map((row) => this.mapRow(row));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (onrejected) return onrejected(error);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
return onfulfilled ? onfulfilled(value) : value;
|
|
63
|
+
}
|
|
64
|
+
mapRow(row) {
|
|
65
|
+
const result = {};
|
|
66
|
+
this.$fields.forEach(([key, column], index) => {
|
|
67
|
+
const value = row[index];
|
|
68
|
+
result[key] = value === null ? null : column.fromDriverValue(value);
|
|
69
|
+
});
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var SelectStatement = class extends Statement {
|
|
74
|
+
$value;
|
|
75
|
+
$selection;
|
|
76
|
+
$baseNullable = false;
|
|
77
|
+
$joins = new Array();
|
|
78
|
+
constructor(session, value) {
|
|
79
|
+
super(session, value);
|
|
80
|
+
this.$value = value;
|
|
81
|
+
this.$selection = combine(this.$fields.map(([, column]) => column), ", ");
|
|
82
|
+
this.push(sql`SELECT ${this.$selection}`);
|
|
83
|
+
}
|
|
84
|
+
from(table) {
|
|
85
|
+
this.push(sql`FROM ${table}`);
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
leftJoin(table, on) {
|
|
89
|
+
this.push(sql`LEFT JOIN ${table} ON ${on}`);
|
|
90
|
+
if (this.$value instanceof Table) {
|
|
91
|
+
this.$selection.push(", ", combine(Object.values(table.$columns), ", "));
|
|
92
|
+
this.$joins.push({
|
|
93
|
+
table,
|
|
94
|
+
nullable: true
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
rightJoin(table, on) {
|
|
100
|
+
this.push(sql`RIGHT JOIN ${table} ON ${on}`);
|
|
101
|
+
if (this.$value instanceof Table) {
|
|
102
|
+
this.$selection.push(", ", combine(Object.values(table.$columns), ", "));
|
|
103
|
+
this.$baseNullable = true;
|
|
104
|
+
this.$joins.forEach((join) => join.nullable = true);
|
|
105
|
+
this.$joins.push({
|
|
106
|
+
table,
|
|
107
|
+
nullable: false
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
innerJoin(table, on) {
|
|
113
|
+
this.push(sql`INNER JOIN ${table} ON ${on}`);
|
|
114
|
+
if (this.$value instanceof Table) {
|
|
115
|
+
this.$selection.push(", ", combine(Object.values(table.$columns), ", "));
|
|
116
|
+
this.$joins.push({
|
|
117
|
+
table,
|
|
118
|
+
nullable: false
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
crossJoin(table) {
|
|
124
|
+
this.push(sql`CROSS JOIN ${table}`);
|
|
125
|
+
if (this.$value instanceof Table) {
|
|
126
|
+
this.$selection.push(", ", combine(Object.values(table.$columns), ", "));
|
|
127
|
+
this.$joins.push({
|
|
128
|
+
table,
|
|
129
|
+
nullable: false
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
where(chunk) {
|
|
135
|
+
this.push(sql`WHERE ${chunk}`);
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
groupBy(...columns) {
|
|
139
|
+
this.push(sql`GROUP BY ${combine(columns, ", ")}`);
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
having(chunk) {
|
|
143
|
+
this.push(sql`HAVING ${chunk}`);
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
orderBy(...chunks) {
|
|
147
|
+
this.push(sql`ORDER BY ${combine(chunks, ", ")}`);
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
limit(count) {
|
|
151
|
+
this.push(sql`LIMIT ${new Parameter(count)}`);
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
offset(count) {
|
|
155
|
+
this.push(sql`OFFSET ${new Parameter(count)}`);
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
mapRow(row) {
|
|
159
|
+
if (!(this.$value instanceof Table) || this.$joins.length === 0) return super.mapRow(row);
|
|
160
|
+
const result = {};
|
|
161
|
+
let offset = 0;
|
|
162
|
+
for (const { table, nullable } of [{
|
|
163
|
+
table: this.$value,
|
|
164
|
+
nullable: this.$baseNullable
|
|
165
|
+
}, ...this.$joins]) {
|
|
166
|
+
const fields = Object.entries(table.$columns);
|
|
167
|
+
const values = row.slice(offset, offset + fields.length);
|
|
168
|
+
if (nullable && values.every((value) => value === null)) {
|
|
169
|
+
result[table.$name] = null;
|
|
170
|
+
offset += fields.length;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
result[table.$name] = Object.fromEntries(fields.map(([key, column], index) => [key, values[index] === null ? null : column.fromDriverValue(values[index])]));
|
|
174
|
+
offset += fields.length;
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
var InsertStatement = class extends Statement {
|
|
180
|
+
$table;
|
|
181
|
+
constructor(session, table) {
|
|
182
|
+
super(session, table, sql`INSERT INTO ${table}`);
|
|
183
|
+
this.$table = table;
|
|
184
|
+
}
|
|
185
|
+
values(value) {
|
|
186
|
+
const columns = this.$table.$columns;
|
|
187
|
+
const rows = Array.isArray(value) ? value : [value];
|
|
188
|
+
const keys = Object.entries(columns).filter(([key, column]) => column.$defaultValue !== void 0 || rows.some((row) => Object.hasOwn(row, key))).map(([key]) => key);
|
|
189
|
+
this.push(sql`(${combine(keys.map((key) => columns[key].toSqlName()), ", ")}) VALUES ${combine(rows.map((row) => sql`(${combine(keys.map((key) => {
|
|
190
|
+
const column = columns[key];
|
|
191
|
+
const rowValue = row[key];
|
|
192
|
+
if (rowValue !== void 0) return toParameter(column, rowValue);
|
|
193
|
+
return toParameter(column, typeof column.$defaultValue === "function" ? column.$defaultValue() : column.$defaultValue ?? null);
|
|
194
|
+
}), ", ")})`), ", ")}`);
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
onConflictDoNothing() {
|
|
198
|
+
this.push(sql`ON CONFLICT DO NOTHING`);
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
returning() {
|
|
202
|
+
this.push(sql`RETURNING ${combine(this.$fields.map(([, column]) => column.toSqlName()), ", ")}`);
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
var UpdateStatement = class extends Statement {
|
|
207
|
+
$table;
|
|
208
|
+
constructor(session, table) {
|
|
209
|
+
super(session, table, sql`UPDATE ${table} SET`);
|
|
210
|
+
this.$table = table;
|
|
211
|
+
}
|
|
212
|
+
set(value) {
|
|
213
|
+
const columns = this.$table.$columns;
|
|
214
|
+
this.push(combine(Object.entries(value).map(([key, chunk]) => sql`${columns[key].toSqlName()} = ${toParameter(columns[key], chunk ?? null)}`), ", "));
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
where(chunk) {
|
|
218
|
+
this.push(sql`WHERE ${chunk}`);
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
returning() {
|
|
222
|
+
this.push(sql`RETURNING ${combine(this.$fields.map(([, column]) => column.toSqlName()), ", ")}`);
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
var DeleteStatement = class extends Statement {
|
|
227
|
+
$table;
|
|
228
|
+
constructor(session, table) {
|
|
229
|
+
super(session, table, sql`DELETE FROM ${table}`);
|
|
230
|
+
this.$table = table;
|
|
231
|
+
}
|
|
232
|
+
where(chunk) {
|
|
233
|
+
this.push(sql`WHERE ${chunk}`);
|
|
234
|
+
return this;
|
|
235
|
+
}
|
|
236
|
+
returning() {
|
|
237
|
+
this.push(sql`RETURNING ${combine(this.$fields.map(([, column]) => column.toSqlName()), ", ")}`);
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
var PreparedQuery = class {
|
|
242
|
+
$query;
|
|
243
|
+
constructor(query) {
|
|
244
|
+
this.$query = query;
|
|
245
|
+
}
|
|
246
|
+
getQuery() {
|
|
247
|
+
return this.$query;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
var Session = class {
|
|
251
|
+
run(query) {
|
|
252
|
+
return this.prepareQuery(query).run(query.parameters);
|
|
253
|
+
}
|
|
254
|
+
all(query) {
|
|
255
|
+
return this.prepareQuery(query).all(query.parameters);
|
|
256
|
+
}
|
|
257
|
+
get(query) {
|
|
258
|
+
return this.prepareQuery(query).get(query.parameters);
|
|
259
|
+
}
|
|
260
|
+
values(query) {
|
|
261
|
+
return this.prepareQuery(query).values(query.parameters);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
var Migration = class {
|
|
265
|
+
toSql(changes, current) {
|
|
266
|
+
if (current.dialect !== this.$dialect) throw new Error(`Unable to create ${this.$dialect} migration for ${current.dialect} metadata`);
|
|
267
|
+
return this.render({
|
|
268
|
+
changes,
|
|
269
|
+
current
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
render({ changes }) {
|
|
273
|
+
return changes.flatMap((change) => this.renderChange(change));
|
|
274
|
+
}
|
|
275
|
+
diff(previous, current) {
|
|
276
|
+
if (previous && previous.dialect !== current.dialect) throw new Error(`Storage dialect changed from "${previous.dialect}" to "${current.dialect}"`);
|
|
277
|
+
const changes = new Array();
|
|
278
|
+
const previousTables = new Map((previous?.tables ?? []).map((table) => [table.name, table]));
|
|
279
|
+
const currentTables = new Map(current.tables.map((table) => [table.name, table]));
|
|
280
|
+
for (const [name, table] of currentTables) {
|
|
281
|
+
const previousTable = previousTables.get(name);
|
|
282
|
+
if (!previousTable) {
|
|
283
|
+
changes.push({
|
|
284
|
+
kind: "createTable",
|
|
285
|
+
table
|
|
286
|
+
});
|
|
287
|
+
for (const definition of table.definitions) if (definition.kind === "index" || definition.kind === "uniqueIndex") changes.push({
|
|
288
|
+
kind: "createIndex",
|
|
289
|
+
table: table.name,
|
|
290
|
+
index: definition
|
|
291
|
+
});
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const previousDefinitions = this.splitDefinitions(previousTable.definitions);
|
|
295
|
+
const currentDefinitions = this.splitDefinitions(table.definitions);
|
|
296
|
+
const previousColumns = new Map(Object.entries(previousTable.columns));
|
|
297
|
+
const currentColumns = new Map(Object.entries(table.columns));
|
|
298
|
+
changes.push(...this.diffValues(previousColumns, currentColumns, {
|
|
299
|
+
create: (column) => ({
|
|
300
|
+
kind: "createColumn",
|
|
301
|
+
table: name,
|
|
302
|
+
column
|
|
303
|
+
}),
|
|
304
|
+
update: (previous, column) => ({
|
|
305
|
+
kind: "updateColumn",
|
|
306
|
+
table: name,
|
|
307
|
+
before: previous,
|
|
308
|
+
after: column
|
|
309
|
+
}),
|
|
310
|
+
delete: (column) => ({
|
|
311
|
+
kind: "deleteColumn",
|
|
312
|
+
table: name,
|
|
313
|
+
column
|
|
314
|
+
})
|
|
315
|
+
}));
|
|
316
|
+
changes.push(...this.diffValues(previousDefinitions.constraints, currentDefinitions.constraints, {
|
|
317
|
+
create: (constraint) => ({
|
|
318
|
+
kind: "createConstraint",
|
|
319
|
+
table: name,
|
|
320
|
+
constraint
|
|
321
|
+
}),
|
|
322
|
+
delete: (constraint) => ({
|
|
323
|
+
kind: "deleteConstraint",
|
|
324
|
+
table: name,
|
|
325
|
+
constraint
|
|
326
|
+
})
|
|
327
|
+
}));
|
|
328
|
+
changes.push(...this.diffValues(previousDefinitions.indexes, currentDefinitions.indexes, {
|
|
329
|
+
create: (index) => ({
|
|
330
|
+
kind: "createIndex",
|
|
331
|
+
table: name,
|
|
332
|
+
index
|
|
333
|
+
}),
|
|
334
|
+
delete: (index) => ({
|
|
335
|
+
kind: "deleteIndex",
|
|
336
|
+
table: name,
|
|
337
|
+
index
|
|
338
|
+
})
|
|
339
|
+
}));
|
|
340
|
+
}
|
|
341
|
+
for (const [name, table] of previousTables) if (!currentTables.has(name)) changes.push({
|
|
342
|
+
kind: "deleteTable",
|
|
343
|
+
table
|
|
344
|
+
});
|
|
345
|
+
return changes;
|
|
346
|
+
}
|
|
347
|
+
diffValues(before, after, factory) {
|
|
348
|
+
const changes = new Array();
|
|
349
|
+
for (const [key, value] of before) if (!after.has(key)) changes.push(factory.delete(value));
|
|
350
|
+
for (const [key, value] of after) {
|
|
351
|
+
const previous = before.get(key);
|
|
352
|
+
if (previous === void 0) changes.push(factory.create(value));
|
|
353
|
+
else if (!this.equals(previous, value)) {
|
|
354
|
+
if (factory.update) changes.push(factory.update(previous, value));
|
|
355
|
+
else changes.push(factory.delete(previous), factory.create(value));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return changes;
|
|
359
|
+
}
|
|
360
|
+
splitDefinitions(definitions) {
|
|
361
|
+
const constraints = /* @__PURE__ */ new Map();
|
|
362
|
+
const indexes = /* @__PURE__ */ new Map();
|
|
363
|
+
for (const definition of definitions) if (definition.kind === "index" || definition.kind === "uniqueIndex") indexes.set(this.definitionFingerprint(definition), definition);
|
|
364
|
+
else constraints.set(this.definitionFingerprint(definition), definition);
|
|
365
|
+
return {
|
|
366
|
+
constraints,
|
|
367
|
+
indexes
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
definitionFingerprint(definition) {
|
|
371
|
+
if (definition.kind === "check") return `${definition.kind}:${definition.sql}:${JSON.stringify(definition.parameters)}`;
|
|
372
|
+
if (definition.kind === "foreignKey") return `${definition.kind}:${definition.columns.join(",")}:${definition.foreignTable}:${definition.foreignColumns.join(",")}:${definition.onDelete}:${definition.onUpdate}`;
|
|
373
|
+
return `${definition.kind}:${definition.columns.join(",")}`;
|
|
374
|
+
}
|
|
375
|
+
renderChange(change) {
|
|
376
|
+
switch (change.kind) {
|
|
377
|
+
case "createTable": return this.toCreateTableChange(change);
|
|
378
|
+
case "deleteTable": return this.toDeleteTableChange(change);
|
|
379
|
+
case "createColumn": return this.toCreateColumnChange(change);
|
|
380
|
+
case "updateColumn": return this.toUpdateColumnChange(change);
|
|
381
|
+
case "deleteColumn": return this.toDeleteColumnChange(change);
|
|
382
|
+
case "createConstraint": return this.toCreateConstraintChange(change);
|
|
383
|
+
case "deleteConstraint": return this.toDeleteConstraintChange(change);
|
|
384
|
+
case "createIndex": return this.toCreateIndexChange(change);
|
|
385
|
+
case "deleteIndex": return this.toDeleteIndexChange(change);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
toCreateTableChange(change, name = change.table.name) {
|
|
389
|
+
const definitions = new Array();
|
|
390
|
+
for (const column of Object.values(change.table.columns)) {
|
|
391
|
+
definitions.push(...this.toCreateColumnChange({
|
|
392
|
+
kind: "createColumn",
|
|
393
|
+
table: change.table.name,
|
|
394
|
+
column
|
|
395
|
+
}, true));
|
|
396
|
+
for (const constraint of this.columnConstraints(column)) definitions.push(...this.toCreateConstraintChange({
|
|
397
|
+
kind: "createConstraint",
|
|
398
|
+
table: change.table.name,
|
|
399
|
+
constraint
|
|
400
|
+
}, true));
|
|
401
|
+
}
|
|
402
|
+
for (const definition of change.table.definitions) if (definition.kind !== "index" && definition.kind !== "uniqueIndex") definitions.push(...this.toCreateConstraintChange({
|
|
403
|
+
kind: "createConstraint",
|
|
404
|
+
table: change.table.name,
|
|
405
|
+
constraint: definition
|
|
406
|
+
}, true));
|
|
407
|
+
return [`CREATE TABLE ${this.quoteIdentifier(name)} (\n${definitions.map((definition) => ` ${definition}`).join(",\n")}\n);`];
|
|
408
|
+
}
|
|
409
|
+
toDeleteTableChange(change) {
|
|
410
|
+
return [`DROP TABLE ${this.quoteIdentifier(change.table.name)};`];
|
|
411
|
+
}
|
|
412
|
+
toCreateColumnChange(change, inline = false) {
|
|
413
|
+
const definition = `${this.quoteIdentifier(change.column.name)} ${change.column.type}${change.column.notNull ? " NOT NULL" : ""}`;
|
|
414
|
+
if (inline) return [definition];
|
|
415
|
+
const statements = [this.alterTable(change.table, `ADD COLUMN ${definition}`)];
|
|
416
|
+
for (const constraint of this.columnConstraints(change.column)) statements.push(...this.toCreateConstraintChange({
|
|
417
|
+
kind: "createConstraint",
|
|
418
|
+
table: change.table,
|
|
419
|
+
constraint
|
|
420
|
+
}));
|
|
421
|
+
return statements;
|
|
422
|
+
}
|
|
423
|
+
toUpdateColumnChange(change) {
|
|
424
|
+
const statements = new Array();
|
|
425
|
+
if (change.before.type !== change.after.type) statements.push(this.alterTable(change.table, `ALTER COLUMN ${this.quoteIdentifier(change.after.name)} TYPE ${change.after.type}`));
|
|
426
|
+
if (change.before.notNull !== change.after.notNull) statements.push(this.alterTable(change.table, `ALTER COLUMN ${this.quoteIdentifier(change.after.name)} ${change.after.notNull ? "SET" : "DROP"} NOT NULL`));
|
|
427
|
+
statements.push(...this.updateColumnConstraints(change.table, change.before, change.after));
|
|
428
|
+
return statements;
|
|
429
|
+
}
|
|
430
|
+
toDeleteColumnChange(change) {
|
|
431
|
+
const statements = new Array();
|
|
432
|
+
for (const constraint of this.columnConstraints(change.column)) statements.push(...this.toDeleteConstraintChange({
|
|
433
|
+
kind: "deleteConstraint",
|
|
434
|
+
table: change.table,
|
|
435
|
+
constraint
|
|
436
|
+
}));
|
|
437
|
+
statements.push(this.alterTable(change.table, `DROP COLUMN ${this.quoteIdentifier(change.column.name)}`));
|
|
438
|
+
return statements;
|
|
439
|
+
}
|
|
440
|
+
toCreateConstraintChange(change, inline = false) {
|
|
441
|
+
const constraint = change.constraint;
|
|
442
|
+
const name = this.quoteIdentifier(this.constraintName(change.table, constraint));
|
|
443
|
+
let definition;
|
|
444
|
+
if (constraint.kind === "check") {
|
|
445
|
+
let index = 0;
|
|
446
|
+
const sql = constraint.sql.replaceAll("?", () => {
|
|
447
|
+
if (index >= constraint.parameters.length) throw new Error("Check constraint parameter count does not match its SQL placeholders");
|
|
448
|
+
return this.serializeLiteral(constraint.parameters[index++]);
|
|
449
|
+
});
|
|
450
|
+
if (index !== constraint.parameters.length) throw new Error("Check constraint parameter count does not match its SQL placeholders");
|
|
451
|
+
definition = `CONSTRAINT ${name} CHECK (${sql})`;
|
|
452
|
+
} else {
|
|
453
|
+
const columns = this.quoteIdentifiers(constraint.columns);
|
|
454
|
+
if (constraint.kind === "primaryKey") definition = `CONSTRAINT ${name} PRIMARY KEY (${columns})`;
|
|
455
|
+
else if (constraint.kind === "unique") definition = `CONSTRAINT ${name} UNIQUE (${columns})`;
|
|
456
|
+
else {
|
|
457
|
+
let actions = "";
|
|
458
|
+
if (constraint.onDelete) actions += ` ON DELETE ${constraint.onDelete.toUpperCase()}`;
|
|
459
|
+
if (constraint.onUpdate) actions += ` ON UPDATE ${constraint.onUpdate.toUpperCase()}`;
|
|
460
|
+
definition = `CONSTRAINT ${name} FOREIGN KEY (${columns}) REFERENCES ${this.quoteIdentifier(constraint.foreignTable)} (${this.quoteIdentifiers(constraint.foreignColumns)})${actions}`;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return inline ? [definition] : [this.alterTable(change.table, `ADD ${definition}`)];
|
|
464
|
+
}
|
|
465
|
+
toDeleteConstraintChange(change) {
|
|
466
|
+
return [this.alterTable(change.table, `DROP CONSTRAINT ${this.quoteIdentifier(this.constraintName(change.table, change.constraint))}`)];
|
|
467
|
+
}
|
|
468
|
+
toCreateIndexChange(change) {
|
|
469
|
+
return [`CREATE ${change.index.kind === "uniqueIndex" ? "UNIQUE " : ""}INDEX ${this.quoteIdentifier(this.indexName(change.table, change.index))} ON ${this.quoteIdentifier(change.table)} (${this.quoteIdentifiers(change.index.columns)});`];
|
|
470
|
+
}
|
|
471
|
+
toDeleteIndexChange(change) {
|
|
472
|
+
return [`DROP INDEX ${this.quoteIdentifier(this.indexName(change.table, change.index))};`];
|
|
473
|
+
}
|
|
474
|
+
columnConstraints(column) {
|
|
475
|
+
const constraints = new Array();
|
|
476
|
+
if (column.primaryKey) constraints.push({
|
|
477
|
+
kind: "primaryKey",
|
|
478
|
+
columns: [column.name]
|
|
479
|
+
});
|
|
480
|
+
if (column.unique) constraints.push({
|
|
481
|
+
kind: "unique",
|
|
482
|
+
columns: [column.name]
|
|
483
|
+
});
|
|
484
|
+
if (column.references) constraints.push({
|
|
485
|
+
kind: "foreignKey",
|
|
486
|
+
columns: [column.name],
|
|
487
|
+
foreignTable: column.references.table,
|
|
488
|
+
foreignColumns: [column.references.column],
|
|
489
|
+
onDelete: column.references.onDelete,
|
|
490
|
+
onUpdate: column.references.onUpdate
|
|
491
|
+
});
|
|
492
|
+
return constraints;
|
|
493
|
+
}
|
|
494
|
+
updateColumnConstraints(table, before, after) {
|
|
495
|
+
const statements = new Array();
|
|
496
|
+
const beforeConstraints = new Map(this.columnConstraints(before).map((constraint) => [constraint.kind, constraint]));
|
|
497
|
+
const afterConstraints = new Map(this.columnConstraints(after).map((constraint) => [constraint.kind, constraint]));
|
|
498
|
+
for (const [kind, constraint] of beforeConstraints) {
|
|
499
|
+
const next = afterConstraints.get(kind);
|
|
500
|
+
if (!next || !this.equals(constraint, next)) statements.push(...this.toDeleteConstraintChange({
|
|
501
|
+
kind: "deleteConstraint",
|
|
502
|
+
table,
|
|
503
|
+
constraint
|
|
504
|
+
}));
|
|
505
|
+
}
|
|
506
|
+
for (const [kind, constraint] of afterConstraints) {
|
|
507
|
+
const previous = beforeConstraints.get(kind);
|
|
508
|
+
if (!previous || !this.equals(previous, constraint)) statements.push(...this.toCreateConstraintChange({
|
|
509
|
+
kind: "createConstraint",
|
|
510
|
+
table,
|
|
511
|
+
constraint
|
|
512
|
+
}));
|
|
513
|
+
}
|
|
514
|
+
return statements;
|
|
515
|
+
}
|
|
516
|
+
constraintName(table, constraint) {
|
|
517
|
+
if (constraint.kind === "check") return this.generatedName([
|
|
518
|
+
table,
|
|
519
|
+
this.hash(`${constraint.sql}:${JSON.stringify(constraint.parameters)}`),
|
|
520
|
+
"check"
|
|
521
|
+
]);
|
|
522
|
+
return this.generatedName([
|
|
523
|
+
table,
|
|
524
|
+
...constraint.columns,
|
|
525
|
+
constraint.kind
|
|
526
|
+
]);
|
|
527
|
+
}
|
|
528
|
+
indexName(table, index) {
|
|
529
|
+
return this.generatedName([
|
|
530
|
+
table,
|
|
531
|
+
...index.columns,
|
|
532
|
+
index.kind
|
|
533
|
+
]);
|
|
534
|
+
}
|
|
535
|
+
generatedName(parts) {
|
|
536
|
+
const value = parts.join("_").replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
537
|
+
return value.length <= 54 ? value : `${value.slice(0, 45)}_${this.hash(value)}`;
|
|
538
|
+
}
|
|
539
|
+
hash(value) {
|
|
540
|
+
let result = 2166136261;
|
|
541
|
+
for (const character of value) {
|
|
542
|
+
result ^= character.charCodeAt(0);
|
|
543
|
+
result = Math.imul(result, 16777619);
|
|
544
|
+
}
|
|
545
|
+
return (result >>> 0).toString(16).padStart(8, "0");
|
|
546
|
+
}
|
|
547
|
+
quoteIdentifiers(values) {
|
|
548
|
+
return values.map((value) => this.quoteIdentifier(value)).join(", ");
|
|
549
|
+
}
|
|
550
|
+
alterTable(table, value) {
|
|
551
|
+
return `ALTER TABLE ${this.quoteIdentifier(table)} ${value};`;
|
|
552
|
+
}
|
|
553
|
+
equals(left, right) {
|
|
554
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
555
|
+
}
|
|
556
|
+
serializeLiteral(value) {
|
|
557
|
+
if (value === null) return "NULL";
|
|
558
|
+
if (typeof value === "number") {
|
|
559
|
+
if (!Number.isFinite(value)) throw new Error("Unable to serialize non-finite check constraint number");
|
|
560
|
+
return String(value);
|
|
561
|
+
}
|
|
562
|
+
if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
|
|
563
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
var Database = class {
|
|
567
|
+
$schema;
|
|
568
|
+
$session;
|
|
569
|
+
get $dialect() {
|
|
570
|
+
return this.$migration.$dialect;
|
|
571
|
+
}
|
|
572
|
+
constructor(session, schema) {
|
|
573
|
+
this.$session = session;
|
|
574
|
+
this.$schema = schema;
|
|
575
|
+
}
|
|
576
|
+
toMeta() {
|
|
577
|
+
return {
|
|
578
|
+
dialect: this.$dialect,
|
|
579
|
+
tables: Object.values(this.$schema).map((table) => table.toMeta())
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
var Table = class {
|
|
584
|
+
$name;
|
|
585
|
+
$columns;
|
|
586
|
+
$definitions;
|
|
587
|
+
constructor(name, columns, definitions) {
|
|
588
|
+
this.$name = name;
|
|
589
|
+
this.$columns = columns;
|
|
590
|
+
this.$definitions = definitions;
|
|
591
|
+
for (const column of Object.values(columns)) column.$table = this;
|
|
592
|
+
}
|
|
593
|
+
toMeta() {
|
|
594
|
+
return {
|
|
595
|
+
name: this.$name,
|
|
596
|
+
columns: Object.fromEntries(Object.values(this.$columns).map((column) => [column.$name, column.toMeta()])),
|
|
597
|
+
definitions: this.$definitions.map((definition) => definition.toMeta())
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
toSql() {
|
|
601
|
+
return new Sql(`"${this.$name}"`);
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
var Column = class {
|
|
605
|
+
$name;
|
|
606
|
+
$table;
|
|
607
|
+
$notNull;
|
|
608
|
+
$primaryKey;
|
|
609
|
+
$unique;
|
|
610
|
+
$defaultValue;
|
|
611
|
+
$references;
|
|
612
|
+
$onDelete;
|
|
613
|
+
$onUpdate;
|
|
614
|
+
constructor(name) {
|
|
615
|
+
this.$name = name;
|
|
616
|
+
this.$notNull = false;
|
|
617
|
+
this.$primaryKey = false;
|
|
618
|
+
this.$unique = false;
|
|
619
|
+
}
|
|
620
|
+
default(defaultValue) {
|
|
621
|
+
this.$defaultValue = defaultValue;
|
|
622
|
+
return this;
|
|
623
|
+
}
|
|
624
|
+
notNull() {
|
|
625
|
+
this.$notNull = true;
|
|
626
|
+
return this;
|
|
627
|
+
}
|
|
628
|
+
unique() {
|
|
629
|
+
this.$unique = true;
|
|
630
|
+
return this;
|
|
631
|
+
}
|
|
632
|
+
primaryKey() {
|
|
633
|
+
this.$primaryKey = true;
|
|
634
|
+
return this;
|
|
635
|
+
}
|
|
636
|
+
references(references, actions) {
|
|
637
|
+
this.$references = references;
|
|
638
|
+
this.$onDelete = actions?.onDelete;
|
|
639
|
+
this.$onUpdate = actions?.onUpdate;
|
|
640
|
+
return this;
|
|
641
|
+
}
|
|
642
|
+
fromDriverValue(value) {
|
|
643
|
+
return value;
|
|
644
|
+
}
|
|
645
|
+
toDriverValue(value) {
|
|
646
|
+
return value;
|
|
647
|
+
}
|
|
648
|
+
toSql() {
|
|
649
|
+
return this.$table ? new Sql(this.$table, ".", this.toSqlName()) : this.toSqlName();
|
|
650
|
+
}
|
|
651
|
+
toSqlName() {
|
|
652
|
+
return new Sql(`"${this.$name}"`);
|
|
653
|
+
}
|
|
654
|
+
toMeta() {
|
|
655
|
+
const foreignColumn = this.$references?.();
|
|
656
|
+
if (foreignColumn && !foreignColumn.$table) throw new Error(`Referenced column "${foreignColumn.$name}" does not belong to a table`);
|
|
657
|
+
return {
|
|
658
|
+
name: this.$name,
|
|
659
|
+
type: this.toSqlType(),
|
|
660
|
+
notNull: this.$notNull,
|
|
661
|
+
primaryKey: this.$primaryKey,
|
|
662
|
+
unique: this.$unique,
|
|
663
|
+
references: foreignColumn ? {
|
|
664
|
+
table: foreignColumn.$table.$name,
|
|
665
|
+
column: foreignColumn.$name,
|
|
666
|
+
onDelete: this.$onDelete,
|
|
667
|
+
onUpdate: this.$onUpdate
|
|
668
|
+
} : void 0
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
var UniqueConstraint = class {
|
|
673
|
+
$columns;
|
|
674
|
+
constructor(...columns) {
|
|
675
|
+
this.$columns = columns;
|
|
676
|
+
}
|
|
677
|
+
on(...columns) {
|
|
678
|
+
this.$columns = columns;
|
|
679
|
+
return this;
|
|
680
|
+
}
|
|
681
|
+
toMeta() {
|
|
682
|
+
return {
|
|
683
|
+
kind: "unique",
|
|
684
|
+
columns: this.$columns.map((column) => column.$name)
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
var CheckConstraint = class {
|
|
689
|
+
$value;
|
|
690
|
+
$query;
|
|
691
|
+
constructor(value) {
|
|
692
|
+
this.$value = value;
|
|
693
|
+
this.$query = value.toQuery();
|
|
694
|
+
}
|
|
695
|
+
toMeta() {
|
|
696
|
+
return {
|
|
697
|
+
kind: "check",
|
|
698
|
+
sql: this.$query.sql,
|
|
699
|
+
parameters: this.$query.parameters.map((parameter) => {
|
|
700
|
+
if (typeof parameter === "bigint") return parameter.toString();
|
|
701
|
+
if (typeof parameter === "symbol") throw new Error("Unable to serialize symbol parameters");
|
|
702
|
+
return parameter ?? null;
|
|
703
|
+
})
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
var PrimaryKeyConstraint = class {
|
|
708
|
+
$columns;
|
|
709
|
+
constructor(...columns) {
|
|
710
|
+
this.$columns = columns;
|
|
711
|
+
}
|
|
712
|
+
toMeta() {
|
|
713
|
+
return {
|
|
714
|
+
kind: "primaryKey",
|
|
715
|
+
columns: this.$columns.map((column) => column.$name)
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
var ForeignKeyConstraint = class {
|
|
720
|
+
$columns;
|
|
721
|
+
$foreignColumns;
|
|
722
|
+
constructor(columns, foreignColumns) {
|
|
723
|
+
this.$columns = columns;
|
|
724
|
+
this.$foreignColumns = foreignColumns;
|
|
725
|
+
}
|
|
726
|
+
toMeta() {
|
|
727
|
+
const foreignTables = new Set(this.$foreignColumns.map((column) => column.$table?.$name));
|
|
728
|
+
if (foreignTables.size !== 1 || foreignTables.has(void 0)) throw new Error("Foreign key columns must belong to the same table");
|
|
729
|
+
return {
|
|
730
|
+
kind: "foreignKey",
|
|
731
|
+
columns: this.$columns.map((column) => column.$name),
|
|
732
|
+
foreignTable: [...foreignTables][0],
|
|
733
|
+
foreignColumns: this.$foreignColumns.map((column) => column.$name)
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
var Index = class {
|
|
738
|
+
$columns;
|
|
739
|
+
constructor(...columns) {
|
|
740
|
+
this.$columns = columns;
|
|
741
|
+
}
|
|
742
|
+
on(...columns) {
|
|
743
|
+
this.$columns = columns;
|
|
744
|
+
return this;
|
|
745
|
+
}
|
|
746
|
+
toMeta() {
|
|
747
|
+
return {
|
|
748
|
+
kind: "index",
|
|
749
|
+
columns: this.$columns.map((column) => column.$name)
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
var UniqueIndex = class {
|
|
754
|
+
$columns;
|
|
755
|
+
constructor(...columns) {
|
|
756
|
+
this.$columns = columns;
|
|
757
|
+
}
|
|
758
|
+
on(...columns) {
|
|
759
|
+
this.$columns = columns;
|
|
760
|
+
return this;
|
|
761
|
+
}
|
|
762
|
+
toMeta() {
|
|
763
|
+
return {
|
|
764
|
+
kind: "uniqueIndex",
|
|
765
|
+
columns: this.$columns.map((column) => column.$name)
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
function toParameter(column, value) {
|
|
770
|
+
if (value instanceof Parameter || value instanceof Sql || value instanceof Table || value instanceof Column) return value;
|
|
771
|
+
return new Parameter(column.toDriverValue(value));
|
|
772
|
+
}
|
|
773
|
+
function combine(chunks, separator) {
|
|
774
|
+
const result = [];
|
|
775
|
+
chunks.forEach((chunk, index) => {
|
|
776
|
+
if (index > 0) result.push(separator);
|
|
777
|
+
result.push(chunk);
|
|
778
|
+
});
|
|
779
|
+
return new Sql(...result);
|
|
780
|
+
}
|
|
781
|
+
function sql(tokens, ...values) {
|
|
782
|
+
const chunks = [tokens[0]];
|
|
783
|
+
values.forEach((value, index) => {
|
|
784
|
+
chunks.push(value === void 0 ? null : value, tokens[index + 1]);
|
|
785
|
+
});
|
|
786
|
+
return new Sql(...chunks);
|
|
787
|
+
}
|
|
788
|
+
function equals(column, right) {
|
|
789
|
+
return sql`${column} = ${toParameter(column, right)}`;
|
|
790
|
+
}
|
|
791
|
+
function greater(column, right) {
|
|
792
|
+
return sql`${column} > ${toParameter(column, right)}`;
|
|
793
|
+
}
|
|
794
|
+
function greaterEquals(column, right) {
|
|
795
|
+
return sql`${column} >= ${toParameter(column, right)}`;
|
|
796
|
+
}
|
|
797
|
+
function lower(column, right) {
|
|
798
|
+
return sql`${column} < ${toParameter(column, right)}`;
|
|
799
|
+
}
|
|
800
|
+
function lowerEquals(column, right) {
|
|
801
|
+
return sql`${column} <= ${toParameter(column, right)}`;
|
|
802
|
+
}
|
|
803
|
+
function like(column, right) {
|
|
804
|
+
return sql`${column} LIKE ${toParameter(column, right)}`;
|
|
805
|
+
}
|
|
806
|
+
function contains(column, values) {
|
|
807
|
+
return sql`${column} IN (${combine(values.map((value) => toParameter(column, value)), ", ")})`;
|
|
808
|
+
}
|
|
809
|
+
function nullish(chunk) {
|
|
810
|
+
return sql`${chunk} IS NULL`;
|
|
811
|
+
}
|
|
812
|
+
function not(chunk) {
|
|
813
|
+
return sql`NOT ${chunk}`;
|
|
814
|
+
}
|
|
815
|
+
function exists(chunk) {
|
|
816
|
+
return sql`EXISTS (${chunk})`;
|
|
817
|
+
}
|
|
818
|
+
function between(column, from, to) {
|
|
819
|
+
return sql`${column} BETWEEN ${toParameter(column, from)} AND ${toParameter(column, to)}`;
|
|
820
|
+
}
|
|
821
|
+
function and(...chunks) {
|
|
822
|
+
return sql`(${combine(chunks, " AND ")})`;
|
|
823
|
+
}
|
|
824
|
+
function or(...chunks) {
|
|
825
|
+
return sql`(${combine(chunks, " OR ")})`;
|
|
826
|
+
}
|
|
827
|
+
function asc(chunk) {
|
|
828
|
+
return sql`${chunk} ASC`;
|
|
829
|
+
}
|
|
830
|
+
function desc(chunk) {
|
|
831
|
+
return sql`${chunk} DESC`;
|
|
832
|
+
}
|
|
833
|
+
function useStorage(scope, scopeName) {
|
|
834
|
+
const storages = getContext(scope, STORAGE_CONTEXT);
|
|
835
|
+
const storage = storages[scopeName] ?? {};
|
|
836
|
+
storages[scopeName] = storage;
|
|
837
|
+
setContext(scope, STORAGE_CONTEXT, storages);
|
|
838
|
+
return storage;
|
|
839
|
+
}
|
|
840
|
+
var STORAGE_CONTEXT = defineContext("STORAGE_CONTEXT");
|
|
841
|
+
//#endregion
|
|
842
|
+
export { CheckConstraint, Column, Database, DeleteStatement, ForeignKeyConstraint, Index, InsertStatement, Migration, Parameter, PreparedQuery, PrimaryKeyConstraint, STORAGE_CONTEXT, SelectStatement, Session, Sql, Statement, Table, UniqueConstraint, UniqueIndex, UpdateStatement, and, asc, between, combine, contains, desc, equals, exists, greater, greaterEquals, like, lower, lowerEquals, not, nullish, or, sql, toParameter, useStorage };
|