@lobomfz/db 0.4.0 → 0.5.0
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/README.md +55 -4
- package/package.json +8 -5
- package/src/database.ts +184 -271
- package/src/dialect/serialize.ts +1 -1
- package/src/env.ts +2 -1
- package/src/fts/resolve.ts +208 -0
- package/src/fts/sql.ts +148 -0
- package/src/fts/tokenizer.ts +27 -0
- package/src/index.ts +18 -1
- package/src/json.ts +13 -0
- package/src/migration/data-loss-error.ts +29 -0
- package/src/migration/diff.ts +269 -25
- package/src/migration/drift-error.ts +9 -0
- package/src/migration/execute.ts +78 -26
- package/src/migration/guided.ts +136 -0
- package/src/migration/introspect.ts +138 -1
- package/src/migration/registry.ts +43 -0
- package/src/migration/types.ts +110 -1
- package/src/plugin.ts +206 -3
- package/src/schema-compiler.ts +362 -0
- package/src/types.ts +110 -12
- package/src/vector-index/load.ts +67 -0
- package/src/vector-index/nearest.ts +68 -0
- package/src/vector-index/resolve.ts +78 -0
- package/src/vector-index/sql.ts +76 -0
- package/src/vector.ts +13 -0
- package/src/write-validation-plugin.ts +92 -0
package/src/database.ts
CHANGED
|
@@ -4,387 +4,300 @@ import type { Type } from "arktype";
|
|
|
4
4
|
import { Kysely } from "kysely";
|
|
5
5
|
|
|
6
6
|
import { BunSqliteDialect } from "./dialect/dialect.js";
|
|
7
|
-
import type
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
7
|
+
import { resolveFtsPlan, type FtsTableInfo, type ResolvedFtsPlan } from "./fts/resolve.js";
|
|
8
|
+
import { generateRebuildSelect } from "./fts/sql.js";
|
|
9
|
+
import { DataLossError } from "./migration/data-loss-error.js";
|
|
10
|
+
import { Differ } from "./migration/diff.js";
|
|
10
11
|
import { Executor } from "./migration/execute.js";
|
|
12
|
+
import { GuidedMigrationRunner } from "./migration/guided.js";
|
|
11
13
|
import { Introspector } from "./migration/introspect.js";
|
|
14
|
+
import { MigrationRegistry } from "./migration/registry.js";
|
|
15
|
+
import type { DesiredSchema, DesiredTable, ExistingSchema } from "./migration/types.js";
|
|
12
16
|
import { ResultHydrationPlugin } from "./plugin.js";
|
|
17
|
+
import { SchemaCompiler } from "./schema-compiler.js";
|
|
13
18
|
import type {
|
|
19
|
+
DataMigration,
|
|
14
20
|
DatabaseOptions,
|
|
15
|
-
|
|
21
|
+
DatabasePragmas,
|
|
22
|
+
FtsApi,
|
|
23
|
+
FtsConfig,
|
|
16
24
|
SchemaRecord,
|
|
17
25
|
TablesFromSchemas,
|
|
18
|
-
|
|
26
|
+
VectorIndexApi,
|
|
27
|
+
VectorIndexConfig,
|
|
19
28
|
} from "./types.js";
|
|
29
|
+
import { loadExtensions } from "./vector-index/load.js";
|
|
30
|
+
import { registerVectorIndex, unregisterVectorIndex } from "./vector-index/nearest.js";
|
|
31
|
+
import {
|
|
32
|
+
resolveVectorIndex,
|
|
33
|
+
type ResolvedVectorIndex,
|
|
34
|
+
type VectorAnchorInfo,
|
|
35
|
+
} from "./vector-index/resolve.js";
|
|
36
|
+
import { generateRebuildVectorSql } from "./vector-index/sql.js";
|
|
20
37
|
import { WriteValidationPlugin } from "./write-validation-plugin.js";
|
|
21
38
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
proto?: unknown;
|
|
25
|
-
unit?: unknown;
|
|
26
|
-
structure?: unknown;
|
|
27
|
-
inner?: { divisor?: unknown };
|
|
28
|
-
meta?: DbFieldMeta & { _generated?: GeneratedPreset };
|
|
39
|
+
const defaultPragmas: DatabasePragmas = {
|
|
40
|
+
foreign_keys: true,
|
|
29
41
|
};
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
43
|
+
export class Database<
|
|
44
|
+
T extends SchemaRecord,
|
|
45
|
+
F extends FtsConfig<T> = FtsConfig<T>,
|
|
46
|
+
V extends VectorIndexConfig<T> = VectorIndexConfig<T>,
|
|
47
|
+
> {
|
|
48
|
+
private sqlite: BunDatabase;
|
|
49
|
+
private compiler = new SchemaCompiler();
|
|
50
|
+
private ftsPlans = new Map<string, ResolvedFtsPlan>();
|
|
51
|
+
private vectorIndexPlans = new Map<string, ResolvedVectorIndex>();
|
|
41
52
|
|
|
42
|
-
|
|
43
|
-
key: string;
|
|
44
|
-
kind: "required" | "optional";
|
|
45
|
-
domain?: string;
|
|
46
|
-
nullable?: boolean;
|
|
47
|
-
isBoolean?: boolean;
|
|
48
|
-
isInteger?: boolean;
|
|
49
|
-
isDate?: boolean;
|
|
50
|
-
isBlob?: boolean;
|
|
51
|
-
isJson?: boolean;
|
|
52
|
-
jsonSchema?: Type;
|
|
53
|
-
meta?: DbFieldMeta;
|
|
54
|
-
generated?: GeneratedPreset;
|
|
55
|
-
defaultValue?: unknown;
|
|
56
|
-
};
|
|
53
|
+
readonly infer: TablesFromSchemas<T, F, V> = undefined as any;
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
string: "TEXT",
|
|
60
|
-
number: "REAL",
|
|
61
|
-
};
|
|
55
|
+
readonly kysely: Kysely<TablesFromSchemas<T, F, V>>;
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
foreign_keys: true,
|
|
65
|
-
};
|
|
57
|
+
readonly fts: FtsApi<F>;
|
|
66
58
|
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
readonly vectorIndex: VectorIndexApi<V>;
|
|
60
|
+
|
|
61
|
+
static async open<
|
|
62
|
+
T extends SchemaRecord,
|
|
63
|
+
F extends FtsConfig<T> = FtsConfig<T>,
|
|
64
|
+
V extends VectorIndexConfig<T> = VectorIndexConfig<T>,
|
|
65
|
+
>(options: DatabaseOptions<T, F, V>) {
|
|
66
|
+
const db = new Database(options);
|
|
69
67
|
|
|
70
|
-
|
|
68
|
+
await db.runMigrations();
|
|
71
69
|
|
|
72
|
-
|
|
70
|
+
return db;
|
|
71
|
+
}
|
|
73
72
|
|
|
74
|
-
constructor(private options: DatabaseOptions<T>) {
|
|
73
|
+
private constructor(private options: DatabaseOptions<T, F, V>) {
|
|
75
74
|
const tableSchemas = new Map<string, Type>(Object.entries(options.schema.tables));
|
|
76
75
|
const writeSchemas = new Map<string, Type>(
|
|
77
76
|
Object.entries(options.schema.tables).map(([table, schema]) => [
|
|
78
77
|
table,
|
|
79
|
-
this.createWriteSchema(schema),
|
|
78
|
+
this.compiler.createWriteSchema(schema),
|
|
80
79
|
]),
|
|
81
80
|
);
|
|
82
81
|
|
|
83
82
|
this.sqlite = new BunDatabase(options.path);
|
|
84
83
|
|
|
85
84
|
this.applyPragmas();
|
|
86
|
-
|
|
87
|
-
this.
|
|
85
|
+
this.assertVectorIndexesHaveExtension();
|
|
86
|
+
this.loadConfiguredExtensions();
|
|
88
87
|
|
|
89
88
|
const validation = {
|
|
90
89
|
onRead: options.validation?.onRead ?? false,
|
|
91
90
|
};
|
|
92
91
|
|
|
93
|
-
this.kysely = new Kysely<TablesFromSchemas<T>>({
|
|
92
|
+
this.kysely = new Kysely<TablesFromSchemas<T, F, V>>({
|
|
94
93
|
dialect: new BunSqliteDialect({ database: this.sqlite }),
|
|
95
94
|
plugins: [
|
|
96
95
|
new WriteValidationPlugin(writeSchemas),
|
|
97
96
|
new ResultHydrationPlugin(tableSchemas, validation),
|
|
98
97
|
],
|
|
99
98
|
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private applyPragmas() {
|
|
103
|
-
const pragmas = { ...defaultPragmas, ...this.options.pragmas };
|
|
104
|
-
|
|
105
|
-
if (pragmas.journal_mode) {
|
|
106
|
-
this.sqlite.run(`PRAGMA journal_mode = ${pragmas.journal_mode.toUpperCase()}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (pragmas.synchronous) {
|
|
110
|
-
this.sqlite.run(`PRAGMA synchronous = ${pragmas.synchronous.toUpperCase()}`);
|
|
111
|
-
}
|
|
112
99
|
|
|
113
|
-
this.
|
|
100
|
+
this.fts = {
|
|
101
|
+
rebuild: (name: string) => this.rebuildFts(name),
|
|
102
|
+
} as FtsApi<F>;
|
|
114
103
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
104
|
+
this.vectorIndex = {
|
|
105
|
+
rebuild: (name: string) => this.rebuildVectorIndex(name),
|
|
106
|
+
} as VectorIndexApi<V>;
|
|
118
107
|
}
|
|
119
108
|
|
|
120
|
-
private
|
|
121
|
-
const
|
|
122
|
-
const kind: Prop["kind"] = structureProp.required ? "required" : "optional";
|
|
123
|
-
const defaultValue = inner.default;
|
|
109
|
+
private loadConfiguredExtensions() {
|
|
110
|
+
const extensions = this.options.extensions ?? [];
|
|
124
111
|
|
|
125
|
-
|
|
126
|
-
const nullable = concrete.length < v.branches.length;
|
|
112
|
+
loadExtensions(this.sqlite, extensions);
|
|
127
113
|
|
|
128
|
-
const
|
|
129
|
-
const meta = { ...branchMeta, ...v.meta };
|
|
130
|
-
const generated = meta._generated;
|
|
131
|
-
|
|
132
|
-
if (v.proto === Date || concrete.some((b) => b.proto === Date)) {
|
|
133
|
-
return { key, kind, nullable, isDate: true, generated, defaultValue };
|
|
134
|
-
}
|
|
114
|
+
const hasSqliteVec = extensions.some((e) => e.kind === "sqlite-vec");
|
|
135
115
|
|
|
136
|
-
if (
|
|
137
|
-
return
|
|
138
|
-
key,
|
|
139
|
-
kind,
|
|
140
|
-
nullable,
|
|
141
|
-
isBlob: true,
|
|
142
|
-
meta,
|
|
143
|
-
generated,
|
|
144
|
-
defaultValue,
|
|
145
|
-
};
|
|
116
|
+
if (hasSqliteVec) {
|
|
117
|
+
return;
|
|
146
118
|
}
|
|
147
119
|
|
|
148
|
-
|
|
149
|
-
return { key, kind, nullable, isBoolean: true, generated, defaultValue };
|
|
150
|
-
}
|
|
120
|
+
const probe = new Introspector(this.sqlite);
|
|
151
121
|
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
key,
|
|
155
|
-
kind,
|
|
156
|
-
nullable,
|
|
157
|
-
isJson: true,
|
|
158
|
-
jsonSchema: (parentSchema as any).get(key) as Type,
|
|
159
|
-
meta,
|
|
160
|
-
generated,
|
|
161
|
-
defaultValue,
|
|
162
|
-
};
|
|
122
|
+
if (probe.hasVec0Tables()) {
|
|
123
|
+
loadExtensions(this.sqlite, [{ kind: "sqlite-vec" }]);
|
|
163
124
|
}
|
|
164
|
-
|
|
165
|
-
const branch = concrete[0];
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
key,
|
|
169
|
-
kind,
|
|
170
|
-
nullable,
|
|
171
|
-
domain: branch?.domain,
|
|
172
|
-
isInteger: !!branch?.inner?.divisor,
|
|
173
|
-
meta,
|
|
174
|
-
generated,
|
|
175
|
-
defaultValue,
|
|
176
|
-
};
|
|
177
125
|
}
|
|
178
126
|
|
|
179
|
-
private
|
|
180
|
-
|
|
181
|
-
return "TEXT";
|
|
182
|
-
}
|
|
127
|
+
private assertVectorIndexesHaveExtension() {
|
|
128
|
+
const vectorIndexes = this.options.schema.vectorIndexes;
|
|
183
129
|
|
|
184
|
-
if (
|
|
185
|
-
return
|
|
130
|
+
if (!vectorIndexes || Object.keys(vectorIndexes).length === 0) {
|
|
131
|
+
return;
|
|
186
132
|
}
|
|
187
133
|
|
|
188
|
-
|
|
189
|
-
return "INTEGER";
|
|
190
|
-
}
|
|
134
|
+
const hasSqliteVec = (this.options.extensions ?? []).some((e) => e.kind === "sqlite-vec");
|
|
191
135
|
|
|
192
|
-
if (
|
|
193
|
-
return
|
|
136
|
+
if (hasSqliteVec) {
|
|
137
|
+
return;
|
|
194
138
|
}
|
|
195
139
|
|
|
196
|
-
|
|
197
|
-
}
|
|
140
|
+
const [first] = Object.keys(vectorIndexes);
|
|
198
141
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (prop.meta?.primaryKey) {
|
|
205
|
-
return "PRIMARY KEY";
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (prop.kind === "required" && !prop.nullable) {
|
|
209
|
-
return "NOT NULL";
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return null;
|
|
142
|
+
throw new Error(
|
|
143
|
+
`vectorIndex "${first}" requires the sqlite-vec extension. Add { kind: "sqlite-vec" } to DatabaseOptions.extensions.`,
|
|
144
|
+
);
|
|
213
145
|
}
|
|
214
146
|
|
|
215
|
-
private
|
|
216
|
-
|
|
217
|
-
return "DEFAULT (unixepoch())";
|
|
218
|
-
}
|
|
147
|
+
private applyPragmas() {
|
|
148
|
+
const pragmas = { ...defaultPragmas, ...this.options.pragmas };
|
|
219
149
|
|
|
220
|
-
if (
|
|
221
|
-
|
|
150
|
+
if (pragmas.journal_mode) {
|
|
151
|
+
this.sqlite.run(`PRAGMA journal_mode = ${pragmas.journal_mode.toUpperCase()}`);
|
|
222
152
|
}
|
|
223
153
|
|
|
224
|
-
if (
|
|
225
|
-
|
|
154
|
+
if (pragmas.synchronous) {
|
|
155
|
+
this.sqlite.run(`PRAGMA synchronous = ${pragmas.synchronous.toUpperCase()}`);
|
|
226
156
|
}
|
|
227
157
|
|
|
228
|
-
|
|
229
|
-
return `DEFAULT '${prop.defaultValue}'`;
|
|
230
|
-
}
|
|
158
|
+
this.sqlite.run(`PRAGMA foreign_keys = ${pragmas.foreign_keys ? "ON" : "OFF"}`);
|
|
231
159
|
|
|
232
|
-
if (
|
|
233
|
-
|
|
160
|
+
if (pragmas.busy_timeout_ms !== undefined) {
|
|
161
|
+
this.sqlite.run(`PRAGMA busy_timeout = ${pragmas.busy_timeout_ms}`);
|
|
234
162
|
}
|
|
235
|
-
|
|
236
|
-
throw new Error(
|
|
237
|
-
`Unsupported default value type: ${typeof prop.defaultValue} ${JSON.stringify(prop)}`,
|
|
238
|
-
);
|
|
239
163
|
}
|
|
240
164
|
|
|
241
|
-
private
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
prop.meta?.unique ? "UNIQUE" : null,
|
|
247
|
-
this.defaultClause(prop),
|
|
248
|
-
]
|
|
249
|
-
.filter(Boolean)
|
|
250
|
-
.join(" ");
|
|
251
|
-
}
|
|
165
|
+
private buildDesiredSchema(): DesiredSchema {
|
|
166
|
+
const desiredTables: DesiredTable[] = [];
|
|
167
|
+
const tableInfos = new Map<string, FtsTableInfo>();
|
|
168
|
+
const vectorAnchors = new Map<string, VectorAnchorInfo>();
|
|
169
|
+
const schemaIndexes = this.options.schema.indexes;
|
|
252
170
|
|
|
253
|
-
|
|
254
|
-
|
|
171
|
+
for (const [name, schema] of Object.entries(this.options.schema.tables)) {
|
|
172
|
+
const compiled = this.compiler.compileTable(name, schema, schemaIndexes?.[name] ?? []);
|
|
255
173
|
|
|
256
|
-
|
|
257
|
-
|
|
174
|
+
desiredTables.push(compiled.table);
|
|
175
|
+
tableInfos.set(name, compiled.tableInfo);
|
|
176
|
+
vectorAnchors.set(name, compiled.vectorAnchor);
|
|
258
177
|
}
|
|
259
178
|
|
|
260
|
-
const [
|
|
179
|
+
const desiredFts: ResolvedFtsPlan[] = [];
|
|
261
180
|
|
|
262
|
-
|
|
181
|
+
if (this.options.schema.fts) {
|
|
182
|
+
for (const [name, entry] of Object.entries(this.options.schema.fts)) {
|
|
183
|
+
const plan = resolveFtsPlan(name, entry, tableInfos);
|
|
184
|
+
desiredFts.push(plan);
|
|
185
|
+
this.ftsPlans.set(name, plan);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
263
188
|
|
|
264
|
-
const
|
|
189
|
+
const desiredVectorIndexes: ResolvedVectorIndex[] = [];
|
|
265
190
|
|
|
266
|
-
if (
|
|
267
|
-
|
|
191
|
+
if (this.options.schema.vectorIndexes) {
|
|
192
|
+
for (const [name, entry] of Object.entries(this.options.schema.vectorIndexes)) {
|
|
193
|
+
const plan = resolveVectorIndex(name, entry as any, vectorAnchors);
|
|
194
|
+
desiredVectorIndexes.push(plan);
|
|
195
|
+
this.vectorIndexPlans.set(name, plan);
|
|
196
|
+
registerVectorIndex(plan);
|
|
197
|
+
}
|
|
268
198
|
}
|
|
269
199
|
|
|
270
|
-
return
|
|
200
|
+
return { desiredTables, desiredFts, desiredVectorIndexes };
|
|
271
201
|
}
|
|
272
202
|
|
|
273
|
-
private
|
|
274
|
-
|
|
203
|
+
private async runMigrations() {
|
|
204
|
+
const desired = this.buildDesiredSchema();
|
|
205
|
+
|
|
206
|
+
const introspector = new Introspector(this.sqlite);
|
|
207
|
+
const existingVectorIndexes = introspector.introspectVectorIndexes();
|
|
208
|
+
const existingState: ExistingSchema = {
|
|
209
|
+
existing: introspector.introspect(),
|
|
210
|
+
existingFts: introspector.introspectFts(),
|
|
211
|
+
existingVectorIndexes,
|
|
212
|
+
};
|
|
275
213
|
|
|
276
|
-
const
|
|
214
|
+
const migration = this.options.migration;
|
|
277
215
|
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
|
|
216
|
+
if (migration) {
|
|
217
|
+
await this.applyWithMigration(desired, existingState, migration);
|
|
218
|
+
} else {
|
|
219
|
+
this.applyDiff(desired, existingState);
|
|
220
|
+
}
|
|
281
221
|
|
|
282
|
-
|
|
283
|
-
|
|
222
|
+
for (const name of existingVectorIndexes.keys()) {
|
|
223
|
+
if (!this.vectorIndexPlans.has(name)) {
|
|
224
|
+
unregisterVectorIndex(name);
|
|
284
225
|
}
|
|
285
226
|
}
|
|
286
|
-
|
|
287
|
-
return def;
|
|
288
227
|
}
|
|
289
228
|
|
|
290
|
-
private
|
|
291
|
-
const
|
|
229
|
+
private applyDiff(desired: DesiredSchema, existingState: ExistingSchema) {
|
|
230
|
+
const differ = new Differ(desired, existingState);
|
|
292
231
|
|
|
293
|
-
|
|
294
|
-
|
|
232
|
+
const ops = differ.diff();
|
|
233
|
+
|
|
234
|
+
if (this.options.failOnDataLoss && differ.destructive.length > 0) {
|
|
235
|
+
throw new DataLossError(differ.destructive);
|
|
295
236
|
}
|
|
296
237
|
|
|
297
|
-
|
|
238
|
+
new Executor(this.sqlite, ops).execute();
|
|
298
239
|
}
|
|
299
240
|
|
|
300
|
-
private
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
241
|
+
private async applyWithMigration(
|
|
242
|
+
desired: DesiredSchema,
|
|
243
|
+
existingState: ExistingSchema,
|
|
244
|
+
migration: DataMigration<T, F, V>,
|
|
245
|
+
) {
|
|
246
|
+
const registry = new MigrationRegistry(this.sqlite);
|
|
247
|
+
registry.ensureTable();
|
|
304
248
|
|
|
305
|
-
|
|
306
|
-
return schema;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return (schema as any).omit(...autoIncrementColumns) as Type;
|
|
310
|
-
}
|
|
249
|
+
const checksum = MigrationRegistry.checksum(migration.run);
|
|
311
250
|
|
|
312
|
-
|
|
313
|
-
const columns: string[] = [];
|
|
314
|
-
const fks: string[] = [];
|
|
251
|
+
registry.assertNoDrift(migration.id, checksum);
|
|
315
252
|
|
|
316
|
-
|
|
317
|
-
|
|
253
|
+
if (registry.appliedChecksum(migration.id) !== null) {
|
|
254
|
+
this.applyDiff(desired, existingState);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
318
257
|
|
|
319
|
-
|
|
258
|
+
const fresh = !desired.desiredTables.some((table) => existingState.existing.has(table.name));
|
|
320
259
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
260
|
+
if (fresh) {
|
|
261
|
+
this.applyDiff(desired, existingState);
|
|
262
|
+
registry.record(migration.id, checksum);
|
|
263
|
+
return;
|
|
324
264
|
}
|
|
325
265
|
|
|
326
|
-
|
|
266
|
+
await new GuidedMigrationRunner({
|
|
267
|
+
sqlite: this.sqlite,
|
|
268
|
+
kysely: this.kysely,
|
|
269
|
+
desired,
|
|
270
|
+
existing: existingState.existing,
|
|
271
|
+
migration,
|
|
272
|
+
checksum,
|
|
273
|
+
registry,
|
|
274
|
+
}).run();
|
|
327
275
|
}
|
|
328
276
|
|
|
329
|
-
private
|
|
330
|
-
const
|
|
331
|
-
const schemaIndexes = this.options.schema.indexes;
|
|
277
|
+
private rebuildFts(name: string) {
|
|
278
|
+
const plan = this.ftsPlans.get(name);
|
|
332
279
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const columns = props.map((prop) => {
|
|
337
|
-
const isNotNull = this.columnConstraint(prop) === "NOT NULL";
|
|
338
|
-
const defaultClause = this.defaultClause(prop);
|
|
339
|
-
const hasLiteralDefault = prop.generated !== "now" && defaultClause !== null;
|
|
340
|
-
|
|
341
|
-
return {
|
|
342
|
-
name: prop.key,
|
|
343
|
-
addable: !isNotNull || hasLiteralDefault,
|
|
344
|
-
columnDef: this.addColumnDef(prop),
|
|
345
|
-
type: this.sqlType(prop),
|
|
346
|
-
notnull: isNotNull,
|
|
347
|
-
defaultValue: defaultClause
|
|
348
|
-
? defaultClause.replace("DEFAULT ", "").replace(/^\((.+)\)$/, "$1")
|
|
349
|
-
: null,
|
|
350
|
-
unique: !!prop.meta?.unique,
|
|
351
|
-
references: prop.meta?.references ?? null,
|
|
352
|
-
onDelete: prop.meta?.onDelete?.toUpperCase() ?? null,
|
|
353
|
-
};
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
const indexes = (schemaIndexes?.[name] ?? []).map((indexDef) => ({
|
|
357
|
-
name: this.generateIndexName(name, indexDef.columns, indexDef.unique ?? false),
|
|
358
|
-
columns: indexDef.columns,
|
|
359
|
-
sql: this.generateCreateIndexSQL(name, indexDef),
|
|
360
|
-
}));
|
|
361
|
-
|
|
362
|
-
desiredTables.push({
|
|
363
|
-
name,
|
|
364
|
-
sql: this.generateCreateTableSQL(name, props),
|
|
365
|
-
columns,
|
|
366
|
-
indexes,
|
|
367
|
-
});
|
|
280
|
+
if (!plan) {
|
|
281
|
+
throw new Error(`FTS "${name}" is not declared in the schema`);
|
|
368
282
|
}
|
|
369
283
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
284
|
+
this.sqlite.transaction(() => {
|
|
285
|
+
this.sqlite.run(`DELETE FROM "${name}"`);
|
|
286
|
+
this.sqlite.run(generateRebuildSelect(plan));
|
|
287
|
+
})();
|
|
374
288
|
}
|
|
375
289
|
|
|
376
|
-
private
|
|
377
|
-
const
|
|
290
|
+
private rebuildVectorIndex(name: string) {
|
|
291
|
+
const plan = this.vectorIndexPlans.get(name);
|
|
378
292
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
private generateCreateIndexSQL(tableName: string, indexDef: IndexDefinition) {
|
|
383
|
-
const indexName = this.generateIndexName(tableName, indexDef.columns, indexDef.unique ?? false);
|
|
384
|
-
const unique = indexDef.unique ? "UNIQUE " : "";
|
|
385
|
-
const columns = indexDef.columns.map((c) => `"${c}"`).join(", ");
|
|
293
|
+
if (!plan) {
|
|
294
|
+
throw new Error(`vector index "${name}" is not declared in the schema`);
|
|
295
|
+
}
|
|
386
296
|
|
|
387
|
-
|
|
297
|
+
this.sqlite.transaction(() => {
|
|
298
|
+
this.sqlite.run(`DELETE FROM "${name}"`);
|
|
299
|
+
this.sqlite.run(generateRebuildVectorSql(plan));
|
|
300
|
+
})();
|
|
388
301
|
}
|
|
389
302
|
|
|
390
303
|
reset(table?: keyof T & string): void {
|
package/src/dialect/serialize.ts
CHANGED
package/src/env.ts
CHANGED
|
@@ -3,7 +3,8 @@ export type DbFieldMeta = {
|
|
|
3
3
|
unique?: boolean;
|
|
4
4
|
references?: `${string}.${string}`;
|
|
5
5
|
onDelete?: "cascade" | "set null" | "restrict";
|
|
6
|
-
_generated?: "autoincrement" | "now";
|
|
6
|
+
_generated?: "autoincrement" | "now" | "vector";
|
|
7
|
+
_vectorDim?: number;
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
declare global {
|