qubu 0.6.0 → 0.6.2

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.
Files changed (95) hide show
  1. package/README.md +112 -0
  2. package/dist/{complete-DP7pliuY.mjs → canonical-B5_ouh-c.mjs} +466 -241
  3. package/dist/codegen.d.mts +2 -2
  4. package/dist/codegen.mjs +180 -90
  5. package/dist/column-Cyc2CMnG.mjs +116 -0
  6. package/dist/column-DDRvD7SF.mjs +721 -0
  7. package/dist/{constraints-YGyNPQ_z.mjs → constraints-CAmi18Uk.mjs} +8 -3
  8. package/dist/core.d.mts +3 -3
  9. package/dist/core.mjs +4 -5
  10. package/dist/diagnostics-I9vVtXkc.mjs +40 -0
  11. package/dist/diff.d.mts +9 -9
  12. package/dist/diff.mjs +152 -102
  13. package/dist/{explain-CkIK13L_.mjs → explain-BIqEmZha.mjs} +1 -1
  14. package/dist/expressions-_6JF_J77.mjs +130 -0
  15. package/dist/index-CaxrMD1A.d.mts +1 -0
  16. package/dist/index.d.mts +2 -2
  17. package/dist/index.mjs +414 -77
  18. package/dist/introspection/mysql.d.mts +26 -0
  19. package/dist/introspection/mysql.mjs +1278 -0
  20. package/dist/introspection/postgres.d.mts +43 -0
  21. package/dist/introspection/postgres.mjs +1889 -0
  22. package/dist/introspection/sqlite.d.mts +15 -0
  23. package/dist/introspection/sqlite.mjs +1172 -0
  24. package/dist/introspection.d.mts +26 -87
  25. package/dist/introspection.mjs +3 -4351
  26. package/dist/mysql.d.mts +3 -3
  27. package/dist/mysql.mjs +8 -7
  28. package/dist/{errors-Dxv73YJu.mjs → omit-VEr9Ydux.mjs} +5 -1
  29. package/dist/{on-conflict-DZQ85f1t.mjs → on-conflict-B2rFyHGF.mjs} +78 -8
  30. package/dist/on-duplicate-key-update-Czsw1Yq-.mjs +95 -0
  31. package/dist/postgres-hFhd0I9n.mjs +68 -0
  32. package/dist/postgres.d.mts +3 -3
  33. package/dist/postgres.mjs +3 -52
  34. package/dist/registry-BXE_4M9P.mjs +127 -0
  35. package/dist/{relational-CxnLCqZQ.mjs → relational-CoPBETjI.mjs} +3 -3
  36. package/dist/schema.d.mts +2 -2
  37. package/dist/schema.mjs +8 -7
  38. package/dist/{serialize-BN07IK0v.mjs → serialize-CyobNEx-.mjs} +174 -30
  39. package/dist/serialize-Du2UPZMt.d.mts +92 -0
  40. package/dist/snapshot/mysql.d.mts +5 -5
  41. package/dist/snapshot/mysql.mjs +32 -32
  42. package/dist/snapshot/postgres.d.mts +5 -5
  43. package/dist/snapshot/postgres.mjs +25 -25
  44. package/dist/snapshot/sqlite.d.mts +5 -5
  45. package/dist/snapshot/sqlite.mjs +30 -30
  46. package/dist/snapshot-mIb-Zzb5.mjs +1489 -0
  47. package/dist/snapshot.d.mts +4 -5
  48. package/dist/snapshot.mjs +3 -4
  49. package/dist/{source-SqrKWjFJ.mjs → source-DYSUqzvb.mjs} +2 -2
  50. package/dist/sqlite.d.mts +2 -2
  51. package/dist/sqlite.mjs +10 -10
  52. package/dist/{table-BwflqeAj.mjs → table-B8zEq0az.mjs} +4 -4
  53. package/dist/{types-BIJsj2fJ.mjs → types-CYHpSPwj.mjs} +10 -7
  54. package/dist/{types-CTCqtFlS.d.mts → types-CiMvKi5V.d.mts} +14 -4
  55. package/dist/{types-C0VkiwpR.d.mts → types-Dqr4o2I1.d.mts} +640 -189
  56. package/dist/value-CpaUFtjw.mjs +45 -0
  57. package/dist/vite/ambient.d.ts +2 -0
  58. package/dist/vite.d.mts +1 -1
  59. package/dist/vite.mjs +2 -0
  60. package/docs/dialects-and-execution.md +61 -11
  61. package/docs/getting-started.md +1 -1
  62. package/docs/guides/compose-queries.md +22 -0
  63. package/docs/guides/drizzle.md +11 -11
  64. package/docs/guides/extensions/typed-expressions.md +5 -3
  65. package/docs/guides/json.md +95 -2
  66. package/docs/guides/mutations.md +125 -0
  67. package/docs/guides/valtio-sync.md +113 -0
  68. package/docs/index.md +1 -1
  69. package/docs/migrations/adapters.md +58 -21
  70. package/docs/migrations/index.md +50 -12
  71. package/docs/migrations/operations.md +20 -6
  72. package/docs/migrations/recovery.md +7 -1
  73. package/docs/query-model/fragments.md +21 -1
  74. package/docs/reference/introspection-support.md +16 -18
  75. package/docs/reference/supported-surface.md +23 -11
  76. package/docs/schema/catalog-model.md +20 -7
  77. package/docs/schema/code-generation.md +4 -3
  78. package/docs/schema/diff.md +2 -2
  79. package/docs/schema/introspection.md +24 -16
  80. package/docs/schema/snapshots.md +18 -8
  81. package/docs/schema/storage-and-schema-sql.md +7 -2
  82. package/docs/sql-semantic-types.md +8 -0
  83. package/package.json +14 -1
  84. package/dist/canonical-DMvR9yBe.mjs +0 -972
  85. package/dist/column-Da37jYSD.mjs +0 -309
  86. package/dist/column-r1Y4ivwt.mjs +0 -327
  87. package/dist/complete-types-CY0KbzNw.d.mts +0 -371
  88. package/dist/index-B2rZf3-2.d.mts +0 -32
  89. package/dist/json-Db7XRD91.mjs +0 -169
  90. package/dist/omit-OxV58AwX.mjs +0 -5
  91. package/dist/registry-BRcUuazJ.mjs +0 -256
  92. package/dist/serialize-CEIIlWhC.d.mts +0 -66
  93. package/dist/snapshot-Xam8-q0j.mjs +0 -392
  94. package/dist/standard-DfcZEVOj.mjs +0 -12
  95. package/dist/value-D14I_XgL.mjs +0 -29
