uql-orm 0.51.0 → 0.52.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 +1 -1
- package/dist/bunSql/bunSql.util.d.ts +33 -10
- package/dist/bunSql/bunSql.util.js +57 -42
- package/dist/bunSql/bunSqlQuerier.d.ts +13 -8
- package/dist/bunSql/bunSqlQuerier.js +17 -8
- package/dist/bunSql/bunSqlQuerierPool.d.ts +10 -2
- package/dist/bunSql/bunSqlQuerierPool.js +38 -25
- package/dist/bunSql/index.d.ts +1 -3
- package/dist/bunSql/index.js +0 -3
- package/dist/dialect/abstractSqlDialect.d.ts +58 -4
- package/dist/dialect/abstractSqlDialect.js +85 -24
- package/dist/dialect/aliases.d.ts +2 -0
- package/dist/dialect/aliases.js +2 -0
- package/dist/dialect/mergeSqlDialect.d.ts +45 -0
- package/dist/dialect/mergeSqlDialect.js +89 -0
- package/dist/dialect/mysqlLikeSqlDialect.js +4 -1
- package/dist/dialect/pgLikeSqlDialect.js +4 -1
- package/dist/migrate/builder/expressions.js +5 -0
- package/dist/migrate/introspection/index.d.ts +2 -0
- package/dist/migrate/introspection/index.js +2 -0
- package/dist/migrate/introspection/mssqlIntrospector.d.ts +63 -0
- package/dist/migrate/introspection/mssqlIntrospector.js +198 -0
- package/dist/migrate/introspection/registry.d.ts +3 -0
- package/dist/migrate/introspection/registry.js +28 -0
- package/dist/migrate/migrator.js +2 -21
- package/dist/mongo/mongoDialect.js +4 -1
- package/dist/mssql/index.d.ts +3 -0
- package/dist/mssql/index.js +3 -0
- package/dist/mssql/mssqlDialect.d.ts +144 -0
- package/dist/mssql/mssqlDialect.js +328 -0
- package/dist/mssql/mssqlQuerier.d.ts +23 -0
- package/dist/mssql/mssqlQuerier.js +137 -0
- package/dist/mssql/mssqlQuerierPool.d.ts +17 -0
- package/dist/mssql/mssqlQuerierPool.js +32 -0
- package/dist/mssql/mssqlWireTypes.d.ts +23 -0
- package/dist/mssql/mssqlWireTypes.js +44 -0
- package/dist/pglite/pgliteQuerier.d.ts +4 -2
- package/dist/pglite/pgliteQuerier.js +7 -2
- package/dist/postgres/pgCursorStream.d.ts +20 -0
- package/dist/postgres/pgCursorStream.js +49 -0
- package/dist/postgres/pgDialect.d.ts +1 -1
- package/dist/postgres/pgDialect.js +1 -1
- package/dist/postgres/postgresWireDriverCapabilities.d.ts +12 -10
- package/dist/postgres/postgresWireDriverCapabilities.js +12 -10
- package/dist/schema/canonicalType.js +96 -113
- package/dist/sqlite/sqliteDialect.js +4 -1
- package/dist/type/dialect.d.ts +31 -4
- package/dist/type/migratorDialect.d.ts +1 -1
- package/dist/type/migratorDialect.js +1 -0
- package/package.json +13 -3
- package/dist/bunSql/bunSqlCockroachDialect.d.ts +0 -12
- package/dist/bunSql/bunSqlCockroachDialect.js +0 -15
- package/dist/bunSql/bunSqlPostgresDialect.d.ts +0 -11
- package/dist/bunSql/bunSqlPostgresDialect.js +0 -14
- package/dist/bunSql/bunSqliteDialect.d.ts +0 -6
- package/dist/bunSql/bunSqliteDialect.js +0 -6
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ The query is just JSON: build it dynamically, store it, diff it, or send it from
|
|
|
50
50
|
|
|
51
51
|
- **Serializable queries (JSON), not method chains.** Plain JSON in, typed rows out. No DSL to learn.
|
|
52
52
|
- **Type-safe to the leaf, nothing to generate.** Every key is checked against your entity, down into populated relations and [JSON/JSONB](https://uql-orm.dev/querying/json) dot-paths, so `$like` on a numeric column is a compile error. Entities are plain classes on the standard TC39 decorators: no `.prisma` file, no generated client, no `reflect-metadata`, no `experimentalDecorators`.
|
|
53
|
-
- **One API, everywhere it runs.** PostgreSQL, PGlite, CockroachDB, MySQL, MariaDB, SQLite, Turso, libSQL, Neon, Cloudflare D1, Bun's native SQL, and even MongoDB. The same code on Node 24+, Bun, Deno, [Cloudflare Workers](https://uql-orm.dev/cloudflare-d1), [AWS Lambda and Vercel](https://uql-orm.dev/serverless), and [the browser](https://uql-orm.dev/browser), with no native binaries on the `fetch`-based drivers.
|
|
53
|
+
- **One API, everywhere it runs.** PostgreSQL, PGlite, CockroachDB, MySQL, MariaDB, MSSQL, SQLite, Turso, libSQL, Neon, Cloudflare D1, Bun's native SQL, and even MongoDB. The same code on Node 24+, Bun, Deno, [Cloudflare Workers](https://uql-orm.dev/cloudflare-d1), [AWS Lambda and Vercel](https://uql-orm.dev/serverless), and [the browser](https://uql-orm.dev/browser), with no native binaries on the `fetch`-based drivers.
|
|
54
54
|
- **Relations without N+1.** [`$populate`](https://uql-orm.dev/querying/relations) loads a to-many with one query for all parents, not one per parent. Nothing is lazy, so nothing fires behind your back in a serializer.
|
|
55
55
|
- **Migrations you read before they run.** Edit an entity, run `uql-migrate generate:entities`, review the SQL in the PR like any other file. [`drift:check`](https://uql-orm.dev/migrations) catches a database that no longer matches.
|
|
56
56
|
- **Raw SQL when you want it.** [`raw()`](https://uql-orm.dev/querying/raw-sql) fits anywhere in a query, [computed fields](https://uql-orm.dev/entities/computed-fields) are expressions you can filter on, and a migration can be plain SQL.
|
|
@@ -1,25 +1,48 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { PrimaryKey, RawRow
|
|
1
|
+
import type { SQL } from 'bun';
|
|
2
|
+
import type { PrimaryKey, RawRow } from '../type/index.js';
|
|
3
3
|
export type BunSqlResult<T = RawRow> = T[] & {
|
|
4
4
|
count?: number;
|
|
5
5
|
affectedRows?: number;
|
|
6
6
|
lastInsertRowid?: PrimaryKey;
|
|
7
7
|
};
|
|
8
|
-
export declare function getAffectedRows(res: BunSqlResult): number;
|
|
9
|
-
export declare function isReservedConnection(conn: unknown): conn is ReservedSQL;
|
|
10
|
-
export declare function isPoolableDialect(dialectName: SqlDialectName): boolean;
|
|
11
8
|
/**
|
|
12
|
-
*
|
|
9
|
+
* The connection a {@link BunSqlQuerier} holds. `ReservedSQL` satisfies it as it is; the SQLite
|
|
10
|
+
* adapter, which has no reservation, is given the pool's own handle with an inert `release`.
|
|
13
11
|
*/
|
|
14
|
-
export
|
|
12
|
+
export type BunSqlConn = Pick<SQL, 'unsafe'> & {
|
|
13
|
+
release(): void;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The engines `bun:sql` can drive. Its own union rather than {@link SqlDialectName}: every other
|
|
17
|
+
* engine uql supports reaches it through a dedicated pool, and keeping this one total means Bun
|
|
18
|
+
* gaining an adapter is a compile error here until the dialect is named.
|
|
19
|
+
*/
|
|
20
|
+
export type BunSqlDialectName = 'postgres' | 'cockroachdb' | 'mysql' | 'mariadb' | 'sqlite';
|
|
21
|
+
/**
|
|
22
|
+
* Rows a statement read or wrote, from whichever field this adapter fills: Postgres, CockroachDB and
|
|
23
|
+
* SQLite report `count` and leave `affectedRows` null, MySQL and MariaDB the other way around - and
|
|
24
|
+
* `count` is 0 on a MySQL write, so the two are read in that order rather than coalesced.
|
|
25
|
+
*
|
|
26
|
+
* `undefined` when the header carries neither, which leaves the returned rows to answer for it -
|
|
27
|
+
* `buildUpdateResult` already falls back to their count, and it is the only one that should.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getAffectedRows(res: BunSqlResult): number | undefined;
|
|
15
30
|
/**
|
|
16
31
|
* Normalizes SQL.Options into a structure that Bun's SQL engine expects for a given dialect.
|
|
17
32
|
* Crucially handles 'filename' mapping for SQLite and alias resolution for Cockroach/MariaDB.
|
|
18
33
|
*/
|
|
19
|
-
export declare function normalizeBunOpts(config: SQL.Options, dialectName:
|
|
34
|
+
export declare function normalizeBunOpts(config: SQL.Options, dialectName: BunSqlDialectName): SQL.Options;
|
|
35
|
+
/**
|
|
36
|
+
* Robustly infers the UQL SqlDialect from a Bun SQL.Options object.
|
|
37
|
+
*/
|
|
38
|
+
export declare function inferDialectName(config: SQL.Options): BunSqlDialectName;
|
|
20
39
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
40
|
+
* Coerces the BigInts Bun returns (`bigint: true`, set by {@link normalizeBunOpts}) back to numbers.
|
|
41
|
+
*
|
|
42
|
+
* The `bun:sql` counterpart of `postgres/pgNumericTypes.ts`, and at the driver for the same reason:
|
|
43
|
+
* `type: Number` maps to BIGINT, and everything crosses this decode exactly once - entity reads,
|
|
44
|
+
* `RETURNING id`, counts, raw SQL - while hydration only ever sees entity reads. Exact to 2^53, which
|
|
45
|
+
* covers any auto-increment id.
|
|
23
46
|
*/
|
|
24
47
|
export declare function normalizeRows<T>(res: BunSqlResult<T>): T[];
|
|
25
48
|
/**
|
|
@@ -1,46 +1,14 @@
|
|
|
1
|
-
export function getAffectedRows(res) {
|
|
2
|
-
if (Array.isArray(res) && res.length) {
|
|
3
|
-
return res.length;
|
|
4
|
-
}
|
|
5
|
-
return res.affectedRows || res.count || 0;
|
|
6
|
-
}
|
|
7
|
-
export function isReservedConnection(conn) {
|
|
8
|
-
return !!(conn && typeof conn.release === 'function');
|
|
9
|
-
}
|
|
10
|
-
export function isPoolableDialect(dialectName) {
|
|
11
|
-
return dialectName !== 'sqlite';
|
|
12
|
-
}
|
|
13
1
|
/**
|
|
14
|
-
*
|
|
2
|
+
* Rows a statement read or wrote, from whichever field this adapter fills: Postgres, CockroachDB and
|
|
3
|
+
* SQLite report `count` and leave `affectedRows` null, MySQL and MariaDB the other way around - and
|
|
4
|
+
* `count` is 0 on a MySQL write, so the two are read in that order rather than coalesced.
|
|
5
|
+
*
|
|
6
|
+
* `undefined` when the header carries neither, which leaves the returned rows to answer for it -
|
|
7
|
+
* `buildUpdateResult` already falls back to their count, and it is the only one that should.
|
|
15
8
|
*/
|
|
16
|
-
export function
|
|
17
|
-
|
|
18
|
-
return 'sqlite';
|
|
19
|
-
const opts = config;
|
|
20
|
-
if (opts.url) {
|
|
21
|
-
const urlStr = opts.url.toString();
|
|
22
|
-
if (urlStr === ':memory:' || urlStr.endsWith('.db') || urlStr.endsWith('.sqlite')) {
|
|
23
|
-
return 'sqlite';
|
|
24
|
-
}
|
|
25
|
-
const scheme = urlStr.split(':')[0];
|
|
26
|
-
const dialect = DialectMap[scheme];
|
|
27
|
-
if (dialect)
|
|
28
|
-
return dialect;
|
|
29
|
-
}
|
|
30
|
-
if (opts.adapter)
|
|
31
|
-
return opts.adapter;
|
|
32
|
-
return 'postgres';
|
|
9
|
+
export function getAffectedRows(res) {
|
|
10
|
+
return res.affectedRows || res.count || undefined;
|
|
33
11
|
}
|
|
34
|
-
const DialectMap = {
|
|
35
|
-
postgres: 'postgres',
|
|
36
|
-
postgresql: 'postgres',
|
|
37
|
-
mysql: 'mysql',
|
|
38
|
-
mysql2: 'mysql',
|
|
39
|
-
mariadb: 'mariadb',
|
|
40
|
-
sqlite: 'sqlite',
|
|
41
|
-
sqlite3: 'sqlite',
|
|
42
|
-
cockroachdb: 'cockroachdb',
|
|
43
|
-
};
|
|
44
12
|
/**
|
|
45
13
|
* Normalizes SQL.Options into a structure that Bun's SQL engine expects for a given dialect.
|
|
46
14
|
* Crucially handles 'filename' mapping for SQLite and alias resolution for Cockroach/MariaDB.
|
|
@@ -71,8 +39,55 @@ export function normalizeBunOpts(config, dialectName) {
|
|
|
71
39
|
return opts;
|
|
72
40
|
}
|
|
73
41
|
/**
|
|
74
|
-
*
|
|
75
|
-
|
|
42
|
+
* Robustly infers the UQL SqlDialect from a Bun SQL.Options object.
|
|
43
|
+
*/
|
|
44
|
+
export function inferDialectName(config) {
|
|
45
|
+
if (config.filename)
|
|
46
|
+
return 'sqlite';
|
|
47
|
+
const opts = config;
|
|
48
|
+
if (opts.url) {
|
|
49
|
+
const urlStr = opts.url.toString();
|
|
50
|
+
if (urlStr === ':memory:' || urlStr.endsWith('.db') || urlStr.endsWith('.sqlite')) {
|
|
51
|
+
return 'sqlite';
|
|
52
|
+
}
|
|
53
|
+
const scheme = urlStr.split(':')[0] ?? '';
|
|
54
|
+
const elsewhere = ElsewhereMap[scheme];
|
|
55
|
+
if (elsewhere) {
|
|
56
|
+
throw new TypeError(`Bun SQL has no ${elsewhere} driver; use the dedicated uql-orm/${elsewhere} pool`);
|
|
57
|
+
}
|
|
58
|
+
const dialect = DialectMap[scheme];
|
|
59
|
+
if (dialect)
|
|
60
|
+
return dialect;
|
|
61
|
+
}
|
|
62
|
+
// Every Bun adapter name is a key here, so the same map answers both questions.
|
|
63
|
+
return (opts.adapter && DialectMap[opts.adapter]) || 'postgres';
|
|
64
|
+
}
|
|
65
|
+
const DialectMap = {
|
|
66
|
+
postgres: 'postgres',
|
|
67
|
+
postgresql: 'postgres',
|
|
68
|
+
mysql: 'mysql',
|
|
69
|
+
mysql2: 'mysql',
|
|
70
|
+
mariadb: 'mariadb',
|
|
71
|
+
sqlite: 'sqlite',
|
|
72
|
+
sqlite3: 'sqlite',
|
|
73
|
+
cockroachdb: 'cockroachdb',
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Engines uql drives elsewhere but Bun cannot dial. Named rather than left out: Bun's `SQL` falls
|
|
77
|
+
* back to Postgres for any scheme it does not know, so an unlisted `mssql://` would have connected
|
|
78
|
+
* as Postgres and failed on the first statement instead of on the pool.
|
|
79
|
+
*/
|
|
80
|
+
const ElsewhereMap = {
|
|
81
|
+
mssql: 'mssql',
|
|
82
|
+
sqlserver: 'mssql',
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Coerces the BigInts Bun returns (`bigint: true`, set by {@link normalizeBunOpts}) back to numbers.
|
|
86
|
+
*
|
|
87
|
+
* The `bun:sql` counterpart of `postgres/pgNumericTypes.ts`, and at the driver for the same reason:
|
|
88
|
+
* `type: Number` maps to BIGINT, and everything crosses this decode exactly once - entity reads,
|
|
89
|
+
* `RETURNING id`, counts, raw SQL - while hydration only ever sees entity reads. Exact to 2^53, which
|
|
90
|
+
* covers any auto-increment id.
|
|
76
91
|
*/
|
|
77
92
|
export function normalizeRows(res) {
|
|
78
93
|
const rows = [];
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SQL } from 'bun';
|
|
2
2
|
import type { AbstractSqlDialect } from '../dialect/index.js';
|
|
3
3
|
import { AbstractPoolQuerier } from '../querier/abstractPoolQuerier.js';
|
|
4
4
|
import type { ExtraOptions } from '../type/index.js';
|
|
5
|
+
import { type BunSqlConn } from './bunSql.util.js';
|
|
5
6
|
/**
|
|
6
7
|
* Querier for `bun:sql`, Bun's built-in driver for Postgres, MySQL, MariaDB, CockroachDB and SQLite.
|
|
7
|
-
*
|
|
8
|
-
* @remarks Deliberately does not override `internalStream`, which every other SQL driver here does:
|
|
9
|
-
* Bun's `SQL.Query` is a `Promise` with no cursor or async-iterator API, so `findManyStream` falls back
|
|
10
|
-
* to the base class buffering the whole result, as `PgliteQuerier` does for the same reason.
|
|
11
8
|
*/
|
|
12
|
-
export declare class BunSqlQuerier extends AbstractPoolQuerier<
|
|
9
|
+
export declare class BunSqlQuerier extends AbstractPoolQuerier<BunSqlConn> {
|
|
13
10
|
readonly sql: SQL;
|
|
14
11
|
readonly extra?: ExtraOptions | undefined;
|
|
15
|
-
constructor(sql: SQL, dialect: AbstractSqlDialect, connFactory: () => Promise<
|
|
12
|
+
constructor(sql: SQL, dialect: AbstractSqlDialect, connFactory: () => Promise<BunSqlConn>, extra?: ExtraOptions | undefined);
|
|
16
13
|
internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
|
|
17
14
|
internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
|
|
15
|
+
/**
|
|
16
|
+
* A server-side cursor where the engine has one, the base class's buffering where it does not.
|
|
17
|
+
*
|
|
18
|
+
* Bun's `SQL.Query` is a `Promise` with no cursor or async-iterator API
|
|
19
|
+
* ([oven-sh/bun#17181](https://github.com/oven-sh/bun/issues/17181)), so the rows have to be paged
|
|
20
|
+
* in SQL instead - which the Postgres wire family can do and MySQL and SQLite cannot.
|
|
21
|
+
*/
|
|
22
|
+
protected internalStream<T>(query: string, values?: unknown[]): AsyncGenerator<Awaited<T>, void, any>;
|
|
18
23
|
private execute;
|
|
19
|
-
protected releaseConn(conn:
|
|
24
|
+
protected releaseConn(conn: BunSqlConn): Promise<void>;
|
|
20
25
|
}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
+
import { streamViaCursor } from '../postgres/pgCursorStream.js';
|
|
1
2
|
import { AbstractPoolQuerier } from '../querier/abstractPoolQuerier.js';
|
|
2
|
-
import { getAffectedRows, getInsertId,
|
|
3
|
+
import { getAffectedRows, getInsertId, normalizeRows } from './bunSql.util.js';
|
|
3
4
|
/**
|
|
4
5
|
* Querier for `bun:sql`, Bun's built-in driver for Postgres, MySQL, MariaDB, CockroachDB and SQLite.
|
|
5
|
-
*
|
|
6
|
-
* @remarks Deliberately does not override `internalStream`, which every other SQL driver here does:
|
|
7
|
-
* Bun's `SQL.Query` is a `Promise` with no cursor or async-iterator API, so `findManyStream` falls back
|
|
8
|
-
* to the base class buffering the whole result, as `PgliteQuerier` does for the same reason.
|
|
9
6
|
*/
|
|
10
7
|
export class BunSqlQuerier extends AbstractPoolQuerier {
|
|
11
8
|
sql;
|
|
@@ -30,14 +27,26 @@ export class BunSqlQuerier extends AbstractPoolQuerier {
|
|
|
30
27
|
upsertStatus: res.affectedRows,
|
|
31
28
|
});
|
|
32
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* A server-side cursor where the engine has one, the base class's buffering where it does not.
|
|
32
|
+
*
|
|
33
|
+
* Bun's `SQL.Query` is a `Promise` with no cursor or async-iterator API
|
|
34
|
+
* ([oven-sh/bun#17181](https://github.com/oven-sh/bun/issues/17181)), so the rows have to be paged
|
|
35
|
+
* in SQL instead - which the Postgres wire family can do and MySQL and SQLite cannot.
|
|
36
|
+
*/
|
|
37
|
+
async *internalStream(query, values) {
|
|
38
|
+
if (!this.dialect.features.serverSideCursors) {
|
|
39
|
+
yield* super.internalStream(query, values);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
yield* streamViaCursor((sql, params) => this.internalAll(sql, params), query, values, this.hasOpenTransaction);
|
|
43
|
+
}
|
|
33
44
|
async execute(query, values) {
|
|
34
45
|
// Safe: UQL parameters are strictly bound. .unsafe() correctly bypasses Bun's tagged template
|
|
35
46
|
// literal parsing requirement so we can execute our dynamically compiled AST strings natively.
|
|
36
47
|
return this.getConn().unsafe(query, values);
|
|
37
48
|
}
|
|
38
49
|
async releaseConn(conn) {
|
|
39
|
-
|
|
40
|
-
await conn.release();
|
|
41
|
-
}
|
|
50
|
+
conn.release();
|
|
42
51
|
}
|
|
43
52
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { SQL } from 'bun';
|
|
2
2
|
import type { AbstractSqlDialect } from '../dialect/abstractSqlDialect.js';
|
|
3
3
|
import { AbstractSqlQuerierPool } from '../querier/index.js';
|
|
4
|
-
import type { ExtraOptions,
|
|
4
|
+
import type { ExtraOptions, SqlPoolCompat } from '../type/index.js';
|
|
5
|
+
import { type BunSqlDialectName } from './bunSql.util.js';
|
|
5
6
|
import { BunSqlQuerier } from './bunSqlQuerier.js';
|
|
6
7
|
export declare class BunSqlQuerierPool extends AbstractSqlQuerierPool<BunSqlQuerier, AbstractSqlDialect> {
|
|
7
8
|
readonly config: SQL.Options;
|
|
8
9
|
readonly sql: SQL;
|
|
9
|
-
readonly sqlDialectName:
|
|
10
|
+
readonly sqlDialectName: BunSqlDialectName;
|
|
10
11
|
private foreignKeysOn?;
|
|
11
12
|
constructor(config: SQL.Options, extra?: ExtraOptions);
|
|
12
13
|
/**
|
|
@@ -15,5 +16,12 @@ export declare class BunSqlQuerierPool extends AbstractSqlQuerierPool<BunSqlQuer
|
|
|
15
16
|
*/
|
|
16
17
|
get pool(): SqlPoolCompat;
|
|
17
18
|
getQuerier(): Promise<BunSqlQuerier>;
|
|
19
|
+
/**
|
|
20
|
+
* Bun's SQLite adapter does not support connection reservation (it's unpooled), and leaves
|
|
21
|
+
* `foreign_keys` off as `bun:sqlite` does, so without the pragma the constraints uql emits in its
|
|
22
|
+
* own DDL are decorative. One connection means one pragma, issued on the first acquisition, and a
|
|
23
|
+
* `release` that does nothing: the handle is the pool's, and outlives every querier over it.
|
|
24
|
+
*/
|
|
25
|
+
private acquire;
|
|
18
26
|
end(): Promise<void>;
|
|
19
27
|
}
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { SQL } from 'bun';
|
|
2
|
+
import { CockroachDialect } from '../cockroachdb/cockroachDialect.js';
|
|
2
3
|
import { dialectOptionsFrom } from '../dialect/abstractDialect.js';
|
|
3
4
|
import { MariaDialect } from '../maria/mariaDialect.js';
|
|
4
5
|
import { MySqlDialect } from '../mysql/mysqlDialect.js';
|
|
6
|
+
import { PostgresDialect } from '../postgres/postgresDialect.js';
|
|
7
|
+
import { POSTGRES_WIRE_DRIVER_CAPABILITIES } from '../postgres/postgresWireDriverCapabilities.js';
|
|
5
8
|
import { AbstractSqlQuerierPool } from '../querier/index.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { BunSqlPostgresDialect } from './bunSqlPostgresDialect.js';
|
|
9
|
+
import { SqliteDialect } from '../sqlite/sqliteDialect.js';
|
|
10
|
+
import { getAffectedRows, inferDialectName, normalizeBunOpts, normalizeRows, } from './bunSql.util.js';
|
|
9
11
|
import { BunSqlQuerier } from './bunSqlQuerier.js';
|
|
10
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The dialect each engine `bun:sql` drives is given, and how this driver shapes its parameters.
|
|
14
|
+
*
|
|
15
|
+
* The engine dialects themselves, not `bun:sql` subclasses of them: what Bun changes is the binding,
|
|
16
|
+
* never the SQL, and a per-instance `driverCapabilities` is where the base class already takes that -
|
|
17
|
+
* so the Postgres and CockroachDB entries differ from `PgQuerierPool`'s only by naming the same
|
|
18
|
+
* constant. Total over {@link BunSqlDialectName}, so a new Bun adapter has to be answered here.
|
|
19
|
+
*/
|
|
11
20
|
const DialectMap = {
|
|
12
|
-
postgres:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
postgres: [PostgresDialect, POSTGRES_WIRE_DRIVER_CAPABILITIES],
|
|
22
|
+
cockroachdb: [CockroachDialect, POSTGRES_WIRE_DRIVER_CAPABILITIES],
|
|
23
|
+
mysql: [MySqlDialect],
|
|
24
|
+
mariadb: [MariaDialect],
|
|
25
|
+
sqlite: [SqliteDialect],
|
|
17
26
|
};
|
|
18
27
|
export class BunSqlQuerierPool extends AbstractSqlQuerierPool {
|
|
19
28
|
config;
|
|
@@ -22,7 +31,8 @@ export class BunSqlQuerierPool extends AbstractSqlQuerierPool {
|
|
|
22
31
|
foreignKeysOn;
|
|
23
32
|
constructor(config, extra) {
|
|
24
33
|
const dialectName = inferDialectName(config);
|
|
25
|
-
|
|
34
|
+
const [Dialect, driverCapabilities] = DialectMap[dialectName];
|
|
35
|
+
super(new Dialect({ ...dialectOptionsFrom(extra), driverCapabilities }), extra);
|
|
26
36
|
this.config = config;
|
|
27
37
|
this.sqlDialectName = dialectName;
|
|
28
38
|
const opts = normalizeBunOpts(config, dialectName);
|
|
@@ -34,28 +44,31 @@ export class BunSqlQuerierPool extends AbstractSqlQuerierPool {
|
|
|
34
44
|
*/
|
|
35
45
|
get pool() {
|
|
36
46
|
return {
|
|
37
|
-
query: (text, values) => this.sql.unsafe(text, this.dialect.normalizeValues(values)).then((res) =>
|
|
38
|
-
rows
|
|
39
|
-
rowCount: getAffectedRows(res)
|
|
40
|
-
})
|
|
47
|
+
query: (text, values) => this.sql.unsafe(text, this.dialect.normalizeValues(values)).then((res) => {
|
|
48
|
+
const rows = normalizeRows(res);
|
|
49
|
+
return { rows, rowCount: getAffectedRows(res) ?? rows.length };
|
|
50
|
+
}),
|
|
41
51
|
on: () => {
|
|
42
52
|
/* no-op for event listeners */
|
|
43
53
|
},
|
|
44
54
|
};
|
|
45
55
|
}
|
|
46
56
|
async getQuerier() {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
return new BunSqlQuerier(this.sql, this.dialect, () => this.acquire(), this.extra);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Bun's SQLite adapter does not support connection reservation (it's unpooled), and leaves
|
|
61
|
+
* `foreign_keys` off as `bun:sqlite` does, so without the pragma the constraints uql emits in its
|
|
62
|
+
* own DDL are decorative. One connection means one pragma, issued on the first acquisition, and a
|
|
63
|
+
* `release` that does nothing: the handle is the pool's, and outlives every querier over it.
|
|
64
|
+
*/
|
|
65
|
+
async acquire() {
|
|
66
|
+
if (this.sqlDialectName !== 'sqlite') {
|
|
56
67
|
return this.sql.reserve();
|
|
57
|
-
}
|
|
58
|
-
|
|
68
|
+
}
|
|
69
|
+
this.foreignKeysOn ??= this.sql.unsafe('PRAGMA foreign_keys = ON');
|
|
70
|
+
await this.foreignKeysOn;
|
|
71
|
+
return { unsafe: this.sql.unsafe.bind(this.sql), release: () => { } };
|
|
59
72
|
}
|
|
60
73
|
async end() {
|
|
61
74
|
await this.sql.close();
|
package/dist/bunSql/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
export * from './bunSqlCockroachDialect.js';
|
|
3
|
-
export * from './bunSqlPostgresDialect.js';
|
|
1
|
+
export type { BunSqlConn, BunSqlDialectName, BunSqlResult } from './bunSql.util.js';
|
|
4
2
|
export * from './bunSqlQuerier.js';
|
|
5
3
|
export * from './bunSqlQuerierPool.js';
|
package/dist/bunSql/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type EntityMeta, type FieldKey, type FieldOptions, type IsolationLevel, type JsonColumnType, type JsonUpdateOp, type Query, type QueryAggMap, type QueryAggregate, type QueryBuildFn, type QueryComparisonOptions, type QueryConflictPaths, type QueryContext, type QueryDialect, type QueryExclude, type QueryFilter, type QueryGroupMap, type QueryGroupOp, type QueryHavingMap, type QueryOptions, type QueryPager, QueryRaw, type QueryRawFnOptions, type QuerySearch, type QuerySelectOptions, type QuerySelectValue, type QuerySizeComparisonOps, type QuerySortMap, type QueryTextSearchOptions, type QueryVectorNear, type QueryWhere, type QueryWhereArray, type QueryWhereFieldOperatorMap, type QueryWhereMap, type QueryWhereOptions, type RelationMeta, type SqlDialectName, type SqlQueryDialect, type Type, type UpdatePayload } from '../type/index.js';
|
|
1
|
+
import { type EntityData, type EntityMeta, type FieldKey, type FieldOptions, type IsolationLevel, type JsonColumnType, type JsonUpdateOp, type Query, type QueryAggMap, type QueryAggregate, type QueryBuildFn, type QueryComparisonOptions, type QueryConflictPaths, type QueryContext, type QueryDialect, type QueryExclude, type QueryFilter, type QueryGroupMap, type QueryGroupOp, type QueryHavingMap, type QueryOptions, type QueryPager, QueryRaw, type QueryRawFnOptions, type QuerySearch, type QuerySelectOptions, type QuerySelectValue, type QuerySizeComparisonOps, type QuerySortMap, type QueryTextSearchOptions, type QueryVectorNear, type QueryWhere, type QueryWhereArray, type QueryWhereFieldOperatorMap, type QueryWhereMap, type QueryWhereOptions, type RelationMeta, type SqlDialectName, type SqlQueryDialect, type Type, type UpdatePayload } from '../type/index.js';
|
|
2
2
|
import { type ParentPartition } from '../util/index.js';
|
|
3
3
|
import type { HydrateKind } from './hydrateColumn.js';
|
|
4
4
|
import { type JsonAccessMode } from './jsonSql.js';
|
|
@@ -6,6 +6,15 @@ import { type QueryJoins, type QuerySortOptions } from './queryJoins.js';
|
|
|
6
6
|
import { VectorSqlDialect } from './vectorSqlDialect.js';
|
|
7
7
|
/** How a column's values are bound: see {@link AbstractSqlDialect.persistKind}. */
|
|
8
8
|
type PersistKind = 'plain' | 'json' | 'vector';
|
|
9
|
+
/** What {@link AbstractSqlDialect.insertShape} resolves once for a write, indexed in step. */
|
|
10
|
+
type InsertShape<E> = {
|
|
11
|
+
readonly meta: EntityMeta<E>;
|
|
12
|
+
readonly payloads: EntityData<E>[];
|
|
13
|
+
readonly keys: FieldKey<E>[];
|
|
14
|
+
readonly fields: (FieldOptions | undefined)[];
|
|
15
|
+
readonly columns: string[];
|
|
16
|
+
readonly kinds: PersistKind[];
|
|
17
|
+
};
|
|
9
18
|
/** One entry of {@link AbstractSqlDialect.hydratableFields}: a field key and how it decodes. */
|
|
10
19
|
type HydratableField = readonly [string, HydrateKind];
|
|
11
20
|
export type { HydrateKind };
|
|
@@ -111,6 +120,11 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
|
|
|
111
120
|
protected returningIdExpression<E>(meta: EntityMeta<E>): string;
|
|
112
121
|
search<E>(ctx: QueryContext, entity: Type<E>, q?: Query<E>, opts?: QueryOptions, joins?: QueryJoins): void;
|
|
113
122
|
selectFields<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelectValue<E> | undefined, opts?: QuerySelectOptions, exclude?: QueryExclude<E>): void;
|
|
123
|
+
/**
|
|
124
|
+
* What follows `SELECT` before the projection. Empty everywhere but SQL Server, whose `FETCH` will
|
|
125
|
+
* not take a zero and which spells "no rows" as `TOP (0)` instead.
|
|
126
|
+
*/
|
|
127
|
+
protected selectModifier<E>(_q: Query<E>): string;
|
|
114
128
|
/**
|
|
115
129
|
* The expression a scalar field is read through, the plain column by default. MariaDB reads a
|
|
116
130
|
* vector column back with `VEC_ToText`, since selecting it raw yields its binary form.
|
|
@@ -280,7 +294,9 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
|
|
|
280
294
|
protected jsonScalarParam(ctx: QueryContext, value: unknown): string;
|
|
281
295
|
/** {@link resolveOperandField}, appended. */
|
|
282
296
|
getComparisonKey<E>(ctx: QueryContext, entity: Type<E>, key: FieldKey<E>, opts?: QueryOptions): void;
|
|
283
|
-
|
|
297
|
+
/** Appends the `ORDER BY`, reporting whether there was one - which {@link pager} needs on the
|
|
298
|
+
* engines that refuse to page an unordered statement. */
|
|
299
|
+
sort<E>(ctx: QueryContext, entity: Type<E>, sort: QuerySortMap<E> | undefined, opts?: QuerySortOptions): boolean;
|
|
284
300
|
/**
|
|
285
301
|
* Walks `$sort` against the metadata of the entity each level addresses, rather than flattening it
|
|
286
302
|
* to dotted strings and reading every key off the root: only that way does a related column resolve
|
|
@@ -293,7 +309,11 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
|
|
|
293
309
|
* `$agg` alias - is an output alias, which is never table-qualified and needs no resolving.
|
|
294
310
|
*/
|
|
295
311
|
private sortColumn;
|
|
296
|
-
|
|
312
|
+
/**
|
|
313
|
+
* `LIMIT`/`OFFSET`. `sorted` says whether an `ORDER BY` was emitted just before, which
|
|
314
|
+
* {@link MergeSqlDialect} needs: SQL Server refuses to page a statement that has none.
|
|
315
|
+
*/
|
|
316
|
+
pager(ctx: QueryContext, opts: QueryPager, _sorted?: boolean): void;
|
|
297
317
|
/** Whether this engine has row locks at all. The SQLite family locks the database instead. */
|
|
298
318
|
readonly supportsRowLocks: boolean;
|
|
299
319
|
/**
|
|
@@ -306,6 +326,12 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
|
|
|
306
326
|
readonly supportsLockOf: boolean;
|
|
307
327
|
/** Validated before the querier checks for a transaction, so the clearer error wins. */
|
|
308
328
|
assertLockSupported<E>(entity: Type<E>, q: Query<E>, joins?: QueryJoins): void;
|
|
329
|
+
/**
|
|
330
|
+
* The lock as a hint on the table itself, for the engine that has no trailing `FOR UPDATE`. Empty
|
|
331
|
+
* everywhere else, which is where {@link appendLock} does the work instead - the two are the same
|
|
332
|
+
* lock spelled at opposite ends of the statement, so exactly one of them ever emits.
|
|
333
|
+
*/
|
|
334
|
+
protected lockHint<E>(_q: Query<E>): string;
|
|
309
335
|
/**
|
|
310
336
|
* The trailing `FOR UPDATE`. Narrowing to the queried table is not a nicety once a relation is
|
|
311
337
|
* joined: Postgres refuses a bare `FOR UPDATE` over the nullable side of an outer join outright,
|
|
@@ -354,11 +380,32 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
|
|
|
354
380
|
protected readonly totalOverExpr = "COUNT(*) OVER ()";
|
|
355
381
|
find<E>(ctx: QueryContext, entity: Type<E>, q?: Query<E>, opts?: QueryOptions, totalAlias?: string): void;
|
|
356
382
|
insert<E>(ctx: QueryContext, entity: Type<E>, payload: E | E[], opts?: QueryOptions): void;
|
|
383
|
+
/**
|
|
384
|
+
* Where the clause reporting an insert's generated ids goes. `suffix` is `RETURNING ...` at the end
|
|
385
|
+
* of the statement, which every engine here but one spells that way; SQL Server's `OUTPUT` has no
|
|
386
|
+
* trailing form and sits between the column list and `VALUES`.
|
|
387
|
+
*
|
|
388
|
+
* A knob rather than a pair of hooks: one concept decides where the string {@link returningId}
|
|
389
|
+
* already built ends up, so the two ends cannot disagree.
|
|
390
|
+
*/
|
|
391
|
+
readonly returningPosition: 'suffix' | 'after-target';
|
|
357
392
|
/**
|
|
358
393
|
* `INSERT INTO ... VALUES (...)` and nothing more. The upsert builders extend this rather than
|
|
359
394
|
* {@link insert}: their own clause has to come before the `RETURNING`, not after it.
|
|
360
395
|
*/
|
|
361
|
-
protected appendInsertValues<E>(ctx: QueryContext, entity: Type<E>, payload: E | E[]
|
|
396
|
+
protected appendInsertValues<E>(ctx: QueryContext, entity: Type<E>, payload: E | E[],
|
|
397
|
+
/** Spliced between the column list and `VALUES`; see {@link returningPosition}. */
|
|
398
|
+
afterTarget?: string): void;
|
|
399
|
+
/**
|
|
400
|
+
* The columns an insert writes and the records it writes them from, resolved once.
|
|
401
|
+
*
|
|
402
|
+
* Split out of {@link appendInsertValues} because a `MERGE` needs the same rows as a `VALUES` row
|
|
403
|
+
* source rather than as an `INSERT`, and both have to apply `onInsert` defaults and the
|
|
404
|
+
* JSON/vector binding rules identically.
|
|
405
|
+
*/
|
|
406
|
+
protected insertShape<E>(entity: Type<E>, payload: E | E[]): InsertShape<E>;
|
|
407
|
+
/** `(a, b), (c, d)` - the row constructor an INSERT and a MERGE source both write. */
|
|
408
|
+
protected appendValueRows<E>(ctx: QueryContext, { payloads, keys, fields, kinds }: InsertShape<E>): void;
|
|
362
409
|
/**
|
|
363
410
|
* Emit the value for a column a payload record does not provide (the column list is the union
|
|
364
411
|
* across all records). `DEFAULT` delegates to the database default; SQLite overrides this since
|
|
@@ -587,6 +634,13 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
|
|
|
587
634
|
/** ANSI-style single-quote escaping. MySQL-family dialects override this for backslash escaping. */
|
|
588
635
|
escape(value: unknown): string;
|
|
589
636
|
protected get regexpOp(): string;
|
|
637
|
+
/**
|
|
638
|
+
* The `$regex` predicate. An infix operator on the MySQL family (`REGEXP`) and the Postgres one
|
|
639
|
+
* (`~`), but a function on Oracle and SQL Server 2025 (`REGEXP_LIKE(col, ?)`) - which is why this
|
|
640
|
+
* is a method rather than the operator token alone. An engine with no regex at all overrides it to
|
|
641
|
+
* throw, the way {@link appendTextSearch} already does.
|
|
642
|
+
*/
|
|
643
|
+
protected regexCondition(operand: string, placeholder: string): string;
|
|
590
644
|
protected get likeFn(): string;
|
|
591
645
|
/**
|
|
592
646
|
* Not-equal operator token for non-null comparisons.
|