@happyvertical/sql 0.80.6 → 0.81.1
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 +49 -0
- package/dist/chunks/{schema-manager-a8RlxIE2.js → alter-utils-B900pfK_.js} +246 -2
- package/dist/chunks/alter-utils-B900pfK_.js.map +1 -0
- package/dist/chunks/{duckdb-D_Wu36JL.js → duckdb-CWy7Nziq.js} +45 -21
- package/dist/chunks/duckdb-CWy7Nziq.js.map +1 -0
- package/dist/chunks/{sqlite-B4ussRTS.js → sqlite-kBmA-x_C.js} +68 -29
- package/dist/chunks/sqlite-kBmA-x_C.js.map +1 -0
- package/dist/chunks/{sqlite-native-BgiF4301.js → sqlite-native-Dnlo3Tux.js} +73 -21
- package/dist/chunks/sqlite-native-Dnlo3Tux.js.map +1 -0
- package/dist/chunks/types-uVFHgaSU.js +133 -0
- package/dist/chunks/types-uVFHgaSU.js.map +1 -0
- package/dist/duckdb.d.ts.map +1 -1
- package/dist/index.js +715 -733
- package/dist/index.js.map +1 -1
- package/dist/json.d.ts.map +1 -1
- package/dist/postgres.d.ts.map +1 -1
- package/dist/shared/alter-utils.d.ts +48 -0
- package/dist/shared/alter-utils.d.ts.map +1 -1
- package/dist/shared/transaction-lock.d.ts +54 -0
- package/dist/shared/transaction-lock.d.ts.map +1 -0
- package/dist/shared/types.d.ts +69 -7
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/utils.d.ts +31 -0
- package/dist/shared/utils.d.ts.map +1 -1
- package/dist/sqlite-native.d.ts.map +1 -1
- package/dist/sqlite.d.ts +20 -0
- package/dist/sqlite.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/chunks/alter-utils-K7tiuj5L.js +0 -135
- package/dist/chunks/alter-utils-K7tiuj5L.js.map +0 -1
- package/dist/chunks/duckdb-D_Wu36JL.js.map +0 -1
- package/dist/chunks/schema-manager-a8RlxIE2.js.map +0 -1
- package/dist/chunks/sqlite-B4ussRTS.js.map +0 -1
- package/dist/chunks/sqlite-native-BgiF4301.js.map +0 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { resolveSchemas } from "../index.js";
|
|
1
|
+
import { a as validateColumnName, c as validateTableName, d as formatDbError, i as generateCreateIndexStatement, l as DatabaseSchemaManager, o as validateColumnNames, p as resolveInsertColumns, r as generateAddColumnStatement, s as validateIndexName, u as buildWhere } from "./alter-utils-B900pfK_.js";
|
|
2
|
+
import { n as resolveSchemas, r as createTransactionLock, t as NestedTransactionError } from "./types-uVFHgaSU.js";
|
|
4
3
|
import { DatabaseError } from "@happyvertical/utils";
|
|
5
4
|
import { resolve } from "node:path";
|
|
6
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -467,6 +466,7 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
467
466
|
});
|
|
468
467
|
const { DatabaseSync } = await loadNodeSqlite();
|
|
469
468
|
const database = new DatabaseSync(location.location, { allowExtension: wantsVector });
|
|
469
|
+
const connectionLock = createTransactionLock("sqlite (native)", options.transactionQueueTimeout);
|
|
470
470
|
try {
|
|
471
471
|
if (wantsVector) await loadSqliteVectorExtension(database);
|
|
472
472
|
const honkerDb = wantsNotifications && location.filePath ? await openHonkerDatabase(location.filePath, notificationsOptions) : null;
|
|
@@ -503,8 +503,14 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
503
503
|
};
|
|
504
504
|
};
|
|
505
505
|
const insert = async (table, data) => {
|
|
506
|
+
validateTableName(table);
|
|
506
507
|
const records = (Array.isArray(data) ? data : [data]).map(serializeRecord);
|
|
507
|
-
|
|
508
|
+
if (records.length === 0) return {
|
|
509
|
+
operation: "insert",
|
|
510
|
+
affected: 0
|
|
511
|
+
};
|
|
512
|
+
const keys = resolveInsertColumns(table, records);
|
|
513
|
+
validateColumnNames(keys);
|
|
508
514
|
const placeholders = records.map(() => `(${keys.map(() => "?").join(", ")})`).join(", ");
|
|
509
515
|
const values = records.flatMap((record) => keys.map((key) => record[key]));
|
|
510
516
|
const sql = `INSERT INTO ${table} (${keys.join(", ")}) VALUES ${placeholders}`;
|
|
@@ -523,6 +529,7 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
523
529
|
}
|
|
524
530
|
};
|
|
525
531
|
const get = async (table, where) => {
|
|
532
|
+
validateTableName(table);
|
|
526
533
|
const { sql: whereClause, values } = buildQuestionWhere(where);
|
|
527
534
|
if (!whereClause) throw new DatabaseError("GET requires at least one WHERE condition to prevent returning an arbitrary record", { table });
|
|
528
535
|
const sql = `SELECT * FROM ${table} ${whereClause}`;
|
|
@@ -538,6 +545,7 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
538
545
|
}
|
|
539
546
|
};
|
|
540
547
|
const list = async (table, where) => {
|
|
548
|
+
validateTableName(table);
|
|
541
549
|
const { sql: whereClause, values } = buildQuestionWhere(where);
|
|
542
550
|
const sql = `SELECT * FROM ${table} ${whereClause}`;
|
|
543
551
|
try {
|
|
@@ -552,8 +560,10 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
552
560
|
}
|
|
553
561
|
};
|
|
554
562
|
const update = async (table, where, data) => {
|
|
563
|
+
validateTableName(table);
|
|
555
564
|
const serializedData = serializeRecord(data);
|
|
556
565
|
const keys = Object.keys(serializedData);
|
|
566
|
+
validateColumnNames(keys);
|
|
557
567
|
const values = Object.values(serializedData);
|
|
558
568
|
const setClause = keys.map((key) => `${key} = ?`).join(", ");
|
|
559
569
|
const { sql: whereClause, values: whereValues } = buildQuestionWhere(where);
|
|
@@ -613,12 +623,24 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
613
623
|
affected: executeNativeQuery(database, `INSERT INTO ${table} (${keys.join(", ")}) VALUES (${placeholders})`, values).rowCount
|
|
614
624
|
};
|
|
615
625
|
};
|
|
626
|
+
/**
|
|
627
|
+
* Rejects hostile table/column identifiers before an upsert builds SQL.
|
|
628
|
+
*
|
|
629
|
+
* Called from the public entry points rather than from `executeUpsert`, so
|
|
630
|
+
* the failure reaches the caller naming the bad identifier instead of being
|
|
631
|
+
* flattened into the wrapper's generic "Failed to upsert record into table".
|
|
632
|
+
*/
|
|
633
|
+
const validateUpsertIdentifiers = (table, conflictColumns, data) => {
|
|
634
|
+
validateTableName(table);
|
|
635
|
+
validateColumnNames([...conflictColumns, ...Object.keys(data)]);
|
|
636
|
+
};
|
|
616
637
|
const executeUpsert = async (table, conflictColumns, data, upsertOptions, acquireTransaction) => {
|
|
617
638
|
const serializedData = serializeRecord(data);
|
|
618
639
|
validateUpsertConflictColumns(table, conflictColumns, serializedData);
|
|
619
640
|
if (upsertOptions?.nullsDistinct || !hasNullConflictValue(conflictColumns, serializedData)) return executeStandardUpsert(table, conflictColumns, serializedData);
|
|
620
641
|
if (!acquireTransaction) return executeNullAwareUpsertAttempt(table, conflictColumns, serializedData);
|
|
621
642
|
return withNativeNullAwareUpsertLock(`${location.location}:${table}:${conflictColumns.map((column) => `${column}:${JSON.stringify(serializedData[column])}`).join("|")}`, async () => {
|
|
643
|
+
const releaseConnection = await connectionLock.acquire();
|
|
622
644
|
try {
|
|
623
645
|
database.exec("BEGIN IMMEDIATE");
|
|
624
646
|
const result = executeNullAwareUpsertAttempt(table, conflictColumns, serializedData);
|
|
@@ -629,10 +651,13 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
629
651
|
database.exec("ROLLBACK");
|
|
630
652
|
} catch (_rollbackError) {}
|
|
631
653
|
throw error;
|
|
654
|
+
} finally {
|
|
655
|
+
releaseConnection();
|
|
632
656
|
}
|
|
633
657
|
});
|
|
634
658
|
};
|
|
635
659
|
const upsert = async (table, conflictColumns, data, upsertOptions) => {
|
|
660
|
+
validateUpsertIdentifiers(table, conflictColumns, data);
|
|
636
661
|
try {
|
|
637
662
|
return await executeUpsert(table, conflictColumns, data, upsertOptions, true);
|
|
638
663
|
} catch (error) {
|
|
@@ -645,7 +670,10 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
645
670
|
});
|
|
646
671
|
}
|
|
647
672
|
};
|
|
648
|
-
const upsertInCurrentTransaction = (table, conflictColumns, data, upsertOptions) =>
|
|
673
|
+
const upsertInCurrentTransaction = (table, conflictColumns, data, upsertOptions) => {
|
|
674
|
+
validateUpsertIdentifiers(table, conflictColumns, data);
|
|
675
|
+
return executeUpsert(table, conflictColumns, data, upsertOptions, false);
|
|
676
|
+
};
|
|
649
677
|
const getOrInsert = async (table, where, data) => {
|
|
650
678
|
const existing = await get(table, where);
|
|
651
679
|
if (existing) return existing;
|
|
@@ -769,7 +797,7 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
769
797
|
const name = `hv_sp_${savepointSequence}`;
|
|
770
798
|
database.exec(`SAVEPOINT ${name}`);
|
|
771
799
|
try {
|
|
772
|
-
const result = await callback(buildDatabaseInterface(upsertInCurrentTransaction, nestedTransaction));
|
|
800
|
+
const result = await callback(buildDatabaseInterface(upsertInCurrentTransaction, nestedTransaction, refuseNestedBeginTransaction));
|
|
773
801
|
database.exec(`RELEASE SAVEPOINT ${name}`);
|
|
774
802
|
return result;
|
|
775
803
|
} catch (error) {
|
|
@@ -780,10 +808,10 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
780
808
|
throw error;
|
|
781
809
|
}
|
|
782
810
|
};
|
|
783
|
-
const transaction = async (callback) => {
|
|
811
|
+
const transaction = async (callback) => connectionLock.run(async () => {
|
|
784
812
|
try {
|
|
785
813
|
database.exec("BEGIN TRANSACTION");
|
|
786
|
-
const result = await callback(buildDatabaseInterface(upsertInCurrentTransaction, nestedTransaction));
|
|
814
|
+
const result = await callback(buildDatabaseInterface(upsertInCurrentTransaction, nestedTransaction, refuseNestedBeginTransaction));
|
|
787
815
|
database.exec("COMMIT");
|
|
788
816
|
return result;
|
|
789
817
|
} catch (error) {
|
|
@@ -792,22 +820,46 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
792
820
|
} catch {}
|
|
793
821
|
throw error;
|
|
794
822
|
}
|
|
823
|
+
});
|
|
824
|
+
/**
|
|
825
|
+
* The `beginTransaction` exposed on a transaction-scoped interface.
|
|
826
|
+
*
|
|
827
|
+
* Unlike `transaction`, a manual handle has no savepoint equivalent: it
|
|
828
|
+
* would have to own the connection until the caller ends it, which the
|
|
829
|
+
* enclosing transaction already does. Issuing a second BEGIN threw before
|
|
830
|
+
* transactions were serialized; now it would instead wait on a lock its own
|
|
831
|
+
* caller holds, so refuse explicitly rather than deadlock.
|
|
832
|
+
*/
|
|
833
|
+
const refuseNestedBeginTransaction = async () => {
|
|
834
|
+
throw new NestedTransactionError("beginTransaction() cannot be called inside a transaction. Use the transaction you already have, or nest with transaction() to re-enter it under a savepoint.", { adapter: "sqlite-native" });
|
|
795
835
|
};
|
|
796
836
|
const beginTransaction = async () => {
|
|
797
|
-
|
|
837
|
+
const releaseConnection = await connectionLock.acquire();
|
|
838
|
+
try {
|
|
839
|
+
database.exec("BEGIN TRANSACTION");
|
|
840
|
+
} catch (error) {
|
|
841
|
+
releaseConnection();
|
|
842
|
+
throw error;
|
|
843
|
+
}
|
|
798
844
|
let active = true;
|
|
799
|
-
const
|
|
800
|
-
if (!active) throw new DatabaseError("Transaction already ended", {});
|
|
801
|
-
database.exec("COMMIT");
|
|
802
|
-
active = false;
|
|
803
|
-
};
|
|
804
|
-
const rollback = async () => {
|
|
845
|
+
const end = (command) => {
|
|
805
846
|
if (!active) throw new DatabaseError("Transaction already ended", {});
|
|
806
|
-
|
|
807
|
-
|
|
847
|
+
try {
|
|
848
|
+
database.exec(command);
|
|
849
|
+
} catch (error) {
|
|
850
|
+
try {
|
|
851
|
+
database.exec("ROLLBACK");
|
|
852
|
+
} catch {}
|
|
853
|
+
throw error;
|
|
854
|
+
} finally {
|
|
855
|
+
active = false;
|
|
856
|
+
releaseConnection();
|
|
857
|
+
}
|
|
808
858
|
};
|
|
859
|
+
const commit = async () => end("COMMIT");
|
|
860
|
+
const rollback = async () => end("ROLLBACK");
|
|
809
861
|
return {
|
|
810
|
-
...buildDatabaseInterface(upsertInCurrentTransaction, nestedTransaction),
|
|
862
|
+
...buildDatabaseInterface(upsertInCurrentTransaction, nestedTransaction, refuseNestedBeginTransaction),
|
|
811
863
|
commit,
|
|
812
864
|
rollback,
|
|
813
865
|
isActive: () => active
|
|
@@ -879,7 +931,7 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
879
931
|
database.close();
|
|
880
932
|
if (options.dbid) nativeMemoryConnectionCache.delete(options.dbid);
|
|
881
933
|
};
|
|
882
|
-
function buildDatabaseInterface(upsertImpl, transactionImpl = transaction) {
|
|
934
|
+
function buildDatabaseInterface(upsertImpl, transactionImpl = transaction, beginTransactionImpl = beginTransaction) {
|
|
883
935
|
return {
|
|
884
936
|
url,
|
|
885
937
|
client: database,
|
|
@@ -905,7 +957,7 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
905
957
|
syncSchema,
|
|
906
958
|
initializeSchemas,
|
|
907
959
|
transaction: transactionImpl,
|
|
908
|
-
beginTransaction,
|
|
960
|
+
beginTransaction: beginTransactionImpl,
|
|
909
961
|
getTableSchema,
|
|
910
962
|
alterTable,
|
|
911
963
|
close,
|
|
@@ -927,4 +979,4 @@ async function createNativeSqliteDatabase(options, url) {
|
|
|
927
979
|
//#endregion
|
|
928
980
|
export { getNativeSqliteDatabase };
|
|
929
981
|
|
|
930
|
-
//# sourceMappingURL=sqlite-native-
|
|
982
|
+
//# sourceMappingURL=sqlite-native-Dnlo3Tux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-native-Dnlo3Tux.js","names":[],"sources":["../../src/sqlite-native.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { DatabaseError } from '@happyvertical/utils';\nimport { DatabaseSchemaManager } from './schema-manager.js';\nimport {\n generateAddColumnStatement,\n generateCreateIndexStatement,\n validateColumnName,\n validateColumnNames,\n validateIndexName,\n validateTableName,\n} from './shared/alter-utils.js';\nimport { createTransactionLock } from './shared/transaction-lock.js';\nimport type {\n ColumnDefinition,\n ColumnDefinitionWithName,\n DatabaseInterface,\n IndexDefinition,\n NotificationCapabilities,\n NotificationListenOptions,\n NotificationPruneOptions,\n NotificationWaitOptions,\n QueryResult,\n SchemaInitializationOptions,\n SchemaProvider,\n SqliteNotificationCapabilityOptions,\n SqliteVectorCapabilityOptions,\n TableInterface,\n TableSchemaInfo,\n TransactionHandle,\n UpsertOptions,\n VectorCapabilities,\n VectorIndexOptions,\n VectorSearchOptions,\n VectorSearchResult,\n WhereClause,\n} from './shared/types.js';\nimport { NestedTransactionError, resolveSchemas } from './shared/types.js';\nimport {\n buildWhere,\n formatDbError,\n resolveInsertColumns,\n} from './shared/utils.js';\nimport type { SqliteOptions } from './sqlite.js';\n\ntype NativeDatabase = any;\ntype NativeStatement = any;\n\nconst nativeMemoryConnectionCache = new Map<string, DatabaseInterface>();\nconst nativePendingConnections = new Map<string, Promise<DatabaseInterface>>();\nconst nativeNullAwareUpsertLocks = new Map<string, Promise<void>>();\nlet nodeSqliteModulePromise: Promise<any> | null = null;\n// sqlite-vector's ESM wrapper uses dynamic require for platform packages.\nconst requireOptionalPackage = createRequire(import.meta.url);\n\ninterface NativeSqliteLocation {\n location: string;\n filePath: string | null;\n isMemory: boolean;\n}\n\ninterface NativeQueryResult {\n rows: Record<string, any>[];\n rowCount: number;\n command?: string;\n fields?: Array<{ name: string }>;\n}\n\nfunction getCapabilityOptions<T extends Record<string, any>>(\n value: boolean | T | undefined,\n): T {\n return typeof value === 'object' && value ? value : ({} as T);\n}\n\nfunction resolveNativeLocation(url = ':memory:'): NativeSqliteLocation {\n if (\n url.startsWith('libsql://') ||\n url.startsWith('http://') ||\n url.startsWith('https://')\n ) {\n throw new DatabaseError(\n 'SQLite native capabilities are only supported for local SQLite databases',\n {\n url,\n hint: \"Use a local file path, 'file:...' URL, or ':memory:' when enabling SQLite capabilities.\",\n },\n );\n }\n\n if (url === ':memory:' || url.startsWith('file::memory:')) {\n return { location: url, filePath: null, isMemory: true };\n }\n\n if (url.startsWith('file://')) {\n const filePath = fileURLToPath(new URL(url));\n return { location: filePath, filePath, isMemory: false };\n }\n\n if (url.startsWith('file:')) {\n const path = url.slice('file:'.length);\n if (!path || path.includes('?')) {\n throw new DatabaseError(\n 'SQLite native capabilities do not support file: URLs with query parameters',\n { url },\n );\n }\n const filePath = resolve(path);\n return { location: filePath, filePath, isMemory: false };\n }\n\n const filePath = resolve(url);\n return { location: filePath, filePath, isMemory: false };\n}\n\nasync function loadNodeSqlite(): Promise<any> {\n nodeSqliteModulePromise ??= importNodeSqlite().catch((error) => {\n nodeSqliteModulePromise = null;\n throw error;\n });\n return nodeSqliteModulePromise;\n}\n\nasync function importNodeSqlite(): Promise<any> {\n const originalEmitWarning = process.emitWarning;\n\n try {\n process.emitWarning = function suppressNodeSqliteExperimentalWarning(\n warning: string | Error,\n ...args: any[]\n ) {\n if (\n args[0] === 'ExperimentalWarning' &&\n String(warning).includes('SQLite is an experimental feature')\n ) {\n return;\n }\n\n return originalEmitWarning.call(process, warning as any, ...args);\n } as typeof process.emitWarning;\n\n const moduleName = 'node:sqlite';\n return (await import(/* @vite-ignore */ moduleName)) as any;\n } catch (error) {\n throw new DatabaseError('Node native SQLite is not available', {\n originalError: formatDbError(error),\n hint: 'SQLite native capabilities require a Node.js runtime with node:sqlite support.',\n });\n } finally {\n process.emitWarning = originalEmitWarning;\n }\n}\n\nasync function loadSqliteVectorExtension(\n database: NativeDatabase,\n): Promise<void> {\n try {\n const moduleName = '@sqliteai/sqlite-vector';\n const module = requireOptionalPackage(moduleName) as any;\n const getExtensionPath =\n module.getExtensionPath ?? module.default?.getExtensionPath;\n\n if (typeof getExtensionPath !== 'function') {\n throw new Error('getExtensionPath export was not found');\n }\n\n database.enableLoadExtension(true);\n database.loadExtension(getExtensionPath());\n } catch (error) {\n throw new DatabaseError(\n 'Failed to load optional SQLite vector capability: install @sqliteai/sqlite-vector',\n {\n packageName: '@sqliteai/sqlite-vector',\n originalError: formatDbError(error),\n hint: 'Install @sqliteai/sqlite-vector to use SQLite vector capabilities.',\n },\n );\n } finally {\n try {\n database.enableLoadExtension(false);\n } catch (_error) {\n // Leave the original extension-loading failure intact.\n }\n }\n}\n\nasync function openHonkerDatabase(\n filePath: string,\n options: SqliteNotificationCapabilityOptions,\n): Promise<any> {\n try {\n const moduleName = '@russellthehippo/honker-node';\n const module = requireOptionalPackage(moduleName) as any;\n const open = module.open ?? module.default?.open;\n\n if (typeof open !== 'function') {\n throw new Error('open export was not found');\n }\n\n return open(filePath, options.maxReaders, options.watcherBackend);\n } catch (error) {\n throw new DatabaseError(\n 'Failed to load optional SQLite notification capability: install @russellthehippo/honker-node',\n {\n packageName: '@russellthehippo/honker-node',\n originalError: formatDbError(error),\n hint: 'Install @russellthehippo/honker-node to use SQLite notification capabilities.',\n },\n );\n }\n}\n\nfunction toPlainRow(row: Record<string, any>): Record<string, any> {\n return Object.fromEntries(Object.entries(row));\n}\n\nfunction getErrorMessage(error: unknown): string | undefined {\n return error instanceof Error ? error.message : undefined;\n}\n\nfunction firstSqlToken(sql: string): string {\n return sql.trim().split(/\\s+/)[0]?.toUpperCase() ?? '';\n}\n\nfunction normalizeNativeQuery(\n sql: string,\n values: any[],\n): { sql: string; args: any[] } {\n const flatArgs = Array.isArray(values[0]) ? values[0] : values;\n const parameterIndexes = Array.from(sql.matchAll(/\\$(\\d+)/g)).map((match) =>\n Number(match[1]),\n );\n\n if (parameterIndexes.length === 0) {\n return { sql, args: flatArgs };\n }\n\n const args = parameterIndexes.map((index) => flatArgs[index - 1]);\n return { sql: sql.replace(/\\$(\\d+)/g, '?'), args };\n}\n\nfunction executeNativeQuery(\n database: NativeDatabase,\n sql: string,\n args: any[] = [],\n): NativeQueryResult {\n const statement: NativeStatement = database.prepare(sql);\n const columns = statement.columns();\n\n if (columns.length > 0) {\n const rows = statement.all(...args).map(toPlainRow);\n return {\n rows,\n rowCount: rows.length,\n command: firstSqlToken(sql),\n fields: columns.map((column: { name: string }) => ({\n name: column.name,\n })),\n };\n }\n\n const result = statement.run(...args);\n return {\n rows: [],\n rowCount: Number(result?.changes ?? 0),\n command: firstSqlToken(sql),\n fields: [],\n };\n}\n\nfunction createNativeTablesFromSchemas(\n database: NativeDatabase,\n schemas: Record<string, SchemaProvider>,\n): void {\n for (const [tableName, schema] of Object.entries(schemas)) {\n try {\n const result = executeNativeQuery(\n database,\n \"SELECT name FROM sqlite_master WHERE type='table' AND name=?\",\n [tableName],\n );\n\n if (result.rows.length > 0) {\n continue;\n }\n\n database.exec(schema.ddl);\n\n for (const indexSql of schema.indexes ?? []) {\n try {\n database.exec(indexSql);\n } catch (error) {\n console.warn(\n `[sqlite] Failed to create index for ${tableName}: ${formatDbError(error)}`,\n );\n }\n }\n\n for (const triggerSql of schema.triggers ?? []) {\n try {\n database.exec(triggerSql);\n } catch (error) {\n console.warn(\n `[sqlite] Failed to create trigger for ${tableName}: ${formatDbError(error)}`,\n );\n }\n }\n } catch (error) {\n throw new DatabaseError(\n `Failed to create table ${tableName} from schema`,\n {\n tableName,\n schema,\n originalError: formatDbError(error),\n },\n );\n }\n }\n}\n\nfunction quoteIdentifier(identifier: string): string {\n return `\"${identifier.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction buildQuestionWhere(where: WhereClause, startIndex = 1) {\n const built = buildWhere(where, startIndex, 'sqlite');\n return {\n sql: built.sql.replace(/\\$(\\d+)/g, '?'),\n values: built.values,\n };\n}\n\nfunction serializeValue(value: any): any {\n if (value === null || value === undefined) {\n return value;\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (typeof value === 'object') {\n return JSON.stringify(value);\n }\n return value;\n}\n\nfunction serializeRecord(record: Record<string, any>): Record<string, any> {\n const serialized: Record<string, any> = {};\n for (const [key, value] of Object.entries(record)) {\n if (value === undefined) {\n continue;\n }\n serialized[key] = serializeValue(value);\n }\n return serialized;\n}\n\nasync function withNativeNullAwareUpsertLock<T>(\n lockKey: string,\n callback: () => Promise<T>,\n): Promise<T> {\n const previous = nativeNullAwareUpsertLocks.get(lockKey) ?? Promise.resolve();\n let release: () => void = () => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const next = previous.catch(() => {}).then(() => current);\n\n nativeNullAwareUpsertLocks.set(lockKey, next);\n await previous.catch(() => {});\n\n try {\n return await callback();\n } finally {\n release();\n if (nativeNullAwareUpsertLocks.get(lockKey) === next) {\n nativeNullAwareUpsertLocks.delete(lockKey);\n }\n }\n}\n\nfunction sqliteVectorDistance(metric: 'cosine' | 'l2' | 'ip'): string {\n switch (metric) {\n case 'l2':\n return 'L2';\n case 'ip':\n return 'DOT';\n default:\n return 'COSINE';\n }\n}\n\nfunction sqliteVectorQuantization(\n options: SqliteVectorCapabilityOptions,\n): string {\n const parts: string[] = [];\n\n switch (options.quantization ?? 'turbo4') {\n case 'turbo2':\n parts.push('qtype=TURBO,qbits=2');\n break;\n case 'turbo3':\n parts.push('qtype=TURBO,qbits=3');\n break;\n case 'turbo4':\n parts.push('qtype=TURBO,qbits=4');\n break;\n case 'uint8':\n parts.push('qtype=UINT8');\n break;\n case 'int8':\n parts.push('qtype=INT8');\n break;\n case '1bit':\n parts.push('qtype=1BIT');\n break;\n }\n\n if (options.maxMemory) {\n parts.push(`max_memory=${options.maxMemory}`);\n }\n\n return parts.join(',');\n}\n\nfunction createNotificationCapabilities(\n honkerDb: any,\n closeables: Set<{ close?: () => void }>,\n): NotificationCapabilities {\n return {\n async notify(channel: string, payload: any): Promise<number> {\n return Number(honkerDb.notify(channel, payload));\n },\n\n listen(channel: string, options?: NotificationListenOptions) {\n const listener = honkerDb.listen(channel, {\n fallbackPollS:\n options?.fallbackPollMs === null\n ? null\n : options?.fallbackPollMs !== undefined\n ? options.fallbackPollMs / 1000\n : undefined,\n });\n closeables.add(listener);\n return listener;\n },\n\n async waitForUpdate(options?: NotificationWaitOptions): Promise<boolean> {\n const updateEvents = honkerDb.updateEvents();\n closeables.add(updateEvents);\n let timeout: NodeJS.Timeout | undefined;\n let removeAbortListener: (() => void) | undefined;\n\n try {\n if (options?.signal?.aborted) {\n return false;\n }\n\n const updatePromise = updateEvents.next().then(\n () => true,\n () => false,\n );\n const races: Array<Promise<boolean>> = [updatePromise];\n\n if (options?.timeoutMs !== undefined) {\n const timeoutMs = options.timeoutMs;\n races.push(\n new Promise((resolve) => {\n timeout = setTimeout(\n () => resolve(false),\n Math.max(0, timeoutMs),\n );\n }),\n );\n }\n\n if (options?.signal) {\n races.push(\n new Promise((resolve) => {\n const onAbort = () => resolve(false);\n options.signal?.addEventListener('abort', onAbort, {\n once: true,\n });\n removeAbortListener = () =>\n options.signal?.removeEventListener('abort', onAbort);\n }),\n );\n }\n\n return await Promise.race(races);\n } finally {\n if (timeout) {\n clearTimeout(timeout);\n }\n removeAbortListener?.();\n closeables.delete(updateEvents);\n updateEvents.close?.();\n }\n },\n\n async prune(options?: NotificationPruneOptions): Promise<number> {\n return Number(\n honkerDb.pruneNotifications(options?.olderThanS, options?.maxKeep),\n );\n },\n };\n}\n\nfunction createVectorCapabilities(\n database: NativeDatabase,\n options: SqliteVectorCapabilityOptions,\n): VectorCapabilities {\n const initialized = new Map<\n string,\n { dimensions: number; metric: 'cosine' | 'l2' | 'ip' }\n >();\n const indexed = new Map<\n string,\n { metric: 'cosine' | 'l2' | 'ip'; options: SqliteVectorCapabilityOptions }\n >();\n\n const keyFor = (table: string, column: string) => `${table}.${column}`;\n\n const columnExists = (table: string, column: string): boolean => {\n const rows = database\n .prepare(`PRAGMA table_info(${quoteIdentifier(table)})`)\n .all()\n .map(toPlainRow);\n return rows.some((row: Record<string, any>) => row.name === column);\n };\n\n const ensureInitialized = (\n table: string,\n column: string,\n dimensions: number,\n metric: 'cosine' | 'l2' | 'ip' = 'cosine',\n ): void => {\n if (!Number.isInteger(dimensions) || dimensions <= 0) {\n throw new DatabaseError('Vector dimensions must be a positive integer', {\n dimensions,\n });\n }\n\n validateTableName(table);\n validateColumnName(column);\n\n executeNativeQuery(database, 'SELECT vector_init(?, ?, ?)', [\n table,\n column,\n `dimension=${dimensions},type=FLOAT32,distance=${sqliteVectorDistance(metric)}`,\n ]);\n initialized.set(keyFor(table, column), { dimensions, metric });\n };\n\n const quantize = (\n table: string,\n column: string,\n vectorOptions: SqliteVectorCapabilityOptions,\n ): void => {\n const quantizationOptions = sqliteVectorQuantization(vectorOptions);\n if (quantizationOptions) {\n executeNativeQuery(database, 'SELECT vector_quantize(?, ?, ?)', [\n table,\n column,\n quantizationOptions,\n ]);\n } else {\n executeNativeQuery(database, 'SELECT vector_quantize(?, ?)', [\n table,\n column,\n ]);\n }\n\n if (vectorOptions.preload) {\n executeNativeQuery(database, 'SELECT vector_quantize_preload(?, ?)', [\n table,\n column,\n ]);\n }\n };\n\n return {\n async ensureColumn(\n table: string,\n column: string,\n dimensions: number,\n ): Promise<void> {\n validateTableName(table);\n validateColumnName(column);\n\n try {\n if (!columnExists(table, column)) {\n executeNativeQuery(\n database,\n `ALTER TABLE ${quoteIdentifier(table)} ADD COLUMN ${quoteIdentifier(column)} BLOB`,\n );\n }\n ensureInitialized(table, column, dimensions, 'cosine');\n } catch (error) {\n throw new DatabaseError('Failed to ensure SQLite vector column', {\n table,\n column,\n dimensions,\n originalError: formatDbError(error),\n });\n }\n },\n\n async ensureIndex(\n table: string,\n column: string,\n indexOptions?: VectorIndexOptions,\n ): Promise<void> {\n validateTableName(table);\n validateColumnName(column);\n\n const key = keyFor(table, column);\n const dimensions =\n indexOptions?.dimensions ?? initialized.get(key)?.dimensions;\n if (!dimensions) {\n throw new DatabaseError(\n 'SQLite vector index creation requires dimensions',\n {\n table,\n column,\n hint: 'Call ensureColumn(table, column, dimensions) first or pass dimensions to ensureIndex().',\n },\n );\n }\n\n const metric = indexOptions?.metric ?? 'cosine';\n\n try {\n ensureInitialized(table, column, dimensions, metric);\n const vectorOptions = { ...options };\n quantize(table, column, vectorOptions);\n indexed.set(key, { metric, options: vectorOptions });\n } catch (error) {\n throw new DatabaseError('Failed to ensure SQLite vector index', {\n table,\n column,\n originalError: formatDbError(error),\n });\n }\n },\n\n async upsertVector(\n table: string,\n where: Record<string, any>,\n column: string,\n embedding: number[],\n ): Promise<void> {\n validateTableName(table);\n validateColumnName(column);\n\n const key = keyFor(table, column);\n const dimensions = initialized.get(key)?.dimensions ?? embedding.length;\n const { sql: whereClause, values } = buildQuestionWhere(where);\n\n if (!whereClause) {\n throw new DatabaseError(\n 'SQLite vector update requires at least one WHERE condition',\n { table, column },\n );\n }\n\n try {\n ensureInitialized(\n table,\n column,\n dimensions,\n initialized.get(key)?.metric ?? 'cosine',\n );\n executeNativeQuery(\n database,\n `UPDATE ${quoteIdentifier(table)} SET ${quoteIdentifier(column)} = vector_as_f32(?) ${whereClause}`,\n [JSON.stringify(embedding), ...values],\n );\n\n const indexState = indexed.get(key);\n if (indexState) {\n quantize(table, column, indexState.options);\n }\n } catch (error) {\n throw new DatabaseError('Failed to upsert SQLite vector', {\n table,\n column,\n where,\n originalError: formatDbError(error),\n });\n }\n },\n\n async search(\n table: string,\n column: string,\n embedding: number[],\n searchOptions?: VectorSearchOptions,\n ): Promise<VectorSearchResult[]> {\n validateTableName(table);\n validateColumnName(column);\n\n const key = keyFor(table, column);\n const metric = searchOptions?.metric ?? 'cosine';\n const limit = searchOptions?.limit ?? 10;\n const limitArg = BigInt(limit);\n const dimensions = initialized.get(key)?.dimensions ?? embedding.length;\n const indexState = indexed.get(key);\n const useQuantizedScan = indexState?.metric === metric;\n const scanFunction = useQuantizedScan\n ? 'vector_quantize_scan'\n : 'vector_full_scan';\n const convertedWhere = searchOptions?.where\n ? searchOptions.where.replace(/\\$(\\d+)/g, '?')\n : '';\n const whereParams = searchOptions?.params ?? [];\n\n try {\n ensureInitialized(table, column, dimensions, metric);\n\n const rows = convertedWhere\n ? executeNativeQuery(\n database,\n `SELECT base.*, base.rowid AS __sqlite_rowid, vec.distance\n FROM ${scanFunction}(?, ?, vector_as_f32(?)) AS vec\n JOIN ${quoteIdentifier(table)} AS base ON base.rowid = vec.rowid\n WHERE (${convertedWhere})\n ORDER BY vec.distance\n LIMIT ?`,\n [\n table,\n column,\n JSON.stringify(embedding),\n ...whereParams,\n limitArg,\n ],\n ).rows\n : executeNativeQuery(\n database,\n `SELECT base.*, base.rowid AS __sqlite_rowid, vec.distance\n FROM ${scanFunction}(?, ?, vector_as_f32(?), ?) AS vec\n JOIN ${quoteIdentifier(table)} AS base ON base.rowid = vec.rowid\n ORDER BY vec.distance`,\n [table, column, JSON.stringify(embedding), limitArg],\n ).rows;\n\n return rows.map((row) => {\n const { __sqlite_rowid: rowid, ...result } = row;\n return {\n ...result,\n id: String(result.id ?? rowid),\n distance: Number(row.distance),\n };\n });\n } catch (error) {\n throw new DatabaseError('Failed to execute SQLite vector search', {\n table,\n column,\n metric,\n limit,\n originalError: formatDbError(error),\n });\n }\n },\n };\n}\n\nexport async function getNativeSqliteDatabase(\n options: SqliteOptions = {},\n): Promise<DatabaseInterface> {\n const url = options.url || ':memory:';\n\n if (options.dbid) {\n const cached = nativeMemoryConnectionCache.get(options.dbid);\n if (cached) {\n return cached;\n }\n\n const pending = nativePendingConnections.get(options.dbid);\n if (pending) {\n return pending;\n }\n }\n\n const connectionPromise = createNativeSqliteDatabase(options, url);\n\n if (options.dbid) {\n nativePendingConnections.set(options.dbid, connectionPromise);\n }\n\n try {\n const db = await connectionPromise;\n if (options.dbid) {\n nativeMemoryConnectionCache.set(options.dbid, db);\n }\n return db;\n } finally {\n if (options.dbid) {\n nativePendingConnections.delete(options.dbid);\n }\n }\n}\n\nasync function createNativeSqliteDatabase(\n options: SqliteOptions,\n url: string,\n): Promise<DatabaseInterface> {\n const location = resolveNativeLocation(url);\n const notificationsOptions = getCapabilityOptions(\n options.capabilities?.notifications,\n );\n const vectorOptions = getCapabilityOptions(options.capabilities?.vector);\n const wantsNotifications = Boolean(options.capabilities?.notifications);\n const wantsVector = Boolean(options.capabilities?.vector);\n\n if (wantsNotifications && location.isMemory) {\n throw new DatabaseError(\n 'SQLite notification capabilities require a file-backed database',\n {\n url,\n hint: 'Use a temporary file-backed .db for development and tests that need notifications.',\n },\n );\n }\n\n const { DatabaseSync } = await loadNodeSqlite();\n const database = new DatabaseSync(location.location, {\n allowExtension: wantsVector,\n });\n\n // One lock per connection. `getNativeSqliteDatabase` hands cached callers\n // this same closure, so \"created here\" and \"per connection\" are the same\n // scope.\n const connectionLock = createTransactionLock(\n 'sqlite (native)',\n options.transactionQueueTimeout,\n );\n\n try {\n if (wantsVector) {\n await loadSqliteVectorExtension(database);\n }\n\n const honkerDb =\n wantsNotifications && location.filePath\n ? await openHonkerDatabase(location.filePath, notificationsOptions)\n : null;\n const notificationCloseables = new Set<{ close?: () => void }>();\n let closed = false;\n\n const vectorCapabilities = wantsVector\n ? createVectorCapabilities(database, vectorOptions)\n : undefined;\n const notificationCapabilities = honkerDb\n ? createNotificationCapabilities(honkerDb, notificationCloseables)\n : undefined;\n\n const query = async (\n sql: string,\n ...values: any[]\n ): Promise<{ rows: Record<string, any>[]; rowCount: number }> => {\n const normalized = normalizeNativeQuery(sql, values);\n try {\n const result = executeNativeQuery(\n database,\n normalized.sql,\n normalized.args,\n );\n return { rows: result.rows, rowCount: result.rowCount };\n } catch (error) {\n throw new DatabaseError('Failed to execute raw query', {\n sql: normalized.sql,\n values: normalized.args,\n originalError: formatDbError(error),\n });\n }\n };\n\n const parseTemplate = (strings: TemplateStringsArray, ...vars: any[]) => {\n let sql = strings[0];\n const values = [];\n for (let i = 0; i < vars.length; i++) {\n values.push(vars[i]);\n sql += `?${strings[i + 1]}`;\n }\n return { sql, values };\n };\n\n const insert = async (\n table: string,\n data: Record<string, any> | Record<string, any>[],\n ): Promise<QueryResult> => {\n validateTableName(table);\n const records = (Array.isArray(data) ? data : [data]).map(\n serializeRecord,\n );\n if (records.length === 0) {\n return { operation: 'insert', affected: 0 };\n }\n const keys = resolveInsertColumns(table, records);\n validateColumnNames(keys);\n const placeholders = records\n .map(() => `(${keys.map(() => '?').join(', ')})`)\n .join(', ');\n const values = records.flatMap((record) =>\n keys.map((key) => record[key]),\n );\n const sql = `INSERT INTO ${table} (${keys.join(', ')}) VALUES ${placeholders}`;\n\n try {\n const result = executeNativeQuery(database, sql, values);\n return { operation: 'insert', affected: result.rowCount };\n } catch (error) {\n throw new DatabaseError('Failed to insert records into table', {\n table,\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const get = async (\n table: string,\n where: WhereClause,\n ): Promise<Record<string, any> | null> => {\n validateTableName(table);\n const { sql: whereClause, values } = buildQuestionWhere(where);\n if (!whereClause) {\n throw new DatabaseError(\n 'GET requires at least one WHERE condition to prevent returning an arbitrary record',\n { table },\n );\n }\n\n const sql = `SELECT * FROM ${table} ${whereClause}`;\n try {\n const result = executeNativeQuery(database, sql, values);\n return result.rows[0] || null;\n } catch (error) {\n throw new DatabaseError('Failed to retrieve record from table', {\n table,\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const list = async (\n table: string,\n where: WhereClause,\n ): Promise<Record<string, any>[]> => {\n validateTableName(table);\n const { sql: whereClause, values } = buildQuestionWhere(where);\n const sql = `SELECT * FROM ${table} ${whereClause}`;\n try {\n return executeNativeQuery(database, sql, values).rows;\n } catch (error) {\n throw new DatabaseError('Failed to list records from table', {\n table,\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const update = async (\n table: string,\n where: WhereClause,\n data: Record<string, any>,\n ): Promise<QueryResult> => {\n validateTableName(table);\n const serializedData = serializeRecord(data);\n const keys = Object.keys(serializedData);\n validateColumnNames(keys);\n const values = Object.values(serializedData);\n const setClause = keys.map((key) => `${key} = ?`).join(', ');\n const { sql: whereClause, values: whereValues } =\n buildQuestionWhere(where);\n\n if (!whereClause) {\n throw new DatabaseError(\n 'UPDATE requires at least one WHERE condition to prevent accidental update of all records',\n { table },\n );\n }\n\n const sql = `UPDATE ${table} SET ${setClause} ${whereClause}`;\n try {\n const result = executeNativeQuery(database, sql, [\n ...values,\n ...whereValues,\n ]);\n return { operation: 'update', affected: result.rowCount };\n } catch (error) {\n throw new DatabaseError('Failed to update records in table', {\n table,\n sql,\n values: [...values, ...whereValues],\n originalError: formatDbError(error),\n });\n }\n };\n\n const hasNullConflictValue = (\n conflictColumns: string[],\n data: Record<string, any>,\n ): boolean => conflictColumns.some((column) => data[column] === null);\n\n const validateUpsertConflictColumns = (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): void => {\n const missingColumns = conflictColumns.filter(\n (column) => !(column in serializedData),\n );\n\n if (missingColumns.length > 0) {\n throw new DatabaseError('Conflict columns missing from data', {\n table,\n conflictColumns,\n missingColumns,\n availableColumns: Object.keys(serializedData),\n hint: 'All columns specified in ON CONFLICT must be present in the data being inserted. Undefined values are filtered out during serialization - consider using null or an empty string instead.',\n });\n }\n };\n\n const executeStandardUpsert = (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): QueryResult => {\n const keys = Object.keys(serializedData);\n const values = Object.values(serializedData);\n const placeholders = keys.map(() => '?').join(', ');\n const updateSet = keys\n .map((key) => `${key} = excluded.${key}`)\n .join(', ');\n const conflict = conflictColumns.join(', ');\n const sql = `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${placeholders}) ON CONFLICT(${conflict}) DO UPDATE SET ${updateSet}`;\n const result = executeNativeQuery(database, sql, values);\n return { operation: 'upsert', affected: result.rowCount };\n };\n\n const executeNullAwareUpsertAttempt = (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): QueryResult => {\n const keys = Object.keys(serializedData);\n const values = Object.values(serializedData);\n const updateSet = keys.map((key) => `${key} = ?`).join(', ');\n const whereClause = conflictColumns\n .map((column) => `${column} IS ?`)\n .join(' AND ');\n const whereValues = conflictColumns.map(\n (column) => serializedData[column],\n );\n\n const updateResult = executeNativeQuery(\n database,\n `UPDATE ${table} SET ${updateSet} WHERE ${whereClause}`,\n [...values, ...whereValues],\n );\n\n if (updateResult.rowCount > 0) {\n return { operation: 'upsert', affected: updateResult.rowCount };\n }\n\n const placeholders = keys.map(() => '?').join(', ');\n const insertResult = executeNativeQuery(\n database,\n `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${placeholders})`,\n values,\n );\n\n return { operation: 'upsert', affected: insertResult.rowCount };\n };\n\n /**\n * Rejects hostile table/column identifiers before an upsert builds SQL.\n *\n * Called from the public entry points rather than from `executeUpsert`, so\n * the failure reaches the caller naming the bad identifier instead of being\n * flattened into the wrapper's generic \"Failed to upsert record into table\".\n */\n const validateUpsertIdentifiers = (\n table: string,\n conflictColumns: string[],\n data: Record<string, any>,\n ): void => {\n validateTableName(table);\n validateColumnNames([...conflictColumns, ...Object.keys(data)]);\n };\n\n const executeUpsert = async (\n table: string,\n conflictColumns: string[],\n data: Record<string, any>,\n upsertOptions: UpsertOptions | undefined,\n acquireTransaction: boolean,\n ): Promise<QueryResult> => {\n const serializedData = serializeRecord(data);\n validateUpsertConflictColumns(table, conflictColumns, serializedData);\n\n if (\n upsertOptions?.nullsDistinct ||\n !hasNullConflictValue(conflictColumns, serializedData)\n ) {\n return executeStandardUpsert(table, conflictColumns, serializedData);\n }\n\n if (!acquireTransaction) {\n return executeNullAwareUpsertAttempt(\n table,\n conflictColumns,\n serializedData,\n );\n }\n\n const lockKey = `${location.location}:${table}:${conflictColumns\n .map((column) => `${column}:${JSON.stringify(serializedData[column])}`)\n .join('|')}`;\n\n return withNativeNullAwareUpsertLock(lockKey, async () => {\n // This opens a transaction on the shared connection like any other, so\n // it takes the same lock — otherwise a null-aware upsert overlapping a\n // `transaction()` call reintroduces exactly the corruption the lock\n // exists to prevent.\n const releaseConnection = await connectionLock.acquire();\n try {\n database.exec('BEGIN IMMEDIATE');\n const result = executeNullAwareUpsertAttempt(\n table,\n conflictColumns,\n serializedData,\n );\n database.exec('COMMIT');\n return result;\n } catch (error) {\n try {\n database.exec('ROLLBACK');\n } catch (_rollbackError) {\n // Preserve the original failure.\n }\n throw error;\n } finally {\n releaseConnection();\n }\n });\n };\n\n const upsert = async (\n table: string,\n conflictColumns: string[],\n data: Record<string, any>,\n upsertOptions?: UpsertOptions,\n ): Promise<QueryResult> => {\n validateUpsertIdentifiers(table, conflictColumns, data);\n try {\n return await executeUpsert(\n table,\n conflictColumns,\n data,\n upsertOptions,\n true,\n );\n } catch (error) {\n if (getErrorMessage(error) === 'Conflict columns missing from data') {\n throw error;\n }\n throw new DatabaseError('Failed to upsert record into table', {\n table,\n values: data,\n conflictColumns,\n originalError: formatDbError(error),\n });\n }\n };\n\n const upsertInCurrentTransaction = (\n table: string,\n conflictColumns: string[],\n data: Record<string, any>,\n upsertOptions?: UpsertOptions,\n ): Promise<QueryResult> => {\n validateUpsertIdentifiers(table, conflictColumns, data);\n return executeUpsert(table, conflictColumns, data, upsertOptions, false);\n };\n\n const getOrInsert = async (\n table: string,\n where: WhereClause,\n data: Record<string, any>,\n ): Promise<Record<string, any>> => {\n const existing = await get(table, where);\n if (existing) return existing;\n await insert(table, data);\n const inserted = await get(table, where);\n if (!inserted) {\n throw new DatabaseError('Failed to insert and retrieve record', {\n table,\n where,\n data,\n });\n }\n return inserted;\n };\n\n const deleteRecords = async (\n table: string,\n where: WhereClause,\n ): Promise<QueryResult> => {\n validateTableName(table);\n const { sql: whereClause, values } = buildQuestionWhere(where);\n if (!whereClause) {\n throw new DatabaseError(\n 'DELETE requires at least one WHERE condition to prevent accidental deletion of all records',\n { table },\n );\n }\n\n try {\n const result = executeNativeQuery(\n database,\n `DELETE FROM ${table} ${whereClause}`,\n values,\n );\n return { operation: 'delete', affected: result.rowCount };\n } catch (error) {\n throw new DatabaseError('Failed to delete records from table', {\n table,\n where,\n originalError: formatDbError(error),\n });\n }\n };\n\n const count = async (\n table: string,\n where?: WhereClause,\n ): Promise<number> => {\n validateTableName(table);\n if (\n !where ||\n (typeof where === 'object' && Object.keys(where).length === 0)\n ) {\n const result = executeNativeQuery(\n database,\n `SELECT COUNT(*) as count FROM ${table}`,\n );\n return Number(result.rows[0]?.count) || 0;\n }\n\n const { sql: whereClause, values } = buildQuestionWhere(where);\n const result = executeNativeQuery(\n database,\n `SELECT COUNT(*) as count FROM ${table} ${whereClause}`,\n values,\n );\n return Number(result.rows[0]?.count) || 0;\n };\n\n const tableExists = async (tableName: string): Promise<boolean> => {\n const result = executeNativeQuery(\n database,\n \"SELECT name FROM sqlite_master WHERE type='table' AND name=?\",\n [tableName],\n );\n return result.rows.length > 0;\n };\n\n const syncSchema = async (schema: string): Promise<void> => {\n const commands = schema\n .replace(/\\s+/g, ' ')\n .replace(/DEFAULT CAST\\(([^)]+)\\s+AS\\s+\\w+\\)/gi, 'DEFAULT $1')\n .trim()\n .split(';')\n .map((command) => command.trim())\n .filter((command) => command.length > 0);\n\n for (const command of commands) {\n database.exec(command);\n }\n };\n\n const many = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<Record<string, any>[]> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n return executeNativeQuery(database, sql, values).rows;\n } catch (error) {\n throw new DatabaseError('Failed to execute many query', {\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const single = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<Record<string, any> | null> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n return executeNativeQuery(database, sql, values).rows[0] || null;\n } catch (error) {\n throw new DatabaseError('Failed to execute single query', {\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const pluck = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<any> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n const row = executeNativeQuery(database, sql, values).rows[0];\n return row ? Object.values(row)[0] : null;\n } catch (error) {\n throw new DatabaseError('Failed to execute pluck query', {\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const execute = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<void> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n if (values.length === 0) {\n database.exec(sql);\n return;\n }\n executeNativeQuery(database, sql, values);\n } catch (error) {\n throw new DatabaseError('Failed to execute query', {\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const oo = many;\n const oO = single;\n const ox = pluck;\n const xx = execute;\n\n const table = (tableName: string): TableInterface => ({\n insert: (data) => insert(tableName, data),\n get: (where) => get(tableName, where),\n list: (where) => list(tableName, where),\n });\n\n // Names nested savepoints. Only ever appended to an identifier the adapter\n // generates, never caller input.\n let savepointSequence = 0;\n\n /**\n * Re-enters the transaction already in progress under a savepoint.\n *\n * This is what the transaction-scoped interface exposes as `transaction`.\n * Handing it the top-level `transaction` instead — the previous behaviour —\n * sent a nested call into a second BEGIN on the one shared connection; that\n * throws, and the nested call's own ROLLBACK then discarded the *enclosing*\n * transaction's work while later writes committed in autocommit.\n */\n const nestedTransaction = async <T>(\n callback: (tx: DatabaseInterface) => Promise<T>,\n ): Promise<T> => {\n savepointSequence += 1;\n const name = `hv_sp_${savepointSequence}`;\n database.exec(`SAVEPOINT ${name}`);\n try {\n const result = await callback(\n buildDatabaseInterface(\n upsertInCurrentTransaction,\n nestedTransaction,\n refuseNestedBeginTransaction,\n ),\n );\n database.exec(`RELEASE SAVEPOINT ${name}`);\n return result;\n } catch (error) {\n try {\n // ROLLBACK TO leaves the savepoint defined, so release it too or it\n // accumulates for the life of the transaction.\n database.exec(`ROLLBACK TO SAVEPOINT ${name}`);\n database.exec(`RELEASE SAVEPOINT ${name}`);\n } catch {\n // The enclosing transaction is already unwinding; its teardown owns\n // the connection from here.\n }\n throw error;\n }\n };\n\n const transaction = async <T>(\n callback: (tx: DatabaseInterface) => Promise<T>,\n ): Promise<T> =>\n // Held across the whole BEGIN … COMMIT/ROLLBACK span. Without it two\n // overlapping calls raced on the one connection: the second BEGIN threw,\n // its catch ran ROLLBACK, and that rollback ended the *first*\n // transaction — half of its writes durable, half lost, and its promise\n // rejected, so the caller was told nothing had happened.\n connectionLock.run(async () => {\n try {\n database.exec('BEGIN TRANSACTION');\n const txDb = buildDatabaseInterface(\n upsertInCurrentTransaction,\n nestedTransaction,\n refuseNestedBeginTransaction,\n );\n const result = await callback(txDb);\n database.exec('COMMIT');\n return result;\n } catch (error) {\n // A failing ROLLBACK must not replace the caller's error: SQLite\n // reports \"cannot rollback - no transaction is active\" whenever the\n // transaction is already gone, which says nothing about what failed.\n try {\n database.exec('ROLLBACK');\n } catch {\n // Nothing left to roll back.\n }\n throw error;\n }\n });\n\n /**\n * The `beginTransaction` exposed on a transaction-scoped interface.\n *\n * Unlike `transaction`, a manual handle has no savepoint equivalent: it\n * would have to own the connection until the caller ends it, which the\n * enclosing transaction already does. Issuing a second BEGIN threw before\n * transactions were serialized; now it would instead wait on a lock its own\n * caller holds, so refuse explicitly rather than deadlock.\n */\n const refuseNestedBeginTransaction = async (): Promise<never> => {\n throw new NestedTransactionError(\n 'beginTransaction() cannot be called inside a transaction. Use the transaction you already have, or nest with transaction() to re-enter it under a savepoint.',\n { adapter: 'sqlite-native' },\n );\n };\n\n const beginTransaction = async (): Promise<TransactionHandle> => {\n // The handle owns the connection until the caller ends it, so the lock is\n // held across the gap rather than around a single call. A handle that is\n // never committed or rolled back therefore blocks every later transaction\n // until the queue timeout reports it.\n const releaseConnection = await connectionLock.acquire();\n try {\n database.exec('BEGIN TRANSACTION');\n } catch (error) {\n // No transaction was opened, so there is no handle to end it and\n // nothing left to release the connection.\n releaseConnection();\n throw error;\n }\n\n let active = true;\n\n // COMMIT and ROLLBACK can both throw, and the transaction is over either\n // way, so the connection goes back before the error is rethrown.\n const end = (command: 'COMMIT' | 'ROLLBACK'): void => {\n if (!active) {\n throw new DatabaseError('Transaction already ended', {});\n }\n try {\n database.exec(command);\n } catch (error) {\n // COMMIT can fail and leave the transaction *open* — SQLite documents\n // exactly that for SQLITE_BUSY. Releasing the connection then would hand\n // the next queued caller a connection still inside a transaction: its\n // BEGIN would throw, and its catch would ROLLBACK, discarding this\n // transaction's work. So normalize before releasing, the way the pooled\n // adapter's discardTxClient does.\n try {\n database.exec('ROLLBACK');\n } catch {\n // Already gone; nothing left to normalize.\n }\n throw error;\n } finally {\n active = false;\n releaseConnection();\n }\n };\n\n const commit = async (): Promise<void> => end('COMMIT');\n\n const rollback = async (): Promise<void> => end('ROLLBACK');\n\n return {\n // The handle is inside a transaction for the same reason a callback\n // scope is, so it re-enters via savepoint too.\n ...buildDatabaseInterface(\n upsertInCurrentTransaction,\n nestedTransaction,\n refuseNestedBeginTransaction,\n ),\n commit,\n rollback,\n isActive: () => active,\n };\n };\n\n const initializeSchemas = async (\n schemaOptions: SchemaInitializationOptions,\n ): Promise<void> => {\n const schemaManager = new DatabaseSchemaManager();\n await schemaManager.initializeSchemas(\n buildDatabaseInterface(upsert),\n schemaOptions,\n );\n };\n\n const getTableSchema = async (\n tableName: string,\n ): Promise<TableSchemaInfo | null> => {\n validateTableName(tableName);\n\n if (!(await tableExists(tableName))) {\n return null;\n }\n\n const columnRows = database\n .prepare(`PRAGMA table_info(${quoteIdentifier(tableName)})`)\n .all()\n .map(toPlainRow);\n\n const columns: Record<string, ColumnDefinition> = {};\n for (const row of columnRows) {\n columns[row.name as string] = {\n type: row.type as string,\n primaryKey: row.pk === 1,\n notNull: row.notnull === 1,\n defaultValue: row.dflt_value,\n };\n }\n\n const indexRows = executeNativeQuery(\n database,\n `SELECT name FROM sqlite_master\n WHERE type = 'index'\n AND tbl_name = ?\n AND name NOT LIKE 'sqlite_%'`,\n [tableName],\n ).rows;\n\n const indexes: IndexDefinition[] = [];\n for (const row of indexRows) {\n const indexName = row.name as string;\n const indexInfoRows = database\n .prepare(`PRAGMA index_info(${quoteIdentifier(indexName)})`)\n .all()\n .map(toPlainRow);\n const indexListRows = database\n .prepare(`PRAGMA index_list(${quoteIdentifier(tableName)})`)\n .all()\n .map(toPlainRow);\n\n indexes.push({\n name: indexName,\n columns: indexInfoRows.map((info: Record<string, any>) => info.name),\n unique: indexListRows.some(\n (info: Record<string, any>) =>\n info.name === indexName && info.unique === 1,\n ),\n });\n }\n\n const fkRows = database\n .prepare(`PRAGMA foreign_key_list(${quoteIdentifier(tableName)})`)\n .all()\n .map(toPlainRow);\n\n return {\n tableName,\n columns,\n indexes,\n foreignKeys: fkRows.map((row: Record<string, any>) => ({\n column: row.from as string,\n referencesTable: row.table as string,\n referencesColumn: row.to as string,\n onDelete: row.on_delete as string | undefined,\n onUpdate: row.on_update as string | undefined,\n })),\n };\n };\n\n const alterTable = {\n addColumn: async (\n tableName: string,\n column: ColumnDefinitionWithName,\n ): Promise<void> => {\n validateTableName(tableName);\n validateColumnName(column.name);\n const sql = generateAddColumnStatement(tableName, column, 'sqlite');\n executeNativeQuery(database, sql);\n },\n\n addIndex: async (\n tableName: string,\n index: IndexDefinition,\n ): Promise<void> => {\n validateTableName(tableName);\n validateIndexName(index.name);\n for (const column of index.columns) {\n validateColumnName(column);\n }\n const sql = generateCreateIndexStatement(tableName, index);\n executeNativeQuery(database, sql);\n },\n };\n\n const close = async (): Promise<void> => {\n if (closed) {\n return;\n }\n closed = true;\n for (const closeable of notificationCloseables) {\n closeable.close?.();\n }\n notificationCloseables.clear();\n honkerDb?.close?.();\n database.close();\n if (options.dbid) {\n nativeMemoryConnectionCache.delete(options.dbid);\n }\n };\n\n function buildDatabaseInterface(\n upsertImpl: DatabaseInterface['upsert'],\n transactionImpl: DatabaseInterface['transaction'] = transaction,\n beginTransactionImpl: DatabaseInterface['beginTransaction'] = beginTransaction,\n ): DatabaseInterface {\n return {\n url,\n client: database,\n query,\n insert,\n update,\n upsert: upsertImpl,\n get,\n list,\n getOrInsert,\n delete: deleteRecords,\n count,\n table,\n tableExists,\n many,\n single,\n pluck,\n execute,\n oo,\n oO,\n ox,\n xx,\n syncSchema,\n initializeSchemas,\n transaction: transactionImpl,\n beginTransaction: beginTransactionImpl,\n getTableSchema,\n alterTable,\n close,\n ...(vectorCapabilities ? { vector: vectorCapabilities } : {}),\n ...(notificationCapabilities\n ? { notifications: notificationCapabilities }\n : {}),\n };\n }\n\n const db = buildDatabaseInterface(upsert);\n const resolvedSchemas = resolveSchemas(options.schemas);\n if (resolvedSchemas && Object.keys(resolvedSchemas).length > 0) {\n createNativeTablesFromSchemas(database, resolvedSchemas);\n }\n\n return db;\n } catch (error) {\n try {\n database.close();\n } catch (_closeError) {\n // Preserve the original failure.\n }\n throw error;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { DatabaseError } from "@happyvertical/utils";
|
|
2
|
+
//#region src/shared/transaction-lock.ts
|
|
3
|
+
/**
|
|
4
|
+
* How long a queued transaction waits for the connection before giving up.
|
|
5
|
+
*
|
|
6
|
+
* A transaction that legitimately runs longer than this will make the ones
|
|
7
|
+
* behind it fail rather than stall indefinitely. Raise it with the adapter's
|
|
8
|
+
* `transactionQueueTimeout` option if that is the wrong trade for a workload.
|
|
9
|
+
*/
|
|
10
|
+
var DEFAULT_TRANSACTION_QUEUE_TIMEOUT_MS = 3e4;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a lock that serializes transactions on one connection.
|
|
13
|
+
*
|
|
14
|
+
* SQLite, DuckDB and the JSON adapter each drive a single connection, and a
|
|
15
|
+
* connection can only be in one transaction at a time. `transaction()` used to
|
|
16
|
+
* issue `BEGIN` on that shared connection with no serialization and no record
|
|
17
|
+
* of whether one was already open, so two overlapping calls corrupted each
|
|
18
|
+
* other: the second `BEGIN` threw, its `catch` ran `ROLLBACK`, and that
|
|
19
|
+
* rollback landed on the *first* transaction — committing part of its work,
|
|
20
|
+
* discarding the rest, and rejecting its promise. Nesting was not required;
|
|
21
|
+
* two concurrent requests were enough, which made it reachable from any server
|
|
22
|
+
* handling more than one at a time.
|
|
23
|
+
*
|
|
24
|
+
* The lock spans the whole `BEGIN` … `COMMIT`/`ROLLBACK` sequence, so a queued
|
|
25
|
+
* transaction starts only once the previous one has ended. It is a
|
|
26
|
+
* promise-chain mutex, the same shape as the `withNullAwareUpsertLock` the
|
|
27
|
+
* SQLite adapter already used for the narrower upsert case.
|
|
28
|
+
*
|
|
29
|
+
* Re-entrant calls must not come through here. A nested `tx.transaction()`
|
|
30
|
+
* arrives via the transaction-scoped interface, which re-enters under a
|
|
31
|
+
* `SAVEPOINT` (SQLite) or refuses (DuckDB, JSON) without ever reaching the
|
|
32
|
+
* top-level `transaction()`; routing one here instead would make it wait on a
|
|
33
|
+
* lock its own caller is holding.
|
|
34
|
+
*
|
|
35
|
+
* @param adapter - Adapter name, for the timeout error
|
|
36
|
+
* @param timeoutMs - Ceiling on how long to wait for a turn
|
|
37
|
+
*/
|
|
38
|
+
function createTransactionLock(adapter, timeoutMs = DEFAULT_TRANSACTION_QUEUE_TIMEOUT_MS) {
|
|
39
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) throw new DatabaseError("transactionQueueTimeout must be a positive, finite number of milliseconds", {
|
|
40
|
+
adapter,
|
|
41
|
+
timeoutMs
|
|
42
|
+
});
|
|
43
|
+
let tail = Promise.resolve();
|
|
44
|
+
const acquire = async () => {
|
|
45
|
+
const previous = tail;
|
|
46
|
+
let release = () => {};
|
|
47
|
+
const current = new Promise((resolve) => {
|
|
48
|
+
release = resolve;
|
|
49
|
+
});
|
|
50
|
+
tail = previous.then(() => current, () => current);
|
|
51
|
+
let timer;
|
|
52
|
+
try {
|
|
53
|
+
await new Promise((resolve, reject) => {
|
|
54
|
+
timer = setTimeout(() => {
|
|
55
|
+
reject(new DatabaseError(`Timed out after ${timeoutMs}ms waiting for the ${adapter} connection's current transaction to finish. This connection runs one transaction at a time. Usually one of: a beginTransaction() handle that was never committed or rolled back; a top-level db.* call that opens its own transaction being made inside a transaction callback, which waits on the connection its own caller holds (use the tx-scoped method instead); or simply more queued transactions than transactionQueueTimeout allows for.`, {
|
|
56
|
+
adapter,
|
|
57
|
+
timeoutMs
|
|
58
|
+
}));
|
|
59
|
+
}, timeoutMs);
|
|
60
|
+
previous.then(resolve, resolve);
|
|
61
|
+
});
|
|
62
|
+
} catch (error) {
|
|
63
|
+
release();
|
|
64
|
+
throw error;
|
|
65
|
+
} finally {
|
|
66
|
+
if (timer) clearTimeout(timer);
|
|
67
|
+
}
|
|
68
|
+
let released = false;
|
|
69
|
+
return () => {
|
|
70
|
+
if (released) return;
|
|
71
|
+
released = true;
|
|
72
|
+
release();
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
const run = async (work) => {
|
|
76
|
+
const release = await acquire();
|
|
77
|
+
try {
|
|
78
|
+
return await work();
|
|
79
|
+
} finally {
|
|
80
|
+
release();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
acquire,
|
|
85
|
+
run
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/shared/types.ts
|
|
90
|
+
/**
|
|
91
|
+
* Error thrown when `transaction()` is called on a transaction-scoped interface
|
|
92
|
+
* belonging to an adapter whose engine has no savepoints.
|
|
93
|
+
*
|
|
94
|
+
* DuckDB — and therefore the JSON adapter built on it — does not implement
|
|
95
|
+
* `SAVEPOINT`, so there is no way to re-enter the current transaction. Adapters
|
|
96
|
+
* that can re-enter (PostgreSQL, SQLite) do so under a savepoint and never
|
|
97
|
+
* throw this.
|
|
98
|
+
*
|
|
99
|
+
* The alternative would be to open a second, independent transaction, which is
|
|
100
|
+
* what these adapters used to do; on a shared connection that silently
|
|
101
|
+
* destroyed the enclosing transaction's uncommitted work.
|
|
102
|
+
*
|
|
103
|
+
* @example Ensuring a helper works on every adapter
|
|
104
|
+
* ```typescript
|
|
105
|
+
* // Take the transaction as a parameter rather than opening a nested one.
|
|
106
|
+
* async function recordClick(db: DatabaseInterface, id: string) {
|
|
107
|
+
* await db.update('links', { id }, { clicks: 1 });
|
|
108
|
+
* }
|
|
109
|
+
* await db.transaction(async (tx) => recordClick(tx, id));
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
var NestedTransactionError = class extends DatabaseError {
|
|
113
|
+
constructor(message, context) {
|
|
114
|
+
super(message, context);
|
|
115
|
+
this.name = "NestedTransactionError";
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Resolves a SchemasOption to its concrete value.
|
|
120
|
+
*
|
|
121
|
+
* Adapters that need schemas (JSON, DuckDB) call this to unwrap the
|
|
122
|
+
* lazy function. Adapters that don't (Postgres, SQLite) simply ignore
|
|
123
|
+
* the `schemas` option without calling this.
|
|
124
|
+
*/
|
|
125
|
+
function resolveSchemas(schemas) {
|
|
126
|
+
if (!schemas) return void 0;
|
|
127
|
+
if (typeof schemas === "function") return schemas();
|
|
128
|
+
return schemas;
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
export { resolveSchemas as n, createTransactionLock as r, NestedTransactionError as t };
|
|
132
|
+
|
|
133
|
+
//# sourceMappingURL=types-uVFHgaSU.js.map
|