@@ -0,0 +1,1172 @@
1
+ import { n as createIntrospectionDiagnostic } from "../diagnostics-I9vVtXkc.mjs";
2
+ //#region src/introspection/sqlite.ts
3
+ const sqliteServerQuery = `SELECT sqlite_version() AS version, sqlite_source_id() AS source_id`;
4
+ const sqliteDatabaseListQuery = `
5
+ SELECT seq, name, file
6
+ FROM pragma_database_list()
7
+ ORDER BY seq
8
+ `;
9
+ const sqliteSchemaQuery = `
10
+ SELECT type, name, tbl_name, sql
11
+ FROM main.sqlite_schema
12
+ WHERE name NOT LIKE 'sqlite_%'
13
+ ORDER BY type, name
14
+ `;
15
+ const sqliteTempSchemaQuery = `
16
+ SELECT type, name, tbl_name, sql
17
+ FROM temp.sqlite_schema
18
+ WHERE name NOT LIKE 'sqlite_%'
19
+ ORDER BY type, name
20
+ `;
21
+ const sqliteTableListQuery = `
22
+ SELECT schema, name, type, ncol, wr, strict
23
+ FROM pragma_table_list
24
+ WHERE schema = ? AND name NOT LIKE 'sqlite_%'
25
+ ORDER BY name
26
+ `;
27
+ const sqliteTableInfoQuery = `
28
+ SELECT cid, name, type, "notnull" AS not_null, dflt_value, pk, hidden
29
+ FROM pragma_table_xinfo(?, ?)
30
+ ORDER BY cid
31
+ `;
32
+ const sqliteIndexListQuery = `
33
+ SELECT seq, name, "unique" AS unique_index, origin, partial
34
+ FROM pragma_index_list(?, ?)
35
+ ORDER BY seq
36
+ `;
37
+ const sqliteIndexInfoQuery = `
38
+ SELECT seqno, cid, name, "desc" AS descending, coll, key
39
+ FROM pragma_index_xinfo(?, ?)
40
+ ORDER BY seqno
41
+ `;
42
+ const sqliteForeignKeyQuery = `
43
+ SELECT id, seq, "table" AS target_table, "from" AS source_column,
44
+ "to" AS target_column, on_update, on_delete, match
45
+ FROM pragma_foreign_key_list(?, ?)
46
+ ORDER BY id, seq
47
+ `;
48
+ /** Read the selected SQLite database namespace into the normalized catalog. */
49
+ async function readCatalog(connection, options) {
50
+ const diagnostics = [];
51
+ if (connection.dialect !== "sqlite") {
52
+ diagnostics.push(createIntrospectionDiagnostic({
53
+ severity: "error",
54
+ code: "dialect-mismatch",
55
+ message: "SQLite catalog reading requires a SQLite CatalogConnection",
56
+ path: ["connection", "dialect"]
57
+ }));
58
+ return emptyCatalog(options.namespace, diagnostics);
59
+ }
60
+ const serverRows = await query(connection, sqliteServerQuery, [], options, diagnostics, "server");
61
+ const server = serverInfo(serverRows[0], diagnostics);
62
+ const databaseRows = await query(connection, sqliteDatabaseListQuery, [], options, diagnostics, "database-list");
63
+ const databaseNames = databaseRows.map((row) => text(row.name)).filter((name) => name !== void 0);
64
+ if (databaseNames.length > 0 && !databaseNames.includes(options.namespace)) diagnostics.push(createIntrospectionDiagnostic({
65
+ severity: "error",
66
+ code: "missing-catalog-row",
67
+ message: `SQLite database ${options.namespace} is not attached to the selected connection`,
68
+ path: ["namespace", options.namespace],
69
+ remediation: "Attach the database before starting introspection."
70
+ }));
71
+ const schemaRows = options.namespace === "main" || options.namespace === "temp" ? await query(connection, options.namespace === "temp" ? sqliteTempSchemaQuery : sqliteSchemaQuery, [], options, diagnostics, "schema") : [];
72
+ if (options.namespace !== "main" && options.namespace !== "temp" && databaseNames.includes(options.namespace)) diagnostics.push(createIntrospectionDiagnostic({
73
+ severity: "warning",
74
+ code: "partial-result",
75
+ message: `SQLite attached database ${options.namespace} exposes table PRAGMAs but its schema SQL is outside the fixed main/temp catalog statements`,
76
+ path: [
77
+ "namespace",
78
+ options.namespace,
79
+ "schema"
80
+ ],
81
+ remediation: "Use a driver adapter that exposes the attached schema through a fixed statement before relying on generated SQL."
82
+ }));
83
+ const tableRows = await query(connection, sqliteTableListQuery, [options.namespace], options, diagnostics, "table-list");
84
+ const tableSql = new Map(schemaRows.map((row) => [text(row.name), text(row.sql)]).filter((entry) => entry[0] !== void 0));
85
+ const tables = tableRows.filter((row) => normalizeType(row.type) === "table").map((row) => table(row, tableSql.get(text(row.name) ?? "") ?? void 0, options.namespace));
86
+ const tableByName = new Map(tables.map((currentTable) => [currentTable.physicalName, currentTable]));
87
+ const deferredObjects = [];
88
+ const opaqueObjects = [];
89
+ for (const row of tableRows) {
90
+ const type = normalizeType(row.type);
91
+ if (type !== "virtual" && type !== "shadow") continue;
92
+ const physicalName = text(row.name) ?? "unnamed_object";
93
+ deferredObjects.push(deferred(type === "virtual" ? "virtual-table" : "shadow-table", physicalName, tableSql.get(physicalName), options.namespace));
94
+ diagnostics.push(createIntrospectionDiagnostic({
95
+ severity: "warning",
96
+ code: "unmodeled-object",
97
+ message: `SQLite ${type} table ${physicalName} is retained as a deferred object`,
98
+ path: ["deferredObjects", physicalName],
99
+ physicalReference: reference("deferred-object", physicalName, options.namespace, "pragma_table_list", "name"),
100
+ remediation: "Inspect the deferred record before using it as migration input."
101
+ }));
102
+ }
103
+ for (const row of databaseRows) {
104
+ const name = text(row.name);
105
+ if (!name || name === options.namespace || name === "main" || name === "temp") continue;
106
+ const boundary = attachedDatabaseObject(row, options.namespace);
107
+ opaqueObjects.push(boundary);
108
+ diagnostics.push(createIntrospectionDiagnostic({
109
+ severity: "info",
110
+ code: "partial-result",
111
+ message: `Attached SQLite database ${name} is outside the selected namespace`,
112
+ path: [
113
+ "namespace",
114
+ "attached",
115
+ name
116
+ ],
117
+ physicalReference: boundary.reference,
118
+ remediation: "Run a separate catalog read with this database name to inspect it."
119
+ }));
120
+ }
121
+ const views = [];
122
+ for (const row of tableRows.filter((row) => normalizeType(row.type) === "view")) {
123
+ const physicalName = text(row.name) ?? "unnamed_view";
124
+ const view = mapView(row, tableSql.get(physicalName), options.namespace, diagnostics);
125
+ if (view.kind === "deferred-object") deferredObjects.push(view);
126
+ else views.push(view);
127
+ }
128
+ const relationReferences = /* @__PURE__ */ new Map();
129
+ for (const table of tables) relationReferences.set(table.physicalName, {
130
+ kind: "table",
131
+ id: table.id
132
+ });
133
+ for (const view of views) relationReferences.set(view.physicalName, {
134
+ kind: view.kind,
135
+ id: view.id
136
+ });
137
+ const triggers = [];
138
+ for (const currentTable of tables) {
139
+ const tableName = currentTable.physicalName;
140
+ const rows = await query(connection, sqliteTableInfoQuery, [tableName, options.namespace], options, diagnostics, `table-xinfo:${tableName}`);
141
+ const sqlText = tableSql.get(tableName);
142
+ const rowidPrimaryKey = rows.filter((row) => (number(row.pk) ?? 0) > 0).length === 1;
143
+ const columns = rows.filter((row) => number(row.hidden) !== 1).map((row) => column(row, currentTable, options.namespace, sqlText, rowidPrimaryKey, diagnostics));
144
+ currentTable.columns = columns;
145
+ currentTable.constraints = tableConstraints(currentTable, rows, columns, sqlText, options.namespace, diagnostics, opaqueObjects);
146
+ }
147
+ for (const currentTable of tables) {
148
+ const tableName = currentTable.physicalName;
149
+ const sqlText = tableSql.get(tableName);
150
+ const indexRows = await query(connection, sqliteIndexListQuery, [tableName, options.namespace], options, diagnostics, `index-list:${tableName}`);
151
+ const mappedIndexes = [];
152
+ const mappedConstraints = [...currentTable.constraints];
153
+ for (const indexRow of indexRows) {
154
+ const indexName = text(indexRow.name);
155
+ if (!indexName || indexName.startsWith("sqlite_autoindex_") && text(indexRow.origin)?.toLowerCase() !== "u") continue;
156
+ const index = mapIndex(currentTable, indexRow, await query(connection, sqliteIndexInfoQuery, [indexName, options.namespace], options, diagnostics, `index-info:${indexName}`), text(indexRow.origin)?.toLowerCase() === "u" ? sqlText : tableSql.get(indexName), options.namespace, diagnostics, opaqueObjects);
157
+ if (!index) continue;
158
+ if (index.kind === "index") mappedIndexes.push(index);
159
+ else mappedConstraints.push(index);
160
+ }
161
+ const foreignRows = await query(connection, sqliteForeignKeyQuery, [tableName, options.namespace], options, diagnostics, `foreign-key:${tableName}`);
162
+ for (const value of foreignKeys(currentTable, foreignRows, tableByName, options.namespace, diagnostics)) if (value.kind === "opaque-object") opaqueObjects.push(value);
163
+ else mappedConstraints.push(value);
164
+ currentTable.indexes = mappedIndexes;
165
+ currentTable.constraints = mappedConstraints;
166
+ }
167
+ for (const view of views) view.columns = (await query(connection, sqliteTableInfoQuery, [view.physicalName, options.namespace], options, diagnostics, `view-xinfo:${view.physicalName}`)).filter((row) => number(row.hidden) !== 1).map((row) => column(row, view, options.namespace, void 0, false, diagnostics));
168
+ for (const row of schemaRows.filter((row) => normalizeType(row.type) === "trigger")) {
169
+ const trigger = mapTrigger(row, options.namespace, relationReferences, diagnostics);
170
+ if (trigger.kind === "deferred-object") deferredObjects.push(trigger);
171
+ else triggers.push(trigger);
172
+ }
173
+ for (const row of schemaRows) {
174
+ const type = normalizeType(row.type);
175
+ if (type === "table" || type === "view" || type === "trigger" || type === "index") continue;
176
+ const physicalName = text(row.name) ?? "unnamed_object";
177
+ opaqueObjects.push(opaqueSchemaObject(row, options.namespace, diagnostics));
178
+ diagnostics.push(createIntrospectionDiagnostic({
179
+ severity: "warning",
180
+ code: "unmodeled-object",
181
+ message: `SQLite schema object ${physicalName} (${type || "unknown"}) is retained as opaque data`,
182
+ path: ["opaqueObjects", physicalName],
183
+ physicalReference: reference("opaque-object", physicalName, options.namespace, "sqlite_schema", "name")
184
+ }));
185
+ }
186
+ const visibility = databaseRows.length > 0 && options.namespace !== "main" && options.namespace !== "temp" ? "limited" : server.capabilities.visibility;
187
+ const capabilities = {
188
+ generatedColumns: server.capabilities.generatedColumns,
189
+ identityMetadata: server.capabilities.identityMetadata,
190
+ checkConstraints: server.capabilities.checkConstraints,
191
+ checkConstraintEnforcement: server.capabilities.checkConstraintEnforcement,
192
+ expressionDecompilation: server.capabilities.expressionDecompilation,
193
+ indexExpressions: server.capabilities.indexExpressions,
194
+ indexPredicates: server.capabilities.indexPredicates,
195
+ indexIncludedColumns: server.capabilities.indexIncludedColumns,
196
+ namespaces: server.capabilities.namespaces,
197
+ visibility
198
+ };
199
+ const serverCapabilities = {
200
+ ...server.capabilities,
201
+ attachedDatabases: true,
202
+ views: true,
203
+ triggers: true,
204
+ virtualTables: true,
205
+ shadowTables: true,
206
+ comments: false,
207
+ ownership: false,
208
+ typedExtensions: true,
209
+ affinityFacts: true,
210
+ generatedSql: true,
211
+ selectedNamespace: options.namespace,
212
+ visibility
213
+ };
214
+ return Object.freeze({
215
+ dialect: "sqlite",
216
+ server: {
217
+ ...server,
218
+ capabilities: serverCapabilities
219
+ },
220
+ namespace: {
221
+ kind: "sqlite-database",
222
+ name: options.namespace,
223
+ reference: reference("namespace", options.namespace, options.namespace, "sqlite_schema", "name"),
224
+ dialect: extension({
225
+ selectedNamespace: options.namespace,
226
+ sourceIdAvailable: serverSourceIdAvailable(serverRows[0]),
227
+ views: true,
228
+ triggers: true,
229
+ virtualTables: true,
230
+ shadowTables: true,
231
+ comments: false,
232
+ ownership: false,
233
+ typedExtensions: true,
234
+ affinityFacts: true,
235
+ generatedSql: true,
236
+ visibility,
237
+ attachedDatabases: databaseNames.filter((name) => name !== options.namespace)
238
+ })
239
+ },
240
+ tables: Object.freeze(tables),
241
+ views: Object.freeze(views),
242
+ triggers: Object.freeze(triggers),
243
+ deferredObjects: Object.freeze(deferredObjects),
244
+ opaqueObjects: Object.freeze(opaqueObjects),
245
+ capabilities,
246
+ diagnostics: Object.freeze(diagnostics)
247
+ });
248
+ }
249
+ async function query(connection, textValue, parameters, options, diagnostics, operation) {
250
+ try {
251
+ return await connection.query({
252
+ text: textValue,
253
+ parameters
254
+ }, { signal: options.signal });
255
+ } catch {
256
+ diagnostics.push(createIntrospectionDiagnostic({
257
+ severity: "error",
258
+ code: "query-failed",
259
+ message: `SQLite catalog query failed while reading ${operation}`,
260
+ path: [operation],
261
+ remediation: "Check SQLite metadata visibility and the selected database."
262
+ }));
263
+ return [];
264
+ }
265
+ }
266
+ function serverInfo(row, diagnostics) {
267
+ const rawVersion = text(row?.version) ?? "unknown";
268
+ const parts = rawVersion.split(".").map((item) => Number(item));
269
+ const supported = parts.length >= 2 && parts[0] === 3 && parts[1] >= 37;
270
+ if (!supported) diagnostics.push(createIntrospectionDiagnostic({
271
+ severity: "error",
272
+ code: "unsupported-server",
273
+ message: "SQLite introspection requires SQLite 3.37 or newer",
274
+ path: ["server", "version"],
275
+ remediation: "Use SQLite 3.37+ or provide a compatible adapter capability set."
276
+ }));
277
+ return {
278
+ product: "sqlite",
279
+ rawVersion,
280
+ parsedVersion: Number.isFinite(parts[0]) ? {
281
+ major: parts[0],
282
+ minor: parts[1],
283
+ patch: parts[2]
284
+ } : void 0,
285
+ capabilities: {
286
+ generatedColumns: supported,
287
+ identityMetadata: true,
288
+ checkConstraints: true,
289
+ checkConstraintEnforcement: "enforced",
290
+ expressionDecompilation: false,
291
+ indexExpressions: true,
292
+ indexPredicates: true,
293
+ indexIncludedColumns: false,
294
+ namespaces: false,
295
+ visibility: "complete",
296
+ sourceIdAvailable: text(row?.source_id) !== void 0
297
+ }
298
+ };
299
+ }
300
+ function serverSourceIdAvailable(row) {
301
+ return text(row?.source_id) !== void 0;
302
+ }
303
+ function normalizeType(value) {
304
+ return text(value)?.trim().toLowerCase() ?? "";
305
+ }
306
+ function mapView(row, sqlText, namespace, diagnostics) {
307
+ const physicalName = text(row.name) ?? "unnamed_view";
308
+ const physicalReference = reference("view", physicalName, namespace, "sqlite_schema", "name");
309
+ const definition = viewDefinition(sqlText);
310
+ if (!definition) {
311
+ diagnostics.push(createIntrospectionDiagnostic({
312
+ severity: "error",
313
+ code: "expression-parse-failed",
314
+ message: `SQLite view ${physicalName} has no recoverable SELECT definition`,
315
+ path: [
316
+ "views",
317
+ physicalName,
318
+ "definition"
319
+ ],
320
+ physicalReference,
321
+ remediation: "Preserve the CREATE VIEW SQL returned by sqlite_schema."
322
+ }));
323
+ return deferred("view", physicalName, sqlText, namespace);
324
+ }
325
+ return {
326
+ kind: "view",
327
+ id: stableId(physicalName),
328
+ identitySource: "physical-name",
329
+ physicalName,
330
+ columns: [],
331
+ definition: sql(definition, namespace, {
332
+ kind: "view",
333
+ physicalName,
334
+ reference: physicalReference
335
+ }, physicalName),
336
+ reference: physicalReference,
337
+ provenance: {
338
+ kind: "create-sql",
339
+ dialect: "sqlite",
340
+ reference: physicalReference
341
+ },
342
+ dialect: extension({ objectKind: "view" })
343
+ };
344
+ }
345
+ function viewDefinition(sqlText) {
346
+ if (!sqlText) return;
347
+ return sqlText.match(/\bAS\b([\s\S]*)$/i)?.[1]?.trim().replace(/;\s*$/, "") || void 0;
348
+ }
349
+ function attachedDatabaseObject(row, selectedNamespace) {
350
+ const physicalName = text(row.name) ?? "unnamed_database";
351
+ const physicalReference = reference("opaque-object", physicalName, selectedNamespace, "pragma_database_list", "name");
352
+ return {
353
+ kind: "opaque-object",
354
+ id: stableId(`attached:${physicalName}`),
355
+ identitySource: "physical-name",
356
+ objectKind: "attached-database",
357
+ physicalName,
358
+ data: {
359
+ ...number(row.seq) === void 0 ? {} : { sequence: number(row.seq) },
360
+ ...text(row.file) === void 0 ? {} : { file: text(row.file) },
361
+ selected: false
362
+ },
363
+ reference: physicalReference,
364
+ provenance: {
365
+ kind: "catalog",
366
+ dialect: "sqlite",
367
+ reference: physicalReference
368
+ },
369
+ dialect: extension({ selectedNamespace })
370
+ };
371
+ }
372
+ function opaqueSchemaObject(row, namespace, _diagnostics) {
373
+ const physicalName = text(row.name) ?? "unnamed_object";
374
+ const type = normalizeType(row.type) || "unknown";
375
+ const physicalReference = reference("opaque-object", physicalName, namespace, "sqlite_schema", "name");
376
+ const sqlText = text(row.sql);
377
+ return {
378
+ kind: "opaque-object",
379
+ id: stableId(`opaque:${type}:${physicalName}`),
380
+ identitySource: "physical-name",
381
+ objectKind: type,
382
+ physicalName,
383
+ data: {
384
+ type,
385
+ ...text(row.tbl_name) === void 0 ? {} : { tableName: text(row.tbl_name) }
386
+ },
387
+ ...sqlText === void 0 ? {} : { sql: {
388
+ kind: "sql",
389
+ dialect: "sqlite",
390
+ text: sqlText,
391
+ provenance: {
392
+ kind: "create-sql",
393
+ dialect: "sqlite",
394
+ reference: physicalReference
395
+ }
396
+ } },
397
+ reference: physicalReference,
398
+ provenance: {
399
+ kind: "catalog",
400
+ dialect: "sqlite",
401
+ reference: physicalReference
402
+ },
403
+ dialect: extension({ objectKind: type })
404
+ };
405
+ }
406
+ function mapTrigger(row, namespace, relations, diagnostics) {
407
+ const physicalName = text(row.name) ?? "unnamed_trigger";
408
+ const sqlText = text(row.sql);
409
+ const physicalReference = reference("trigger", physicalName, namespace, "sqlite_schema", "name");
410
+ const onMatch = sqlText?.match(/\bON\s+("(?:[^"]|"")*"|`[^`]*`|\[[^\]]*\]|[A-Za-z_][A-Za-z0-9_$]*)/i);
411
+ const targetName = unquoteIdentifier(onMatch?.[1]) ?? text(row.tbl_name);
412
+ const target = targetName === void 0 ? void 0 : relations.get(targetName);
413
+ const beginIndex = sqlText?.search(/\bBEGIN\b/i) ?? -1;
414
+ const header = sqlText === void 0 || beginIndex < 0 ? void 0 : sqlText.slice(0, beginIndex);
415
+ const timing = triggerTiming(header);
416
+ const events = triggerEvents(header);
417
+ if (!sqlText || !target || !header || beginIndex < 0) {
418
+ diagnostics.push(createIntrospectionDiagnostic({
419
+ severity: "error",
420
+ code: "unresolved-reference",
421
+ message: `SQLite trigger ${physicalName} does not have a recoverable body or target`,
422
+ path: ["triggers", physicalName],
423
+ physicalReference,
424
+ remediation: "Retain the CREATE TRIGGER SQL and inspect the trigger after its target is visible."
425
+ }));
426
+ return deferred("trigger", physicalName, sqlText, namespace);
427
+ }
428
+ if (events.length === 0) diagnostics.push(createIntrospectionDiagnostic({
429
+ severity: "warning",
430
+ code: "unsupported-feature",
431
+ message: `SQLite trigger ${physicalName} has no recognized event`,
432
+ path: [
433
+ "triggers",
434
+ physicalName,
435
+ "events"
436
+ ],
437
+ physicalReference
438
+ }));
439
+ const bodyText = sqlText.slice(beginIndex + 5).replace(/END\s*;?\s*$/i, "").trim();
440
+ const condition = triggerCondition(sqlText, onMatch?.index, beginIndex);
441
+ return {
442
+ kind: "trigger",
443
+ id: stableId(physicalName),
444
+ identitySource: "physical-name",
445
+ physicalName,
446
+ table: target,
447
+ timing,
448
+ events,
449
+ orientation: /\bFOR\s+EACH\s+ROW\b/i.test(header) ? "row" : void 0,
450
+ ...condition === void 0 ? {} : { condition: sql(condition, namespace, {
451
+ kind: target.kind,
452
+ physicalName: target.id,
453
+ reference: physicalReference
454
+ }, physicalName) },
455
+ body: sql(bodyText || sqlText, namespace, {
456
+ kind: "trigger",
457
+ physicalName,
458
+ reference: physicalReference
459
+ }, physicalName),
460
+ reference: physicalReference,
461
+ provenance: {
462
+ kind: "create-sql",
463
+ dialect: "sqlite",
464
+ reference: physicalReference
465
+ },
466
+ dialect: extension({
467
+ objectKind: "trigger",
468
+ ...sqlText === void 0 ? {} : { rawSql: sqlText }
469
+ })
470
+ };
471
+ }
472
+ function triggerTiming(header) {
473
+ const value = header?.match(/\b(INSTEAD\s+OF|BEFORE|AFTER)\b/i)?.[1];
474
+ return value?.toLowerCase() === "before" ? "before" : value?.toLowerCase() === "after" ? "after" : value?.toLowerCase() === "instead of" ? "instead-of" : "unknown";
475
+ }
476
+ function triggerEvents(header) {
477
+ const values = header?.match(/\b(INSERT|UPDATE|DELETE)\b/gi) ?? [];
478
+ return [...new Set(values.map((value) => value.toLowerCase()))];
479
+ }
480
+ function triggerCondition(sqlText, _onIndex, beginIndex) {
481
+ return sqlText.slice(0, beginIndex).match(/\bWHEN\s+([\s\S]+)$/i)?.[1]?.trim();
482
+ }
483
+ function unquoteIdentifier(value) {
484
+ if (!value) return;
485
+ const trimmed = value.trim();
486
+ if (trimmed.length < 2) return trimmed;
487
+ if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("`") && trimmed.endsWith("`")) return trimmed.slice(1, -1).replace(/(""|``)/g, (match) => match[0]);
488
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) return trimmed.slice(1, -1);
489
+ return trimmed;
490
+ }
491
+ function affinity(type) {
492
+ const normalized = type.trim().toUpperCase();
493
+ if (/INT/.test(normalized)) return "INTEGER";
494
+ if (/CHAR|CLOB|TEXT/.test(normalized)) return "TEXT";
495
+ if (/REAL|FLOA|DOUB/.test(normalized)) return "REAL";
496
+ if (normalized === "" || normalized === "BLOB") return "BLOB";
497
+ return "NUMERIC";
498
+ }
499
+ function extension(data) {
500
+ return {
501
+ dialect: "sqlite",
502
+ version: 1,
503
+ data
504
+ };
505
+ }
506
+ function indexExpressionFor(sqlText, position) {
507
+ if (!sqlText) return;
508
+ const open = sqlText.indexOf("(", sqlText.search(/\bON\b/i));
509
+ if (open < 0) return;
510
+ const close = matchingParen(sqlText, open);
511
+ if (close < 0) return;
512
+ const term = splitSqlList(sqlText.slice(open + 1, close))[position]?.trim();
513
+ return term && !isSimpleIdentifier(term) ? term : void 0;
514
+ }
515
+ function opaqueIndexExpression(table, indexName, position, info, sqlText, namespace) {
516
+ const physicalReference = reference("opaque-object", `${indexName}:${position}`, namespace, "pragma_index_xinfo", "seqno", position);
517
+ return {
518
+ kind: "opaque-object",
519
+ id: stableId(`index-expression:${table.physicalName}:${indexName}:${position}`),
520
+ identitySource: "deterministic-fallback",
521
+ objectKind: "index-expression-term",
522
+ physicalName: `${indexName}:${position}`,
523
+ data: {
524
+ index: indexName,
525
+ position,
526
+ cid: number(info.cid) ?? -2
527
+ },
528
+ ...sqlText === void 0 ? {} : { sql: sql(sqlText, namespace, table, indexName) },
529
+ reference: physicalReference,
530
+ provenance: {
531
+ kind: "catalog",
532
+ dialect: "sqlite",
533
+ reference: physicalReference
534
+ },
535
+ dialect: extension({ objectKind: "index-expression-term" })
536
+ };
537
+ }
538
+ function matchingParen(source, open) {
539
+ let depth = 0;
540
+ let quote;
541
+ for (let index = open; index < source.length; index++) {
542
+ const character = source[index];
543
+ if (quote === "single") {
544
+ if (character === "'" && source[index + 1] === "'") index++;
545
+ else if (character === "'") quote = void 0;
546
+ continue;
547
+ }
548
+ if (quote === "double") {
549
+ if (character === "\"" && source[index + 1] === "\"") index++;
550
+ else if (character === "\"") quote = void 0;
551
+ continue;
552
+ }
553
+ if (quote === "backtick") {
554
+ if (character === "`") quote = void 0;
555
+ continue;
556
+ }
557
+ if (quote === "bracket") {
558
+ if (character === "]") quote = void 0;
559
+ continue;
560
+ }
561
+ if (character === "'") quote = "single";
562
+ else if (character === "\"") quote = "double";
563
+ else if (character === "`") quote = "backtick";
564
+ else if (character === "[") quote = "bracket";
565
+ else if (character === "(") depth++;
566
+ else if (character === ")" && --depth === 0) return index;
567
+ }
568
+ return -1;
569
+ }
570
+ function splitSqlList(source) {
571
+ const values = [];
572
+ let start = 0;
573
+ let depth = 0;
574
+ let quote;
575
+ for (let index = 0; index < source.length; index++) {
576
+ const character = source[index];
577
+ if (quote === "single") {
578
+ if (character === "'" && source[index + 1] === "'") index++;
579
+ else if (character === "'") quote = void 0;
580
+ continue;
581
+ }
582
+ if (quote === "double") {
583
+ if (character === "\"" && source[index + 1] === "\"") index++;
584
+ else if (character === "\"") quote = void 0;
585
+ continue;
586
+ }
587
+ if (quote === "backtick") {
588
+ if (character === "`") quote = void 0;
589
+ continue;
590
+ }
591
+ if (quote === "bracket") {
592
+ if (character === "]") quote = void 0;
593
+ continue;
594
+ }
595
+ if (character === "'") quote = "single";
596
+ else if (character === "\"") quote = "double";
597
+ else if (character === "`") quote = "backtick";
598
+ else if (character === "[") quote = "bracket";
599
+ else if (character === "(") depth++;
600
+ else if (character === ")") depth--;
601
+ else if (character === "," && depth === 0) {
602
+ values.push(source.slice(start, index));
603
+ start = index + 1;
604
+ }
605
+ }
606
+ values.push(source.slice(start));
607
+ return values;
608
+ }
609
+ function isSimpleIdentifier(value) {
610
+ return /^(?:[A-Za-z_][A-Za-z0-9_$]*|"(?:[^"]|"")*"|`[^`]*`|\[[^\]]*\])(?:\s+(?:ASC|DESC))?$/i.test(value);
611
+ }
612
+ function table(row, sqlText, namespace) {
613
+ const physicalName = text(row.name) ?? "unnamed_table";
614
+ const withoutRowid = boolean(row.wr);
615
+ const strict = boolean(row.strict);
616
+ return {
617
+ kind: "table",
618
+ id: stableId(physicalName),
619
+ identitySource: "physical-name",
620
+ physicalName,
621
+ reference: reference("table", physicalName, namespace, "sqlite_schema", "name"),
622
+ columns: [],
623
+ constraints: [],
624
+ indexes: [],
625
+ dialect: extension({
626
+ tableType: "table",
627
+ withoutRowid,
628
+ strict,
629
+ rowid: !withoutRowid
630
+ }),
631
+ unknownFields: [
632
+ ...withoutRowid ? [{
633
+ name: "withoutRowid",
634
+ value: true
635
+ }] : [],
636
+ ...strict ? [{
637
+ name: "strict",
638
+ value: true
639
+ }] : [],
640
+ ...sqlText ? [{
641
+ name: "createSql",
642
+ value: sqlText
643
+ }] : []
644
+ ]
645
+ };
646
+ }
647
+ function deferred(objectKind, physicalName, sqlText, namespace) {
648
+ return {
649
+ kind: "deferred-object",
650
+ id: stableId(`deferred:${objectKind}:${physicalName}`),
651
+ identitySource: "physical-name",
652
+ objectKind,
653
+ physicalName,
654
+ reference: reference("deferred-object", physicalName, namespace, "sqlite_schema", "name"),
655
+ dialect: extension({ objectKind }),
656
+ unknownFields: sqlText ? [{
657
+ name: "createSql",
658
+ value: sqlText
659
+ }] : void 0
660
+ };
661
+ }
662
+ function column(row, table, namespace, sqlText, rowidPrimaryKey, diagnostics) {
663
+ const physicalName = text(row.name) ?? "unnamed_column";
664
+ const hidden = number(row.hidden) ?? 0;
665
+ const type = text(row.type) ?? "";
666
+ const defaultValue = text(row.dflt_value);
667
+ const generatedExpression = hidden === 2 || hidden === 3 ? generatedExpressionFor(sqlText, physicalName) : void 0;
668
+ if ((hidden === 2 || hidden === 3) && !generatedExpression) diagnostics.push(createIntrospectionDiagnostic({
669
+ severity: "error",
670
+ code: "expression-parse-failed",
671
+ message: `SQLite generated expression for ${physicalName} could not be recovered`,
672
+ path: [
673
+ table.id,
674
+ "columns",
675
+ physicalName,
676
+ "generated"
677
+ ],
678
+ remediation: "Preserve the CREATE TABLE SQL or use lossy mode."
679
+ }));
680
+ const rowid = table.kind === "table" && rowidPrimaryKey && (number(row.pk) ?? 0) > 0 && type.toUpperCase() === "INTEGER" && !/WITHOUT\s+ROWID/i.test(sqlText ?? "");
681
+ const identity = rowid ? {
682
+ kind: "identity",
683
+ generation: "by-default",
684
+ options: {},
685
+ dialect: {
686
+ dialect: "sqlite",
687
+ version: 1,
688
+ data: {
689
+ rowidAlias: true,
690
+ autoIncrement: /AUTOINCREMENT/i.test(sqlText ?? ""),
691
+ withoutRowid: false
692
+ }
693
+ }
694
+ } : void 0;
695
+ return {
696
+ kind: "column",
697
+ id: stableId(physicalName),
698
+ identitySource: "physical-name",
699
+ physicalName,
700
+ ordinalPosition: (number(row.cid) ?? 0) + 1,
701
+ nullable: !boolean(row.not_null) && !(rowid || (number(row.pk) ?? 0) > 0 && (/WITHOUT\s+ROWID/i.test(sqlText ?? "") || /\bSTRICT\b/i.test(sqlText ?? ""))),
702
+ storage: { nativeType: type || "BLOB" },
703
+ default: defaultValue !== void 0 && !generatedExpression ? {
704
+ kind: "expression",
705
+ expression: sql(defaultValue, namespace, table, physicalName)
706
+ } : void 0,
707
+ generated: generatedExpression ? {
708
+ kind: "generated",
709
+ mode: hidden === 2 ? "virtual" : "stored",
710
+ expression: sql(generatedExpression, namespace, table, physicalName)
711
+ } : void 0,
712
+ identity,
713
+ reference: reference("column", physicalName, namespace, "pragma_table_xinfo", "name"),
714
+ dialect: extension({
715
+ declaredType: type,
716
+ affinity: affinity(type),
717
+ hidden
718
+ })
719
+ };
720
+ }
721
+ function tableConstraints(table, rows, columns, sqlText, namespace, diagnostics, opaqueObjects) {
722
+ const primaryNames = new Map(rows.filter((row) => (number(row.pk) ?? 0) > 0).map((row) => [text(row.name), number(row.pk) ?? 0]));
723
+ const primaryColumns = columns.filter((column) => primaryNames.has(column.physicalName)).sort((left, right) => primaryNames.get(left.physicalName) - primaryNames.get(right.physicalName));
724
+ const constraints = [];
725
+ if (primaryColumns.length > 0) {
726
+ const declaredName = declaredConstraintName(sqlText, "PRIMARY\\s+KEY", primaryColumns.map((column) => column.physicalName));
727
+ const primaryName = declaredName ?? `primary_${table.physicalName}`;
728
+ const primary = {
729
+ kind: "primary-key",
730
+ id: stableId(primaryName),
731
+ identitySource: declaredName ? "physical-name" : "deterministic-fallback",
732
+ physicalName: primaryName,
733
+ columns: primaryColumns.map((column) => column.physicalName),
734
+ dialect: extension({ source: "pragma_table_xinfo" }),
735
+ reference: reference("constraint", primaryName, namespace, "sqlite_schema", "tbl_name")
736
+ };
737
+ constraints.push(primary);
738
+ }
739
+ const checkResult = checkExpressions(sqlText);
740
+ checkResult.values.forEach((checkValue, index) => {
741
+ const name = checkValue.name ?? `check_${index}_${table.physicalName}`;
742
+ const check = {
743
+ kind: "check",
744
+ id: stableId(name),
745
+ identitySource: "deterministic-fallback",
746
+ physicalName: name,
747
+ expression: sql(checkValue.expression, namespace, table, name),
748
+ dialect: extension({ source: "create-sql" })
749
+ };
750
+ constraints.push(check);
751
+ });
752
+ if (checkResult.unrecoverable) {
753
+ const name = checkResult.unrecoverableName ?? `check_${checkResult.values.length}_${table.physicalName}`;
754
+ diagnostics.push(createIntrospectionDiagnostic({
755
+ severity: "error",
756
+ code: "expression-parse-failed",
757
+ message: `SQLite CHECK constraint ${name} could not be recovered from CREATE TABLE SQL`,
758
+ path: [
759
+ table.id,
760
+ "constraints",
761
+ name
762
+ ],
763
+ physicalReference: reference("opaque-object", name, namespace, "sqlite_schema", "name"),
764
+ remediation: "Preserve the complete CREATE TABLE SQL or use lossy mode."
765
+ }));
766
+ opaqueObjects.push(opaqueCheckConstraint(table, name, sqlText, namespace));
767
+ }
768
+ return constraints;
769
+ }
770
+ function checkExpressions(sqlText) {
771
+ if (!sqlText) return {
772
+ values: [],
773
+ unrecoverable: false
774
+ };
775
+ const values = [];
776
+ let offset = 0;
777
+ while (offset < sqlText.length) {
778
+ const checkIndex = findSqlKeyword(sqlText, "CHECK", offset);
779
+ if (checkIndex < 0) return {
780
+ values,
781
+ unrecoverable: false
782
+ };
783
+ let open = checkIndex + 5;
784
+ while (/\s/.test(sqlText[open] ?? "")) open++;
785
+ if (sqlText[open] !== "(") {
786
+ offset = checkIndex + 5;
787
+ continue;
788
+ }
789
+ const close = matchingParen(sqlText, open);
790
+ if (close < 0) return {
791
+ values,
792
+ unrecoverable: true,
793
+ unrecoverableName: checkConstraintName(sqlText, checkIndex)
794
+ };
795
+ values.push({
796
+ name: checkConstraintName(sqlText, checkIndex),
797
+ expression: sqlText.slice(open + 1, close).trim()
798
+ });
799
+ offset = close + 1;
800
+ }
801
+ return {
802
+ values,
803
+ unrecoverable: false
804
+ };
805
+ }
806
+ function findSqlKeyword(source, keyword, start) {
807
+ let quote;
808
+ for (let index = start; index < source.length; index++) {
809
+ const character = source[index];
810
+ if (quote === "single") {
811
+ if (character === "'" && source[index + 1] === "'") index++;
812
+ else if (character === "'") quote = void 0;
813
+ continue;
814
+ }
815
+ if (quote === "double") {
816
+ if (character === "\"" && source[index + 1] === "\"") index++;
817
+ else if (character === "\"") quote = void 0;
818
+ continue;
819
+ }
820
+ if (quote === "backtick") {
821
+ if (character === "`") quote = void 0;
822
+ continue;
823
+ }
824
+ if (quote === "bracket") {
825
+ if (character === "]") quote = void 0;
826
+ continue;
827
+ }
828
+ if (character === "'") {
829
+ quote = "single";
830
+ continue;
831
+ }
832
+ if (character === "\"") {
833
+ quote = "double";
834
+ continue;
835
+ }
836
+ if (character === "`") {
837
+ quote = "backtick";
838
+ continue;
839
+ }
840
+ if (character === "[") {
841
+ quote = "bracket";
842
+ continue;
843
+ }
844
+ if (source.slice(index, index + keyword.length).toUpperCase() === keyword.toUpperCase() && !isSqlIdentifierCharacter(source[index - 1]) && !isSqlIdentifierCharacter(source[index + keyword.length])) return index;
845
+ }
846
+ return -1;
847
+ }
848
+ function isSqlIdentifierCharacter(value) {
849
+ return value !== void 0 && /[A-Za-z0-9_$]/.test(value);
850
+ }
851
+ function checkConstraintName(source, checkIndex) {
852
+ return unquoteIdentifier(source.slice(0, checkIndex).match(/CONSTRAINT\s+("(?:[^"]|"")*"|`[^`]*`|\[[^\]]*\]|[A-Za-z_][A-Za-z0-9_$]*)\s*$/i)?.[1]);
853
+ }
854
+ function opaqueCheckConstraint(table, constraintName, sqlText, namespace) {
855
+ const physicalReference = reference("opaque-object", constraintName, namespace, "sqlite_schema", "name", table.physicalName);
856
+ return {
857
+ kind: "opaque-object",
858
+ id: stableId(`check:${table.physicalName}:${constraintName}`),
859
+ identitySource: "deterministic-fallback",
860
+ objectKind: "check-constraint",
861
+ physicalName: constraintName,
862
+ data: {
863
+ table: table.physicalName,
864
+ reason: "definition-unavailable"
865
+ },
866
+ ...sqlText === void 0 ? {} : { sql: sql(sqlText, namespace, table, constraintName) },
867
+ reference: physicalReference,
868
+ provenance: {
869
+ kind: "catalog",
870
+ dialect: "sqlite",
871
+ reference: physicalReference
872
+ }
873
+ };
874
+ }
875
+ function mapIndex(table, row, infoRows, sqlText, namespace, diagnostics, opaqueObjects) {
876
+ const physicalName = text(row.name);
877
+ if (!physicalName) return;
878
+ const terms = [];
879
+ const collations = [];
880
+ for (const info of infoRows.filter((item) => boolean(item.key))) {
881
+ const position = number(info.seqno) ?? 0;
882
+ const cid = number(info.cid) ?? -1;
883
+ const collation = text(info.coll);
884
+ if (collation) collations.push(collation);
885
+ if (cid === -1) continue;
886
+ if (cid === -2) {
887
+ const expressionText = indexExpressionFor(sqlText, position);
888
+ if (expressionText) terms.push({
889
+ kind: "expression",
890
+ expression: sql(expressionText, namespace, table, physicalName),
891
+ position,
892
+ direction: boolean(info.descending) ? "DESC" : "ASC"
893
+ });
894
+ else {
895
+ opaqueObjects.push(opaqueIndexExpression(table, physicalName, position, info, sqlText, namespace));
896
+ diagnostics.push(createIntrospectionDiagnostic({
897
+ severity: "error",
898
+ code: "unsupported-feature",
899
+ message: `SQLite index ${physicalName} has an expression term that was not recovered`,
900
+ path: [
901
+ table.id,
902
+ "indexes",
903
+ physicalName,
904
+ "terms",
905
+ position
906
+ ],
907
+ physicalReference: reference("index", physicalName, namespace, "sqlite_schema", "name"),
908
+ remediation: "Preserve the CREATE INDEX SQL for expression terms."
909
+ }));
910
+ }
911
+ continue;
912
+ }
913
+ const columnName = text(info.name);
914
+ if (cid >= 0 && columnName) terms.push({
915
+ kind: "column",
916
+ column: columnName,
917
+ position,
918
+ direction: boolean(info.descending) ? "DESC" : "ASC"
919
+ });
920
+ else diagnostics.push(createIntrospectionDiagnostic({
921
+ severity: "error",
922
+ code: "unsupported-feature",
923
+ message: `SQLite index ${physicalName} has an expression term that was not recovered`,
924
+ path: [
925
+ table.id,
926
+ "indexes",
927
+ physicalName,
928
+ "terms",
929
+ position
930
+ ],
931
+ physicalReference: reference("index", physicalName, namespace, "sqlite_schema", "name")
932
+ }));
933
+ }
934
+ const unique = boolean(row.unique_index);
935
+ if (text(row.origin) === "u") {
936
+ const internalName = physicalName.startsWith("sqlite_autoindex_");
937
+ const termNames = terms.map((term) => term.kind === "column" ? term.column : `expression_${term.position}`);
938
+ const declaredName = declaredConstraintName(sqlText, "UNIQUE", termNames);
939
+ const constraintName = internalName ? declaredName ?? `unique_${table.physicalName}_${termNames.join("_") || "constraint"}` : physicalName;
940
+ return {
941
+ kind: "unique",
942
+ id: stableId(constraintName),
943
+ identitySource: internalName && !declaredName ? "deterministic-fallback" : "physical-name",
944
+ physicalName: constraintName,
945
+ columns: terms.flatMap((term) => term.kind === "column" ? [term.column] : []),
946
+ nulls: "distinct",
947
+ dialect: extension({
948
+ origin: text(row.origin) ?? "unknown",
949
+ collations,
950
+ ...internalName ? { internalName } : {}
951
+ }),
952
+ reference: reference("constraint", constraintName, namespace, "pragma_index_list", "name", physicalName)
953
+ };
954
+ }
955
+ return {
956
+ kind: "index",
957
+ id: stableId(physicalName),
958
+ identitySource: "physical-name",
959
+ physicalName,
960
+ unique,
961
+ terms,
962
+ dialect: extension({
963
+ origin: text(row.origin) ?? "unknown",
964
+ partial: boolean(row.partial),
965
+ collations
966
+ }),
967
+ predicate: boolean(row.partial) ? partialPredicate(sqlText, physicalName, namespace, table) : void 0,
968
+ reference: reference("index", physicalName, namespace, "sqlite_schema", "name")
969
+ };
970
+ }
971
+ function declaredConstraintName(sqlText, keyword, columns) {
972
+ if (!sqlText) return void 0;
973
+ const pattern = new RegExp(`CONSTRAINT\\s+("(?:[^"]|"")*"|\`[^\`]*\`|\\[[^\\]]*\\]|[A-Za-z_][A-Za-z0-9_\$]*)\\s+${keyword}\\s*\\(([^)]*)\\)`, "gi");
974
+ for (const match of sqlText.matchAll(pattern)) {
975
+ const found = (match[2] ?? "").split(",").map((value) => unquoteIdentifier(value.trim())).filter((value) => value !== void 0);
976
+ if (found.length === columns.length && found.every((value, index) => value === columns[index])) return unquoteIdentifier(match[1]);
977
+ }
978
+ }
979
+ function foreignKeys(table, rows, tables, namespace, diagnostics) {
980
+ const grouped = /* @__PURE__ */ new Map();
981
+ for (const row of rows) {
982
+ const id = text(row.id) ?? "0";
983
+ const group = grouped.get(id) ?? [];
984
+ group.push(row);
985
+ grouped.set(id, group);
986
+ }
987
+ return [...grouped.entries()].map(([key, group]) => {
988
+ const ordered = [...group].sort((left, right) => (number(left.seq) ?? 0) - (number(right.seq) ?? 0));
989
+ const first = ordered[0];
990
+ const physicalName = `foreign_key_${table.physicalName}_${key}`;
991
+ const targetTableName = text(first.target_table);
992
+ const targetTable = targetTableName === void 0 ? void 0 : tables.get(targetTableName);
993
+ const targetColumns = ordered.map((row) => text(row.target_column));
994
+ const sourceColumns = ordered.map((row) => text(row.source_column));
995
+ const explicitTargetColumns = targetColumns.every((column) => column !== void 0);
996
+ const implicitTargetColumns = targetColumns.every((column) => column === void 0);
997
+ let normalizedTargetColumns;
998
+ let problem;
999
+ if (targetTableName === void 0) problem = "has no referenced table";
1000
+ else if (targetTable === void 0) problem = `references table ${targetTableName}, which is not visible`;
1001
+ else if (implicitTargetColumns) {
1002
+ const primaryKey = targetTable.constraints.find((constraint) => constraint.kind === "primary-key");
1003
+ if (primaryKey === void 0) problem = `references ${targetTableName} without target columns, but that table has no declared primary key`;
1004
+ else if (primaryKey.columns.length !== ordered.length) problem = `references ${targetTableName} without target columns with an incompatible primary-key width`;
1005
+ else normalizedTargetColumns = primaryKey.columns;
1006
+ } else if (explicitTargetColumns) normalizedTargetColumns = targetColumns;
1007
+ else problem = "has a mixture of explicit and implicit target columns";
1008
+ if (sourceColumns.some((column) => column === void 0)) problem = problem ?? "has an unresolved source column";
1009
+ const physicalReference = reference("opaque-object", physicalName, namespace, "pragma_foreign_key_list", "id", key);
1010
+ if (problem !== void 0 || targetTableName === void 0 || normalizedTargetColumns === void 0) {
1011
+ diagnostics.push(createIntrospectionDiagnostic({
1012
+ severity: "error",
1013
+ code: "unresolved-reference",
1014
+ message: `SQLite foreign key ${physicalName} ${problem ?? "has no recoverable target columns"}`,
1015
+ path: [
1016
+ table.id,
1017
+ "constraints",
1018
+ physicalName,
1019
+ "target"
1020
+ ],
1021
+ physicalReference,
1022
+ remediation: "Declare explicit target columns or expose the referenced table primary key."
1023
+ }));
1024
+ return opaqueForeignKey(table, physicalName, key, targetTableName, sourceColumns, targetColumns, physicalReference);
1025
+ }
1026
+ return {
1027
+ kind: "foreign-key",
1028
+ id: stableId(physicalName),
1029
+ identitySource: "deterministic-fallback",
1030
+ physicalName,
1031
+ columns: sourceColumns,
1032
+ target: {
1033
+ table: targetTableName,
1034
+ columns: normalizedTargetColumns
1035
+ },
1036
+ onUpdate: action(first.on_update),
1037
+ onDelete: action(first.on_delete),
1038
+ match: match(first.match),
1039
+ dialect: extension({ foreignKeyId: key }),
1040
+ reference: reference("constraint", physicalName, namespace, "pragma_foreign_key_list", "id", key)
1041
+ };
1042
+ });
1043
+ }
1044
+ function opaqueForeignKey(table, physicalName, key, targetTable, sourceColumns, targetColumns, physicalReference) {
1045
+ return {
1046
+ kind: "opaque-object",
1047
+ id: stableId(`foreign-key:${table.physicalName}:${key}`),
1048
+ identitySource: "deterministic-fallback",
1049
+ objectKind: "foreign-key",
1050
+ physicalName,
1051
+ data: {
1052
+ table: table.physicalName,
1053
+ foreignKeyId: key,
1054
+ ...targetTable === void 0 ? {} : { targetTable },
1055
+ sourceColumns: sourceColumns.map((column) => column ?? null),
1056
+ targetColumns: targetColumns.map((column) => column ?? null)
1057
+ },
1058
+ reference: physicalReference,
1059
+ provenance: {
1060
+ kind: "catalog",
1061
+ dialect: "sqlite",
1062
+ reference: physicalReference
1063
+ }
1064
+ };
1065
+ }
1066
+ function partialPredicate(sqlText, indexName, namespace, table) {
1067
+ const predicate = (sqlText?.match(/\bWHERE\s+([\s\S]+)$/i))?.[1]?.trim().replace(/;\s*$/, "");
1068
+ return predicate ? sql(predicate, namespace, table, indexName) : void 0;
1069
+ }
1070
+ function generatedExpressionFor(sqlText, columnName) {
1071
+ if (!sqlText) return;
1072
+ const open = sqlText.indexOf("(");
1073
+ if (open < 0) return;
1074
+ const close = matchingParen(sqlText, open);
1075
+ if (close < 0) return;
1076
+ const definitions = splitSqlList(sqlText.slice(open + 1, close));
1077
+ for (const definition of definitions) {
1078
+ const identifier = definition.trim().match(/^("(?:[^"]|"")*"|`[^`]*`|\[[^\]]*\]|[A-Za-z_][A-Za-z0-9_$]*)/)?.[1];
1079
+ if (unquoteIdentifier(identifier) !== columnName) continue;
1080
+ const generated = definition.match(/\bAS\s*\(/i);
1081
+ if (!generated || generated.index === void 0) return;
1082
+ const generatedOpen = definition.indexOf("(", generated.index);
1083
+ if (generatedOpen < 0) return;
1084
+ const generatedClose = matchingParen(definition, generatedOpen);
1085
+ return generatedClose < 0 ? void 0 : definition.slice(generatedOpen + 1, generatedClose).trim();
1086
+ }
1087
+ }
1088
+ function sql(textValue, dialectNamespace, owner, name) {
1089
+ const ownerReference = owner.reference ?? reference(owner.kind, owner.physicalName, dialectNamespace, "sqlite_schema", "name");
1090
+ return {
1091
+ kind: "sql",
1092
+ dialect: "sqlite",
1093
+ text: textValue,
1094
+ provenance: {
1095
+ kind: "create-sql",
1096
+ dialect: "sqlite",
1097
+ reference: reference(ownerReference.kind, name, dialectNamespace, ownerReference.catalog?.relation ?? "sqlite_schema", ownerReference.catalog?.key ?? "name", ownerReference.catalog?.value ?? ownerReference.name)
1098
+ }
1099
+ };
1100
+ }
1101
+ function reference(kind, name, namespace, relation, key, value = name) {
1102
+ return {
1103
+ kind,
1104
+ name,
1105
+ namespace,
1106
+ catalog: {
1107
+ relation,
1108
+ key,
1109
+ value
1110
+ }
1111
+ };
1112
+ }
1113
+ function emptyCatalog(namespace, diagnostics) {
1114
+ return {
1115
+ dialect: "sqlite",
1116
+ server: {
1117
+ product: "sqlite",
1118
+ rawVersion: "unknown",
1119
+ capabilities: {
1120
+ generatedColumns: false,
1121
+ identityMetadata: false,
1122
+ checkConstraints: false,
1123
+ checkConstraintEnforcement: "unknown",
1124
+ expressionDecompilation: false,
1125
+ indexExpressions: false,
1126
+ indexPredicates: false,
1127
+ indexIncludedColumns: false,
1128
+ namespaces: false,
1129
+ visibility: "unknown"
1130
+ }
1131
+ },
1132
+ namespace: {
1133
+ kind: "sqlite-database",
1134
+ name: namespace
1135
+ },
1136
+ tables: [],
1137
+ deferredObjects: [],
1138
+ diagnostics
1139
+ };
1140
+ }
1141
+ function stableId(value) {
1142
+ if (value && !/[.\\\u0000-\u001f\u007f]/.test(value)) return value;
1143
+ let hash = 2166136261;
1144
+ for (const character of value) {
1145
+ hash ^= character.charCodeAt(0);
1146
+ hash = Math.imul(hash, 16777619);
1147
+ }
1148
+ return `introspected_${(hash >>> 0).toString(16)}`;
1149
+ }
1150
+ function text(value) {
1151
+ return value === null || value === void 0 ? void 0 : String(value);
1152
+ }
1153
+ function number(value) {
1154
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1155
+ if (typeof value === "string" && value.trim() !== "") {
1156
+ const result = Number(value);
1157
+ return Number.isFinite(result) ? result : void 0;
1158
+ }
1159
+ }
1160
+ function boolean(value) {
1161
+ return value === true || value === 1 || value === "1" || value === "t" || value === "true";
1162
+ }
1163
+ function action(value) {
1164
+ const normalized = text(value)?.toLowerCase();
1165
+ return normalized === "cascade" ? "cascade" : normalized === "restrict" ? "restrict" : normalized === "set null" ? "set-null" : normalized === "set default" ? "set-default" : "no-action";
1166
+ }
1167
+ function match(value) {
1168
+ const normalized = text(value)?.toLowerCase();
1169
+ return normalized === "full" ? "full" : normalized === "partial" ? "partial" : "simple";
1170
+ }
1171
+ //#endregion
1172
+ export { readCatalog, sqliteDatabaseListQuery, sqliteForeignKeyQuery, sqliteIndexInfoQuery, sqliteIndexListQuery, sqliteSchemaQuery, sqliteServerQuery, sqliteTableInfoQuery, sqliteTableListQuery, sqliteTempSchemaQuery };