uql-orm 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/browser/uql-browser.min.js.map +3 -3
- package/dist/bunSql/bunSqlCockroachDialect.d.ts +2 -2
- package/dist/bunSql/bunSqlCockroachDialect.js +4 -10
- package/dist/bunSql/bunSqlPostgresDialect.d.ts +2 -2
- package/dist/bunSql/bunSqlPostgresDialect.js +4 -10
- package/dist/cockroachdb/cockroachDialect.d.ts +20 -4
- package/dist/cockroachdb/cockroachDialect.js +33 -18
- package/dist/d1/d1Querier.d.ts +4 -3
- package/dist/d1/d1Querier.js +0 -3
- package/dist/d1/d1SqliteDialect.d.ts +7 -0
- package/dist/d1/d1SqliteDialect.js +9 -0
- package/dist/dialect/abstractDialect.d.ts +8 -3
- package/dist/dialect/abstractDialect.js +9 -4
- package/dist/dialect/abstractSqlDialect.d.ts +49 -8
- package/dist/dialect/abstractSqlDialect.js +95 -47
- package/dist/dialect/index.d.ts +1 -0
- package/dist/dialect/index.js +1 -0
- package/dist/dialect/indexSqlDialect.d.ts +57 -0
- package/dist/dialect/indexSqlDialect.js +101 -0
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +26 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +57 -1
- package/dist/dialect/pgLikeSqlDialect.d.ts +37 -22
- package/dist/dialect/pgLikeSqlDialect.js +97 -68
- package/dist/dialect/vectorCast.d.ts +21 -0
- package/dist/dialect/vectorCast.js +28 -0
- package/dist/dialect/vectorSqlDialect.d.ts +10 -3
- package/dist/dialect/vectorSqlDialect.js +19 -8
- package/dist/entity/decorator/bag.d.ts +35 -0
- package/dist/entity/decorator/bag.js +54 -0
- package/dist/entity/decorator/entity.d.ts +28 -2
- package/dist/entity/decorator/entity.js +40 -2
- package/dist/entity/decorator/members.d.ts +51 -0
- package/dist/entity/decorator/members.js +51 -0
- package/dist/entity/index.d.ts +3 -1
- package/dist/entity/index.js +3 -1
- package/dist/entity/metadata/definition.d.ts +23 -8
- package/dist/entity/metadata/definition.js +56 -100
- package/dist/http/contract.js +1 -2
- package/dist/http/handler.js +5 -26
- package/dist/http/query.js +1 -1
- package/dist/libsql/libsqlDialect.d.ts +12 -2
- package/dist/libsql/libsqlDialect.js +14 -2
- package/dist/libsql/libsqlQuerier.d.ts +9 -22
- package/dist/libsql/libsqlQuerier.js +9 -85
- package/dist/libsql/libsqlQuerierPool.d.ts +10 -8
- package/dist/libsql/libsqlQuerierPool.js +17 -15
- package/dist/maria/mariaDialect.d.ts +26 -4
- package/dist/maria/mariaDialect.js +54 -15
- package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
- package/dist/migrate/acquireQuerierForMigrations.js +28 -0
- package/dist/migrate/builder/migrationBuilder.d.ts +9 -29
- package/dist/migrate/builder/migrationBuilder.js +58 -76
- package/dist/migrate/builder/splitSqlStatements.d.ts +0 -2
- package/dist/migrate/builder/splitSqlStatements.js +0 -2
- package/dist/migrate/builder/tableBuilder.d.ts +12 -2
- package/dist/migrate/builder/tableBuilder.js +71 -119
- package/dist/migrate/builder/types.d.ts +30 -41
- package/dist/migrate/cli-config.js +24 -8
- package/dist/migrate/cli.d.ts +3 -2
- package/dist/migrate/cli.js +26 -49
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -4
- package/dist/migrate/codegen/entityCodeGenerator.js +8 -67
- package/dist/migrate/codegen/fieldOptionsSource.d.ts +10 -0
- package/dist/migrate/codegen/fieldOptionsSource.js +55 -0
- package/dist/migrate/codegen/index.d.ts +0 -3
- package/dist/migrate/codegen/index.js +0 -6
- package/dist/migrate/drift/driftDetector.d.ts +10 -1
- package/dist/migrate/drift/driftDetector.js +22 -5
- package/dist/migrate/generator/indexNodeToSchema.d.ts +9 -0
- package/dist/migrate/generator/indexNodeToSchema.js +14 -0
- package/dist/migrate/generator/mongoCommand.d.ts +57 -0
- package/dist/migrate/generator/mongoCommand.js +26 -0
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +9 -20
- package/dist/migrate/generator/mongoSchemaGenerator.js +40 -82
- package/dist/migrate/index.d.ts +0 -1
- package/dist/migrate/index.js +0 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +30 -11
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +61 -57
- package/dist/migrate/introspection/baseSqlIntrospector.d.ts +0 -1
- package/dist/migrate/introspection/baseSqlIntrospector.js +3 -12
- package/dist/migrate/introspection/mongoIntrospector.js +9 -15
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +5 -8
- package/dist/migrate/introspection/mysqlIntrospector.js +4 -10
- package/dist/migrate/introspection/postgresIntrospector.d.ts +5 -8
- package/dist/migrate/introspection/postgresIntrospector.js +4 -10
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -5
- package/dist/migrate/introspection/sqliteIntrospector.js +26 -18
- package/dist/migrate/migrator.d.ts +23 -3
- package/dist/migrate/migrator.js +101 -150
- package/dist/migrate/schemaGenerator.d.ts +26 -37
- package/dist/migrate/schemaGenerator.js +69 -174
- package/dist/migrate/schemaGeneratorAsync.d.ts +2 -2
- package/dist/migrate/schemaGeneratorAsync.js +3 -3
- package/dist/migrate/storage/databaseStorage.d.ts +2 -0
- package/dist/migrate/storage/databaseStorage.js +12 -27
- package/dist/mongo/mongoDialect.d.ts +2 -0
- package/dist/mongo/mongoDialect.js +14 -3
- package/dist/mongo/mongodbQuerier.js +171 -229
- package/dist/mysql/mysqlDialect.d.ts +8 -0
- package/dist/mysql/mysqlDialect.js +13 -0
- package/dist/nestjs/uqlContextInterceptor.js +58 -22
- package/dist/nestjs/uqlModule.d.ts +2 -11
- package/dist/nestjs/uqlModule.js +93 -44
- package/dist/postgres/postgresDialect.js +2 -2
- package/dist/querier/abstractQuerier.d.ts +28 -0
- package/dist/querier/abstractQuerier.js +47 -22
- package/dist/querier/abstractQuerierPool.d.ts +3 -0
- package/dist/querier/abstractQuerierPool.js +5 -3
- package/dist/querier/abstractSqlQuerier.d.ts +1 -3
- package/dist/querier/abstractSqlQuerier.js +48 -96
- package/dist/querier/index.d.ts +3 -1
- package/dist/querier/index.js +3 -1
- package/dist/querier/querierContext.browser.d.ts +12 -0
- package/dist/querier/querierContext.browser.js +18 -0
- package/dist/querier/querierContext.d.ts +22 -0
- package/dist/querier/querierContext.js +42 -0
- package/dist/querier/queryError.d.ts +22 -0
- package/dist/querier/queryError.js +20 -0
- package/dist/querier/transactional.d.ts +26 -0
- package/dist/querier/transactional.js +43 -0
- package/dist/schema/canonicalType.d.ts +14 -2
- package/dist/schema/canonicalType.js +41 -31
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/schemaASTBuilder.d.ts +9 -2
- package/dist/schema/schemaASTBuilder.js +58 -49
- package/dist/schema/types.d.ts +10 -58
- package/dist/sqlite/abstractSqliteQuerier.d.ts +46 -0
- package/dist/sqlite/abstractSqliteQuerier.js +44 -0
- package/dist/sqlite/bunSqliteAdapter.bun.d.ts +26 -0
- package/dist/sqlite/bunSqliteAdapter.bun.js +25 -0
- package/dist/sqlite/hranaQuerier.d.ts +54 -0
- package/dist/sqlite/hranaQuerier.js +68 -0
- package/dist/sqlite/hranaQuerierPool.d.ts +21 -0
- package/dist/sqlite/hranaQuerierPool.js +25 -0
- package/dist/sqlite/index.d.ts +3 -1
- package/dist/sqlite/index.js +3 -1
- package/dist/sqlite/localSqliteQuerierPool.d.ts +31 -0
- package/dist/sqlite/localSqliteQuerierPool.js +34 -0
- package/dist/sqlite/nodeSqliteAdapter.d.ts +35 -0
- package/dist/sqlite/nodeSqliteAdapter.js +28 -0
- package/dist/sqlite/nodeSqliteQuerierPool.d.ts +28 -0
- package/dist/sqlite/nodeSqliteQuerierPool.js +29 -0
- package/dist/sqlite/sqliteDialect.d.ts +18 -5
- package/dist/sqlite/sqliteDialect.js +23 -36
- package/dist/sqlite/sqliteQuerier.d.ts +16 -9
- package/dist/sqlite/sqliteQuerier.js +4 -27
- package/dist/sqlite/sqliteQuerierPool.d.ts +11 -14
- package/dist/sqlite/sqliteQuerierPool.js +16 -24
- package/dist/turso/index.d.ts +3 -0
- package/dist/turso/index.js +3 -0
- package/dist/turso/local.d.ts +3 -0
- package/dist/turso/local.js +3 -0
- package/dist/turso/tursoDialect.d.ts +15 -0
- package/dist/turso/tursoDialect.js +18 -0
- package/dist/turso/tursoLocalQuerier.d.ts +25 -0
- package/dist/turso/tursoLocalQuerier.js +20 -0
- package/dist/turso/tursoLocalQuerierPool.d.ts +32 -0
- package/dist/turso/tursoLocalQuerierPool.js +39 -0
- package/dist/turso/tursoQuerier.d.ts +10 -0
- package/dist/turso/tursoQuerier.js +10 -0
- package/dist/turso/tursoQuerierPool.d.ts +39 -0
- package/dist/turso/tursoQuerierPool.js +38 -0
- package/dist/type/config.d.ts +0 -6
- package/dist/type/dialect.d.ts +12 -4
- package/dist/type/dialect.js +7 -1
- package/dist/type/entity.d.ts +154 -26
- package/dist/type/migration.d.ts +32 -23
- package/dist/type/querier.d.ts +6 -0
- package/dist/type/vector.d.ts +5 -2
- package/dist/util/hook.util.js +1 -1
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.js +1 -0
- package/dist/util/indexColumn.util.d.ts +6 -0
- package/dist/util/indexColumn.util.js +26 -0
- package/package.json +21 -16
- package/dist/entity/decorator/field.d.ts +0 -2
- package/dist/entity/decorator/field.js +0 -7
- package/dist/entity/decorator/filter.d.ts +0 -7
- package/dist/entity/decorator/filter.js +0 -11
- package/dist/entity/decorator/hook.d.ts +0 -7
- package/dist/entity/decorator/hook.js +0 -14
- package/dist/entity/decorator/id.d.ts +0 -2
- package/dist/entity/decorator/id.js +0 -7
- package/dist/entity/decorator/index-decorator.d.ts +0 -29
- package/dist/entity/decorator/index-decorator.js +0 -26
- package/dist/entity/decorator/index.d.ts +0 -8
- package/dist/entity/decorator/index.js +0 -8
- package/dist/entity/decorator/relation.d.ts +0 -8
- package/dist/entity/decorator/relation.js +0 -19
- package/dist/migrate/codegen/entityMerger.d.ts +0 -114
- package/dist/migrate/codegen/entityMerger.js +0 -293
- package/dist/migrate/codegen/migrationCodeGenerator.d.ts +0 -61
- package/dist/migrate/codegen/migrationCodeGenerator.js +0 -355
- package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
- package/dist/migrate/codegen/smartRelationDetector.js +0 -134
- package/dist/migrate/generator/index.d.ts +0 -2
- package/dist/migrate/generator/index.js +0 -2
- package/dist/migrate/sync/index.d.ts +0 -6
- package/dist/migrate/sync/index.js +0 -6
- package/dist/migrate/sync/schemaSync.d.ts +0 -131
- package/dist/migrate/sync/schemaSync.js +0 -259
- package/dist/querier/decorator/index.d.ts +0 -4
- package/dist/querier/decorator/index.js +0 -4
- package/dist/querier/decorator/injectQuerier.d.ts +0 -3
- package/dist/querier/decorator/injectQuerier.js +0 -33
- package/dist/querier/decorator/log.d.ts +0 -32
- package/dist/querier/decorator/log.js +0 -52
- package/dist/querier/decorator/serialized.d.ts +0 -6
- package/dist/querier/decorator/serialized.js +0 -13
- package/dist/querier/decorator/transactional.d.ts +0 -6
- package/dist/querier/decorator/transactional.js +0 -48
- package/dist/sqlite/betterSqlite3Dialect.d.ts +0 -9
- package/dist/sqlite/betterSqlite3Dialect.js +0 -9
|
@@ -1,3 +1,47 @@
|
|
|
1
1
|
import { AbstractSqlQuerier } from '../querier/index.js';
|
|
2
|
+
import { throwPendingTransaction } from '../util/index.js';
|
|
3
|
+
/** Bound parameters reach a driver as `unknown[]` from the compiler; every driver types them narrowly. */
|
|
4
|
+
export function toSqliteBindValues(values) {
|
|
5
|
+
return (values || []);
|
|
6
|
+
}
|
|
2
7
|
export class AbstractSqliteQuerier extends AbstractSqlQuerier {
|
|
8
|
+
/**
|
|
9
|
+
* SQLite drivers hold a single shared handle rather than a connection from a pool, so releasing
|
|
10
|
+
* a querier returns nothing; it only asserts the unit of work was finished. Drivers owning a
|
|
11
|
+
* closable per-querier connection override this.
|
|
12
|
+
*/
|
|
13
|
+
async internalRelease() {
|
|
14
|
+
if (this.hasOpenTransaction) {
|
|
15
|
+
throwPendingTransaction();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Querier for the SQLite drivers that expose prepared statements: `better-sqlite3`, `bun:sqlite`
|
|
21
|
+
* (through `adaptBunSqlite`) and the embedded Turso engine. They differ only in whether preparing and
|
|
22
|
+
* stepping are synchronous, which `await` and `for await` absorb, so the read/write/stream logic -
|
|
23
|
+
* including the `reader` rule below, whose loss silently drops inserted ids - is written once.
|
|
24
|
+
*/
|
|
25
|
+
export class PreparedSqliteQuerier extends AbstractSqliteQuerier {
|
|
26
|
+
async internalAll(query, values) {
|
|
27
|
+
const stmt = await this.prepare(query);
|
|
28
|
+
return (await stmt.all(...toSqliteBindValues(values)));
|
|
29
|
+
}
|
|
30
|
+
async *internalStream(query, values) {
|
|
31
|
+
const stmt = await this.prepare(query);
|
|
32
|
+
for await (const row of stmt.iterate(...toSqliteBindValues(values))) {
|
|
33
|
+
yield row;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async internalRun(query, values) {
|
|
37
|
+
const stmt = await this.prepare(query);
|
|
38
|
+
// `reader` is true for any statement with a RETURNING clause; `.run()` silently discards
|
|
39
|
+
// returned rows, so those statements must go through `.all()` instead.
|
|
40
|
+
if (stmt.reader) {
|
|
41
|
+
const rows = (await stmt.all(...toSqliteBindValues(values)));
|
|
42
|
+
return this.buildUpdateResult({ rows });
|
|
43
|
+
}
|
|
44
|
+
const { changes, lastInsertRowid } = await stmt.run(...toSqliteBindValues(values));
|
|
45
|
+
return this.buildUpdateResult({ changes, id: lastInsertRowid });
|
|
46
|
+
}
|
|
3
47
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SqliteBindValue, SqliteRunResult } from './abstractSqliteQuerier.js';
|
|
2
|
+
import type { SqliteDatabase } from './sqliteQuerier.js';
|
|
3
|
+
/** A `bun:sqlite` statement: better-sqlite3-shaped, except it reports columns instead of `reader`. */
|
|
4
|
+
type BunStatement = {
|
|
5
|
+
columnNames: string[];
|
|
6
|
+
all(...values: SqliteBindValue[]): unknown[];
|
|
7
|
+
run(...values: SqliteBindValue[]): SqliteRunResult;
|
|
8
|
+
iterate(...values: SqliteBindValue[]): Iterable<unknown>;
|
|
9
|
+
};
|
|
10
|
+
type BunDatabase = {
|
|
11
|
+
prepare(sql: string): BunStatement;
|
|
12
|
+
loadExtension(path: string): void;
|
|
13
|
+
close(): unknown;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Presents a `bun:sqlite` handle as a {@link SqliteDatabase}.
|
|
17
|
+
*
|
|
18
|
+
* @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
|
|
19
|
+
* would take the `run()` path, which discards returned rows, and inserts would report no ids.
|
|
20
|
+
* `columnNames` is non-empty for exactly the statements better-sqlite3 marks as readers.
|
|
21
|
+
*
|
|
22
|
+
* Lives in a `.bun.ts` file because it only ever executes under Bun: the Node coverage run cannot
|
|
23
|
+
* reach it, and `sqliteQuerier.bun.test.ts` covers it under `test:bun` instead.
|
|
24
|
+
*/
|
|
25
|
+
export declare function adaptBunSqlite(db: BunDatabase): SqliteDatabase;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presents a `bun:sqlite` handle as a {@link SqliteDatabase}.
|
|
3
|
+
*
|
|
4
|
+
* @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
|
|
5
|
+
* would take the `run()` path, which discards returned rows, and inserts would report no ids.
|
|
6
|
+
* `columnNames` is non-empty for exactly the statements better-sqlite3 marks as readers.
|
|
7
|
+
*
|
|
8
|
+
* Lives in a `.bun.ts` file because it only ever executes under Bun: the Node coverage run cannot
|
|
9
|
+
* reach it, and `sqliteQuerier.bun.test.ts` covers it under `test:bun` instead.
|
|
10
|
+
*/
|
|
11
|
+
export function adaptBunSqlite(db) {
|
|
12
|
+
return {
|
|
13
|
+
prepare: (sql) => {
|
|
14
|
+
const stmt = db.prepare(sql);
|
|
15
|
+
return {
|
|
16
|
+
reader: stmt.columnNames.length > 0,
|
|
17
|
+
all: (...values) => stmt.all(...values),
|
|
18
|
+
run: (...values) => stmt.run(...values),
|
|
19
|
+
iterate: (...values) => stmt.iterate(...values),
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
loadExtension: (path) => db.loadExtension(path),
|
|
23
|
+
close: () => db.close(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ExtraOptions, TransactionOptions } from '../type/index.js';
|
|
2
|
+
import { AbstractSqliteQuerier, type SqliteBindValue } from './abstractSqliteQuerier.js';
|
|
3
|
+
import type { SqliteDialect } from './sqliteDialect.js';
|
|
4
|
+
/**
|
|
5
|
+
* Structural subset of the Hrana client API, the wire protocol shared by `@libsql/client` and
|
|
6
|
+
* `@tursodatabase/serverless/compat`. Declared here rather than imported so a querier works with
|
|
7
|
+
* any client of this shape (including `@libsql/client/web` and `@libsql/client-wasm`) without
|
|
8
|
+
* depending on vendor types.
|
|
9
|
+
*/
|
|
10
|
+
export type HranaInValue = SqliteBindValue | ArrayBuffer | Date;
|
|
11
|
+
export type HranaResultSet = {
|
|
12
|
+
rows: unknown[];
|
|
13
|
+
rowsAffected: number;
|
|
14
|
+
lastInsertRowid?: bigint;
|
|
15
|
+
};
|
|
16
|
+
export type HranaExecutor = {
|
|
17
|
+
execute(stmt: {
|
|
18
|
+
sql: string;
|
|
19
|
+
args?: HranaInValue[];
|
|
20
|
+
}): Promise<HranaResultSet>;
|
|
21
|
+
};
|
|
22
|
+
export type HranaTransaction = HranaExecutor & {
|
|
23
|
+
commit(): Promise<void>;
|
|
24
|
+
rollback(): Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
export type HranaClient = HranaExecutor & {
|
|
27
|
+
transaction(mode?: 'write' | 'read' | 'deferred'): Promise<HranaTransaction>;
|
|
28
|
+
close(): void;
|
|
29
|
+
};
|
|
30
|
+
/** Connection lifecycle for a {@link HranaQuerier} (separate from {@link ExtraOptions}). */
|
|
31
|
+
export type HranaQuerierConnectionOptions = {
|
|
32
|
+
/** When set, {@link release} closes {@link HranaQuerier.client} (one-shot migration connections). */
|
|
33
|
+
closeClientOnRelease?: boolean;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Querier for SQLite databases reached through a Hrana client.
|
|
37
|
+
*
|
|
38
|
+
* @remarks Transactions use the client's own session handle rather than `BEGIN`/`COMMIT` statements,
|
|
39
|
+
* because over plain HTTP consecutive requests need not share a connection.
|
|
40
|
+
*/
|
|
41
|
+
export declare class HranaQuerier extends AbstractSqliteQuerier {
|
|
42
|
+
readonly client: HranaClient;
|
|
43
|
+
readonly extra?: ExtraOptions | undefined;
|
|
44
|
+
private tx?;
|
|
45
|
+
private readonly closeClientOnRelease;
|
|
46
|
+
constructor(client: HranaClient, dialect: SqliteDialect, extra?: ExtraOptions | undefined, connection?: HranaQuerierConnectionOptions);
|
|
47
|
+
internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
|
|
48
|
+
internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
|
|
49
|
+
get hasOpenTransaction(): boolean;
|
|
50
|
+
beginTransaction(_opts?: TransactionOptions): Promise<void>;
|
|
51
|
+
commitTransaction(): Promise<void>;
|
|
52
|
+
rollbackTransaction(): Promise<void>;
|
|
53
|
+
internalRelease(): Promise<void>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { throwNoPendingTransaction, throwPendingTransaction } from '../util/index.js';
|
|
2
|
+
import { AbstractSqliteQuerier } from './abstractSqliteQuerier.js';
|
|
3
|
+
/**
|
|
4
|
+
* Querier for SQLite databases reached through a Hrana client.
|
|
5
|
+
*
|
|
6
|
+
* @remarks Transactions use the client's own session handle rather than `BEGIN`/`COMMIT` statements,
|
|
7
|
+
* because over plain HTTP consecutive requests need not share a connection.
|
|
8
|
+
*/
|
|
9
|
+
export class HranaQuerier extends AbstractSqliteQuerier {
|
|
10
|
+
client;
|
|
11
|
+
extra;
|
|
12
|
+
tx;
|
|
13
|
+
closeClientOnRelease;
|
|
14
|
+
constructor(client, dialect, extra, connection) {
|
|
15
|
+
super(dialect, extra);
|
|
16
|
+
this.client = client;
|
|
17
|
+
this.extra = extra;
|
|
18
|
+
this.closeClientOnRelease = connection?.closeClientOnRelease ?? false;
|
|
19
|
+
}
|
|
20
|
+
async internalAll(query, values) {
|
|
21
|
+
const target = this.tx || this.client;
|
|
22
|
+
const res = await target.execute({ sql: query, args: values });
|
|
23
|
+
return res.rows;
|
|
24
|
+
}
|
|
25
|
+
async internalRun(query, values) {
|
|
26
|
+
const target = this.tx || this.client;
|
|
27
|
+
const res = await target.execute({ sql: query, args: values });
|
|
28
|
+
const rows = res.rows;
|
|
29
|
+
// `rowsAffected` is unreliably 0 whenever the statement has a RETURNING clause, so prefer
|
|
30
|
+
// the actual row count when rows were returned.
|
|
31
|
+
return this.buildUpdateResult({ rows, changes: rows.length || res.rowsAffected, id: res.lastInsertRowid });
|
|
32
|
+
}
|
|
33
|
+
get hasOpenTransaction() {
|
|
34
|
+
return !!this.tx;
|
|
35
|
+
}
|
|
36
|
+
async beginTransaction(_opts) {
|
|
37
|
+
return this.serialize(async () => {
|
|
38
|
+
if (this.tx) {
|
|
39
|
+
throwPendingTransaction();
|
|
40
|
+
}
|
|
41
|
+
this.tx = await this.client.transaction('write');
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async commitTransaction() {
|
|
45
|
+
return this.serialize(async () => {
|
|
46
|
+
if (!this.tx) {
|
|
47
|
+
throwNoPendingTransaction();
|
|
48
|
+
}
|
|
49
|
+
await this.tx.commit();
|
|
50
|
+
this.tx = undefined;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async rollbackTransaction() {
|
|
54
|
+
return this.serialize(async () => {
|
|
55
|
+
if (!this.tx) {
|
|
56
|
+
throwNoPendingTransaction();
|
|
57
|
+
}
|
|
58
|
+
await this.tx.rollback();
|
|
59
|
+
this.tx = undefined;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async internalRelease() {
|
|
63
|
+
await super.internalRelease();
|
|
64
|
+
if (this.closeClientOnRelease) {
|
|
65
|
+
this.client.close();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AbstractSqlQuerierPool } from '../querier/index.js';
|
|
2
|
+
import type { HranaClient, HranaQuerier, HranaQuerierConnectionOptions } from './hranaQuerier.js';
|
|
3
|
+
import type { SqliteDialect } from './sqliteDialect.js';
|
|
4
|
+
/**
|
|
5
|
+
* Pool for SQLite databases reached over the Hrana wire protocol (`@libsql/client`,
|
|
6
|
+
* `@tursodatabase/serverless/compat`).
|
|
7
|
+
*
|
|
8
|
+
* @remarks The client is shared by every querier, since Hrana keeps no per-connection state: a
|
|
9
|
+
* transaction takes its own session handle. It is resolved on first use rather than in the
|
|
10
|
+
* constructor, so building a pool never throws when the optional driver peer is absent, which is what
|
|
11
|
+
* lets a Workers bundle construct one at module scope.
|
|
12
|
+
*/
|
|
13
|
+
export declare abstract class AbstractHranaQuerierPool<Q extends HranaQuerier, D extends SqliteDialect> extends AbstractSqlQuerierPool<Q, D> {
|
|
14
|
+
private client?;
|
|
15
|
+
/** False when the caller injected their own client, in which case they own its lifecycle. */
|
|
16
|
+
protected readonly ownsClient: boolean;
|
|
17
|
+
protected abstract openClient(): Promise<HranaClient>;
|
|
18
|
+
protected abstract buildQuerier(client: HranaClient, connection?: HranaQuerierConnectionOptions): Q;
|
|
19
|
+
getQuerier(): Promise<Q>;
|
|
20
|
+
end(): Promise<void>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AbstractSqlQuerierPool } from '../querier/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Pool for SQLite databases reached over the Hrana wire protocol (`@libsql/client`,
|
|
4
|
+
* `@tursodatabase/serverless/compat`).
|
|
5
|
+
*
|
|
6
|
+
* @remarks The client is shared by every querier, since Hrana keeps no per-connection state: a
|
|
7
|
+
* transaction takes its own session handle. It is resolved on first use rather than in the
|
|
8
|
+
* constructor, so building a pool never throws when the optional driver peer is absent, which is what
|
|
9
|
+
* lets a Workers bundle construct one at module scope.
|
|
10
|
+
*/
|
|
11
|
+
export class AbstractHranaQuerierPool extends AbstractSqlQuerierPool {
|
|
12
|
+
client;
|
|
13
|
+
/** False when the caller injected their own client, in which case they own its lifecycle. */
|
|
14
|
+
ownsClient = true;
|
|
15
|
+
async getQuerier() {
|
|
16
|
+
this.client ??= await this.openClient();
|
|
17
|
+
return this.buildQuerier(this.client);
|
|
18
|
+
}
|
|
19
|
+
async end() {
|
|
20
|
+
if (this.ownsClient) {
|
|
21
|
+
this.client?.close();
|
|
22
|
+
}
|
|
23
|
+
this.client = undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
package/dist/sqlite/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from './abstractSqliteQuerier.js';
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './hranaQuerier.js';
|
|
3
|
+
export * from './hranaQuerierPool.js';
|
|
4
|
+
export * from './nodeSqliteQuerierPool.js';
|
|
3
5
|
export * from './sqliteDialect.js';
|
|
4
6
|
export * from './sqliteQuerier.js';
|
|
5
7
|
export * from './sqliteQuerierPool.js';
|
package/dist/sqlite/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from './abstractSqliteQuerier.js';
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './hranaQuerier.js';
|
|
3
|
+
export * from './hranaQuerierPool.js';
|
|
4
|
+
export * from './nodeSqliteQuerierPool.js';
|
|
3
5
|
export * from './sqliteDialect.js';
|
|
4
6
|
export * from './sqliteQuerier.js';
|
|
5
7
|
export * from './sqliteQuerierPool.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AbstractSqlQuerierPool } from '../querier/index.js';
|
|
2
|
+
import type { ExtraOptions } from '../type/index.js';
|
|
3
|
+
import { SqliteDialect } from './sqliteDialect.js';
|
|
4
|
+
import { type SqliteDatabase, SqliteQuerier } from './sqliteQuerier.js';
|
|
5
|
+
/** What every local SQLite pool accepts on top of its driver's own options. */
|
|
6
|
+
export type LocalSqlitePoolOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Paths of loadable extensions to install when the connection opens - e.g. what `sqlite-vec`'s
|
|
9
|
+
* `getLoadablePath()` returns, which vector search needs because SQLite itself has no vector
|
|
10
|
+
* functions.
|
|
11
|
+
*/
|
|
12
|
+
extensions?: readonly string[];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Pool for a SQLite database opened in this process, whichever driver provides it.
|
|
16
|
+
*
|
|
17
|
+
* The handle is shared - SQLite gives one connection per file - but each acquisition gets its own
|
|
18
|
+
* querier, so transaction state stays per unit of work. Subclasses supply only {@link createDb}: the
|
|
19
|
+
* lifecycle, and loading the extensions on the way up, are the same for `better-sqlite3`, `bun:sqlite`
|
|
20
|
+
* and `node:sqlite`, and were written out once per pool before.
|
|
21
|
+
*/
|
|
22
|
+
export declare abstract class AbstractLocalSqliteQuerierPool<O extends LocalSqlitePoolOptions> extends AbstractSqlQuerierPool<SqliteQuerier, SqliteDialect> {
|
|
23
|
+
readonly opts?: O | undefined;
|
|
24
|
+
private db?;
|
|
25
|
+
constructor(opts?: O | undefined, extra?: ExtraOptions);
|
|
26
|
+
/** Opens the driver's database. Extensions are loaded by the caller, not here. */
|
|
27
|
+
protected abstract createDb(): Promise<SqliteDatabase>;
|
|
28
|
+
getQuerier(): Promise<SqliteQuerier>;
|
|
29
|
+
private openDb;
|
|
30
|
+
end(): Promise<void>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AbstractSqlQuerierPool } from '../querier/index.js';
|
|
2
|
+
import { SqliteDialect } from './sqliteDialect.js';
|
|
3
|
+
import { SqliteQuerier } from './sqliteQuerier.js';
|
|
4
|
+
/**
|
|
5
|
+
* Pool for a SQLite database opened in this process, whichever driver provides it.
|
|
6
|
+
*
|
|
7
|
+
* The handle is shared - SQLite gives one connection per file - but each acquisition gets its own
|
|
8
|
+
* querier, so transaction state stays per unit of work. Subclasses supply only {@link createDb}: the
|
|
9
|
+
* lifecycle, and loading the extensions on the way up, are the same for `better-sqlite3`, `bun:sqlite`
|
|
10
|
+
* and `node:sqlite`, and were written out once per pool before.
|
|
11
|
+
*/
|
|
12
|
+
export class AbstractLocalSqliteQuerierPool extends AbstractSqlQuerierPool {
|
|
13
|
+
opts;
|
|
14
|
+
db;
|
|
15
|
+
constructor(opts, extra) {
|
|
16
|
+
super(new SqliteDialect({ namingStrategy: extra?.namingStrategy }), extra);
|
|
17
|
+
this.opts = opts;
|
|
18
|
+
}
|
|
19
|
+
async getQuerier() {
|
|
20
|
+
this.db ??= await this.openDb();
|
|
21
|
+
return new SqliteQuerier(this.db, this.dialect, this.extra);
|
|
22
|
+
}
|
|
23
|
+
async openDb() {
|
|
24
|
+
const db = await this.createDb();
|
|
25
|
+
for (const extension of this.opts?.extensions ?? []) {
|
|
26
|
+
db.loadExtension(extension);
|
|
27
|
+
}
|
|
28
|
+
return db;
|
|
29
|
+
}
|
|
30
|
+
async end() {
|
|
31
|
+
await this.db?.close();
|
|
32
|
+
this.db = undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SqliteBindValue } from './abstractSqliteQuerier.js';
|
|
2
|
+
import type { SqliteDatabase } from './sqliteQuerier.js';
|
|
3
|
+
/**
|
|
4
|
+
* A `node:sqlite` statement: better-sqlite3-shaped, except it describes columns instead of reporting
|
|
5
|
+
* `reader`, and types `changes` as possibly `bigint` where better-sqlite3 always answers a `number`.
|
|
6
|
+
*/
|
|
7
|
+
type NodeSqliteStatement = {
|
|
8
|
+
columns(): readonly unknown[];
|
|
9
|
+
all(...values: SqliteBindValue[]): unknown[];
|
|
10
|
+
run(...values: SqliteBindValue[]): {
|
|
11
|
+
changes: number | bigint;
|
|
12
|
+
lastInsertRowid: number | bigint;
|
|
13
|
+
};
|
|
14
|
+
iterate(...values: SqliteBindValue[]): Iterable<unknown>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Structural subset of `node:sqlite`'s `DatabaseSync`, declared locally for the same reason the rest
|
|
18
|
+
* of this folder declares its driver shapes: nothing here depends on `@types/node` being in scope.
|
|
19
|
+
*/
|
|
20
|
+
export type NodeSqliteDatabase = {
|
|
21
|
+
prepare(sql: string): NodeSqliteStatement;
|
|
22
|
+
exec(sql: string): void;
|
|
23
|
+
loadExtension(path: string): void;
|
|
24
|
+
close(): void;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Presents a `node:sqlite` handle as a {@link SqliteDatabase}.
|
|
28
|
+
*
|
|
29
|
+
* @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
|
|
30
|
+
* would take the `run()` path, which discards returned rows, and inserts would report no ids.
|
|
31
|
+
* `columns()` is non-empty for exactly the statements better-sqlite3 marks as readers, including
|
|
32
|
+
* `INSERT ... RETURNING` and `DELETE ... RETURNING`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function adaptNodeSqlite(db: NodeSqliteDatabase): SqliteDatabase;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presents a `node:sqlite` handle as a {@link SqliteDatabase}.
|
|
3
|
+
*
|
|
4
|
+
* @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
|
|
5
|
+
* would take the `run()` path, which discards returned rows, and inserts would report no ids.
|
|
6
|
+
* `columns()` is non-empty for exactly the statements better-sqlite3 marks as readers, including
|
|
7
|
+
* `INSERT ... RETURNING` and `DELETE ... RETURNING`.
|
|
8
|
+
*/
|
|
9
|
+
export function adaptNodeSqlite(db) {
|
|
10
|
+
return {
|
|
11
|
+
prepare: (sql) => {
|
|
12
|
+
const stmt = db.prepare(sql);
|
|
13
|
+
return {
|
|
14
|
+
reader: stmt.columns().length > 0,
|
|
15
|
+
all: (...values) => stmt.all(...values),
|
|
16
|
+
// `changes` is narrowed to `number` to match every other driver; a row count cannot exceed
|
|
17
|
+
// the safe-integer range, so nothing is lost. `lastInsertRowid` keeps its `bigint` arm.
|
|
18
|
+
run: (...values) => {
|
|
19
|
+
const { changes, lastInsertRowid } = stmt.run(...values);
|
|
20
|
+
return { changes: Number(changes), lastInsertRowid };
|
|
21
|
+
},
|
|
22
|
+
iterate: (...values) => stmt.iterate(...values),
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
loadExtension: (path) => db.loadExtension(path),
|
|
26
|
+
close: () => db.close(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ExtraOptions } from '../type/index.js';
|
|
2
|
+
import { AbstractLocalSqliteQuerierPool, type LocalSqlitePoolOptions } from './localSqliteQuerierPool.js';
|
|
3
|
+
import type { SqliteDatabase } from './sqliteQuerier.js';
|
|
4
|
+
/**
|
|
5
|
+
* The `DatabaseSync` options worth surfacing, plus the loadable extensions to install. Declared here
|
|
6
|
+
* rather than imported from `node:sqlite` so this module needs no ambient Node types; unknown keys
|
|
7
|
+
* are ignored by the driver, so the list only has to cover what callers actually set.
|
|
8
|
+
*/
|
|
9
|
+
export type NodeSqlitePoolOptions = LocalSqlitePoolOptions & {
|
|
10
|
+
readonly readOnly?: boolean;
|
|
11
|
+
readonly enableForeignKeyConstraints?: boolean;
|
|
12
|
+
/** Milliseconds a locked database is retried before `SQLITE_BUSY`. */
|
|
13
|
+
readonly timeout?: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Pool backed by Node's built-in `node:sqlite`, so SQLite works with **no dependency at all** rather
|
|
17
|
+
* than requiring the `better-sqlite3` native build. Use {@link Sqlite3QuerierPool} instead when you
|
|
18
|
+
* want `better-sqlite3`, or are on Bun.
|
|
19
|
+
*
|
|
20
|
+
* @remarks `node:sqlite` needs no CLI flag from Node 22.13, and is still a release candidate
|
|
21
|
+
* (stability 1.2) as of Node 26, so `better-sqlite3` via {@link Sqlite3QuerierPool} remains the
|
|
22
|
+
* faster option for read-heavy work.
|
|
23
|
+
*/
|
|
24
|
+
export declare class NodeSqliteQuerierPool extends AbstractLocalSqliteQuerierPool<NodeSqlitePoolOptions> {
|
|
25
|
+
readonly filename: string;
|
|
26
|
+
constructor(filename?: string, opts?: NodeSqlitePoolOptions, extra?: ExtraOptions);
|
|
27
|
+
protected createDb(): Promise<SqliteDatabase>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AbstractLocalSqliteQuerierPool } from './localSqliteQuerierPool.js';
|
|
2
|
+
import { adaptNodeSqlite } from './nodeSqliteAdapter.js';
|
|
3
|
+
/**
|
|
4
|
+
* Pool backed by Node's built-in `node:sqlite`, so SQLite works with **no dependency at all** rather
|
|
5
|
+
* than requiring the `better-sqlite3` native build. Use {@link Sqlite3QuerierPool} instead when you
|
|
6
|
+
* want `better-sqlite3`, or are on Bun.
|
|
7
|
+
*
|
|
8
|
+
* @remarks `node:sqlite` needs no CLI flag from Node 22.13, and is still a release candidate
|
|
9
|
+
* (stability 1.2) as of Node 26, so `better-sqlite3` via {@link Sqlite3QuerierPool} remains the
|
|
10
|
+
* faster option for read-heavy work.
|
|
11
|
+
*/
|
|
12
|
+
export class NodeSqliteQuerierPool extends AbstractLocalSqliteQuerierPool {
|
|
13
|
+
filename;
|
|
14
|
+
constructor(filename = ':memory:', opts, extra) {
|
|
15
|
+
super(opts, extra);
|
|
16
|
+
this.filename = filename;
|
|
17
|
+
}
|
|
18
|
+
async createDb() {
|
|
19
|
+
const { DatabaseSync } = await import('node:sqlite');
|
|
20
|
+
const { extensions, ...driverOpts } = this.opts ?? {};
|
|
21
|
+
const nodeDb = new DatabaseSync(this.filename, {
|
|
22
|
+
...driverOpts,
|
|
23
|
+
// `node:sqlite` refuses `loadExtension` unless the database was opened with this on.
|
|
24
|
+
...(extensions?.length ? { allowExtension: true } : undefined),
|
|
25
|
+
});
|
|
26
|
+
nodeDb.exec('PRAGMA journal_mode = WAL');
|
|
27
|
+
return adaptNodeSqlite(nodeDb);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbstractSqlDialect } from '../dialect/abstractSqlDialect.js';
|
|
2
|
-
import type { DialectFeatures,
|
|
2
|
+
import type { DialectFeatures, EntityMeta, FieldOptions, QueryContext, QuerySizeComparisonOps, QueryTextSearchOptions, Type, VectorDistance } from '../type/index.js';
|
|
3
3
|
export declare class SqliteDialect extends AbstractSqlDialect {
|
|
4
4
|
/** Default {@link DialectFeatures} for SQLite and SQLite-derived dialects. */
|
|
5
5
|
protected readonly featureDefaults: DialectFeatures;
|
|
@@ -14,6 +14,11 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
14
14
|
readonly alterColumnSyntax = "none";
|
|
15
15
|
readonly booleanLiteral = "integer";
|
|
16
16
|
readonly insertIdSource = "returning";
|
|
17
|
+
/**
|
|
18
|
+
* The [sqlite-vec](https://github.com/asg017/sqlite-vec) functions, which need that extension
|
|
19
|
+
* loaded on the connection (see `Sqlite3QuerierPool`'s `extensions` option). libSQL and Turso ship
|
|
20
|
+
* their own vector functions instead, so `LibsqlDialect` overrides this.
|
|
21
|
+
*/
|
|
17
22
|
protected readonly vectorDistanceFns: ReadonlyMap<VectorDistance, string>;
|
|
18
23
|
/**
|
|
19
24
|
* SQLite does not support the `DEFAULT` keyword inside `VALUES`. Inline the metadata default
|
|
@@ -23,7 +28,11 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
23
28
|
protected ilikeExpr(f: string, ph: string): string;
|
|
24
29
|
protected get neOp(): string;
|
|
25
30
|
normalizeValue(value: unknown): unknown;
|
|
26
|
-
|
|
31
|
+
/**
|
|
32
|
+
* FTS5 matches the table itself rather than its columns, so this only works when the table *is* an
|
|
33
|
+
* FTS5 virtual table (UQL does not create those; declare it outside your entities).
|
|
34
|
+
*/
|
|
35
|
+
protected appendTextSearch<E>(ctx: QueryContext, entity: Type<E>, meta: EntityMeta<E>, search: QueryTextSearchOptions<E>): void;
|
|
27
36
|
/**
|
|
28
37
|
* SQLite compares an exploded element as whole JSON text, so containment cannot express "this
|
|
29
38
|
* element includes these keys" - `$elemMatch` always expands to per-field conditions.
|
|
@@ -43,8 +52,6 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
43
52
|
protected jsonElemRef(alias: string, field?: string, asJson?: boolean): string;
|
|
44
53
|
protected getJsonPathScalarExpr(escapedColumn: string, jsonPathStr: string): string;
|
|
45
54
|
protected numericCast(expr: string): string;
|
|
46
|
-
insert<E>(ctx: QueryContext, entity: Type<E>, payload: E | E[], opts?: QueryOptions): void;
|
|
47
|
-
upsert<E>(ctx: QueryContext, entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E | E[]): void;
|
|
48
55
|
protected jsonCast(operand: string): string;
|
|
49
56
|
/**
|
|
50
57
|
* `json_replace` leaves an absent key (and a NULL column) untouched. Elements are read back
|
|
@@ -53,7 +60,13 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
53
60
|
*/
|
|
54
61
|
protected jsonPullKey(ctx: QueryContext, expr: string, escapedCol: string, key: string, value: unknown): string;
|
|
55
62
|
protected jsonSet(ctx: QueryContext, expr: string, set: Record<string, unknown>, field?: FieldOptions): string;
|
|
56
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* `[#]` appends, creating the array when the key is absent.
|
|
65
|
+
*
|
|
66
|
+
* @remarks `json_set` rather than `json_insert`: the two are equivalent here because `[#]` always
|
|
67
|
+
* resolves past the end of the array, and Turso's engine implements `json_insert` as create-only,
|
|
68
|
+
* so it silently drops the element when the array already exists.
|
|
69
|
+
*/
|
|
57
70
|
protected jsonPush(ctx: QueryContext, expr: string, push: Record<string, unknown>): string;
|
|
58
71
|
protected jsonUnset(_ctx: QueryContext, expr: string, unset: readonly string[]): string;
|
|
59
72
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AbstractSqlDialect } from '../dialect/abstractSqlDialect.js';
|
|
2
2
|
import { JSON_ELEM_ALIAS_PREFIX, JSON_PULL_ALIAS, jsonAssignCall, jsonElemExists, jsonPath, jsonRemoveCall, jsonSetTarget, } from '../dialect/jsonSql.js';
|
|
3
|
-
import { getMeta } from '../entity/index.js';
|
|
4
3
|
export class SqliteDialect extends AbstractSqlDialect {
|
|
5
4
|
/** Default {@link DialectFeatures} for SQLite and SQLite-derived dialects. */
|
|
6
5
|
featureDefaults = {
|
|
@@ -13,7 +12,7 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
13
12
|
renameColumn: true,
|
|
14
13
|
foreignKeyAlter: false, // SQLite does not support adding FKs to existing tables
|
|
15
14
|
columnComment: false, // SQLite does not support column comments
|
|
16
|
-
|
|
15
|
+
inlineVectorIndex: false,
|
|
17
16
|
vectorSupportsLength: false,
|
|
18
17
|
supportsTimestamptz: false,
|
|
19
18
|
defaultStringAsText: true,
|
|
@@ -30,10 +29,15 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
30
29
|
booleanLiteral = 'integer';
|
|
31
30
|
// SQLite supports `RETURNING` (including on `INSERT ... ON CONFLICT`), so IDs are exact per row.
|
|
32
31
|
insertIdSource = 'returning';
|
|
32
|
+
/**
|
|
33
|
+
* The [sqlite-vec](https://github.com/asg017/sqlite-vec) functions, which need that extension
|
|
34
|
+
* loaded on the connection (see `Sqlite3QuerierPool`'s `extensions` option). libSQL and Turso ship
|
|
35
|
+
* their own vector functions instead, so `LibsqlDialect` overrides this.
|
|
36
|
+
*/
|
|
33
37
|
vectorDistanceFns = new Map([
|
|
34
38
|
['cosine', 'vec_distance_cosine'],
|
|
35
39
|
['l2', 'vec_distance_L2'],
|
|
36
|
-
['
|
|
40
|
+
['l1', 'vec_distance_L1'],
|
|
37
41
|
]);
|
|
38
42
|
/**
|
|
39
43
|
* SQLite does not support the `DEFAULT` keyword inside `VALUES`. Inline the metadata default
|
|
@@ -58,21 +62,14 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
58
62
|
return value.getTime();
|
|
59
63
|
return super.normalizeValue(value);
|
|
60
64
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
70
|
-
const tableName = this.resolveTableName(entity, meta);
|
|
71
|
-
ctx.append(`${this.escapeId(tableName)} MATCH {${fields.join(' ')}} : `);
|
|
72
|
-
ctx.addValue(search.$value);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
super.compare(ctx, entity, key, val, opts);
|
|
65
|
+
/**
|
|
66
|
+
* FTS5 matches the table itself rather than its columns, so this only works when the table *is* an
|
|
67
|
+
* FTS5 virtual table (UQL does not create those; declare it outside your entities).
|
|
68
|
+
*/
|
|
69
|
+
appendTextSearch(ctx, entity, meta, search) {
|
|
70
|
+
const columns = search.$fields.map((key) => this.escapeId(this.resolveColumnName(key, meta.fields[key])));
|
|
71
|
+
ctx.append(`${this.escapeId(this.resolveTableName(entity, meta))} MATCH {${columns.join(' ')}} : `);
|
|
72
|
+
ctx.addValue(search.$value);
|
|
76
73
|
}
|
|
77
74
|
/**
|
|
78
75
|
* SQLite compares an exploded element as whole JSON text, so containment cannot express "this
|
|
@@ -111,22 +108,6 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
111
108
|
numericCast(expr) {
|
|
112
109
|
return `CAST(${expr} AS REAL)`;
|
|
113
110
|
}
|
|
114
|
-
insert(ctx, entity, payload, opts) {
|
|
115
|
-
super.insert(ctx, entity, payload, opts);
|
|
116
|
-
ctx.append(' ' + this.returningId(entity));
|
|
117
|
-
}
|
|
118
|
-
upsert(ctx, entity, conflictPaths, payload) {
|
|
119
|
-
const meta = getMeta(entity);
|
|
120
|
-
const updateCtx = this.createContext();
|
|
121
|
-
const update = this.getUpsertUpdateAssignments(updateCtx, meta, conflictPaths, payload, (name) => `EXCLUDED.${name}`);
|
|
122
|
-
const keysStr = this.getUpsertConflictPathsStr(meta, conflictPaths);
|
|
123
|
-
const onConflict = update ? `DO UPDATE SET ${update}` : 'DO NOTHING';
|
|
124
|
-
// Use the base (non-RETURNING) insert here: the appended RETURNING below would otherwise
|
|
125
|
-
// be doubled by `this.insert`'s own.
|
|
126
|
-
super.insert(ctx, entity, payload);
|
|
127
|
-
ctx.append(` ON CONFLICT (${keysStr}) ${onConflict} ${this.returningId(entity)}`);
|
|
128
|
-
ctx.pushValue(...updateCtx.values);
|
|
129
|
-
}
|
|
130
111
|
jsonCast(operand) {
|
|
131
112
|
return `json(${operand})`;
|
|
132
113
|
}
|
|
@@ -144,9 +125,15 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
144
125
|
jsonSet(ctx, expr, set, field) {
|
|
145
126
|
return jsonAssignCall((value) => this.jsonScalarParam(ctx, value), 'json_set', jsonSetTarget(expr, field, `'{}'`), set);
|
|
146
127
|
}
|
|
147
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* `[#]` appends, creating the array when the key is absent.
|
|
130
|
+
*
|
|
131
|
+
* @remarks `json_set` rather than `json_insert`: the two are equivalent here because `[#]` always
|
|
132
|
+
* resolves past the end of the array, and Turso's engine implements `json_insert` as create-only,
|
|
133
|
+
* so it silently drops the element when the array already exists.
|
|
134
|
+
*/
|
|
148
135
|
jsonPush(ctx, expr, push) {
|
|
149
|
-
return jsonAssignCall((value) => this.jsonScalarParam(ctx, value), '
|
|
136
|
+
return jsonAssignCall((value) => this.jsonScalarParam(ctx, value), 'json_set', expr, push, '[#]');
|
|
150
137
|
}
|
|
151
138
|
jsonUnset(_ctx, expr, unset) {
|
|
152
139
|
return jsonRemoveCall('json_remove', expr, unset);
|